Chapters

Hide chapters

SwiftUI by Tutorials

Second Edition · iOS 13 · Swift 5.2 · Xcode 11

Before You Begin

Section 0: 3 chapters
Show chapters Hide chapters

Section II: Building Blocks of SwiftUI

Section 2: 6 chapters
Show chapters Hide chapters

4. Integrating SwiftUI
Written by Audrey Tam

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

SwiftUI is so exciting that it’s hard to resist using it for everything in your apps! But you probably have a lot of apps already that are written in plain old Swift using UIKit. There’s no way you have time to rewrite them all in SwiftUI. What to do?

No need to fear. Apple has your back. It’s super-easy to add SwiftUI views to existing UIKit apps, and it’s only a little more work to use UIKit view controllers in SwiftUI apps. With a little more code, you can even create UIKit views that exchange data with SwiftUI views. This helps bridge current (or maybe, not-so-current) shortcomings in SwiftUI controls.

Note: When discussing SwiftUI integration, you’ll hear the term “hosting”: A UIKit app can host SwiftUI views, and a SwiftUI app can host UIKit views.

In this chapter, you’ll learn how to do the following:

  • Host a SwiftUI view in a UIKit project.
  • Host a view controller in a SwiftUI project.
  • Host a UIKit view with data dependencies in a SwiftUI project.

Time to get started!

Getting started

First, duplicate (using Command-D) the BullsEye starter project in the chapter materials. You’ll need a clean copy of this project for the second exercise in this chapter.

Now open the BullsEye starter project, and build and run:

UIKit BullsEye starter app
UIKit BullsEye starter app

This UIKit app displays a random target value between 1 and 100. The user moves the slider to where they think the value is, then taps Hit Me! to see their score.

Also in the starter folder is the final RGBullsEye project from Chapter 3: “Understanding SwiftUI”, minus the timer. But this version resets the target color when the user dismisses the alert, so you can keep playing :].

You’re about to integrate this SwiftUI view into the UIKit BullsEye app!

Targeting iOS 13

SwiftUI requires iOS 13, so check that your UIKit app’s deployment target is iOS 13 or higher:

UIKit BullsEye deployment target is iOS 13.
OEMik WumykEha xivjewniys degfiq or iAP 15.

Hosting a SwiftUI view in a UIKit project

The absolute easiest integration to perform is to host a SwiftUI view in an existing UIKit app. All you have to do is:

Add ContentView.swift to BullsEye project.
Etf DudcophLeiv.lruvp ga VipwtEde xwerams.

Add Play RGBullsEye button.
Izs Zway JXWufjxEba vurbaj.

Create segue from button to Hosting Controller.
Mmaati hoqoo kxiz teqlex wa Picsick Guwqhemmos.

window.rootViewController = UIHostingController(
  rootView: ContentView(...))
import SwiftUI
Create @IBSegueAction in ViewController.
Yniowi @EZCoboeObkeoz um RuiqLufrkevdug.

UIHostingController(coder: coder, rootView:
  ContentView(rGuess: 0.5, gGuess: 0.5, bGuess: 0.5))
Hosting SwiftUI RGBullsEye in UIKit BullsEye.
Yoksibs QzaccAO BTJirtlIfu oh AUFif VejqmUho.

Hosting a view controller in a SwiftUI project

Now, to do the opposite — host the BullsEye view controller in RGBullsEye — here’s what you’ll do:

Add ViewController and storyboard to RGBullsEye project.
Ocw QuibPazmyivpuv arr squxvpouym ke MKPikfzAxe qhiciwt.

Set View Controller’s Storyboard ID.
Lac Diej Hokmbovset’j Glahnwioly OH.

Conforming to UIViewControllerRepresentable

This is where the magic happens.

import SwiftUI
struct ViewControllerRepresentation: UIViewControllerRepresentable {

  func makeUIViewController(
    context: UIViewControllerRepresentableContext
    <ViewControllerRepresentation>) -> ViewController {
    UIStoryboard(name: "Main", bundle: nil)
      .instantiateViewController(
        withIdentifier: "ViewController") as! ViewController
  }

  func updateUIViewController(
    _ uiViewController: ViewController,
    context: UIViewControllerRepresentableContext
    <ViewControllerRepresentation>) {

  }
}

Navigating to the view controller

Almost finally, add this code at the bottom of the highest-level VStack in ContentView, just below the padding modifier of the VStack of ColorSliders:

NavigationLink(destination: ViewControllerRepresentation()) {
  Text("Play BullsEye")
}
.padding(.bottom)
// 1
NavigationView {
  VStack {
    ...
  }
  // 2
  .navigationBarTitle("RGBullsEye")
  .background(Color(.systemBackground))
}
// 3
.navigationViewStyle(StackNavigationViewStyle())
Hosting UIKit BullsEye in SwiftUI RGBullsEye.
Bovlorr EEJus BavznAgi og FyetbIU MSCiyfcAti.

parent?.navigationItem.title = "BullsEye"
Navbar title for UIKit BullsEye in SwiftUI RGBullsEye.
Metlah roslu beg AEYog LiykkUvi ev ZfefcOA TJBokslUsi.

Previewing UIKit views

So that didn’t take long to do. But wait, there’s more! Even if you don’t want to host a view controller in your SwiftUI app, conforming to UIViewControllerRepresentable lets you preview it in Xcode!

struct ViewControllerPreviews: PreviewProvider {
  static var previews: some View {
    ViewControllerRepresentation()
  }
}
Live previewing ViewControllerRepresentation.
Hila lgahiegenb BeepYevvtugcayKumseyenhizeok.

Hosting a UIKit view with data dependencies

Hosting BullsEye in RGBullsEye was pretty easy, but that’s because there aren’t any data dependencies between the BullsEye view controller and the rest of your SwiftUI app.

Conforming to UIViewRepresentable

Start by creating a new iOS ▸ User Interface ▸ SwiftUI View file, and name it ColorUISlider.swift.

struct ColorUISlider: UIViewRepresentable {

  func makeUIView(context: Context) -> UISlider {
    let slider = UISlider(frame: .zero)
    return slider
  }

  func updateUIView(_ uiView: UISlider, context: Context) {

  }

}

Updating the UIView from SwiftUI

Next, add these properties to ColorUISlider:

var color: UIColor
@Binding var value: Double
slider.thumbTintColor = color
slider.value = Float(value)
ColorUISlider(color: .red, value: .constant(0.5))
  .previewLayout(.sizeThatFits)
Previewing ColourUISlider.
Ndoyoewayd XataasAAXfayiq.

Coordinating data between UIView and SwiftUI view

Add this line to updateUIView(_:context:):

uiView.value = Float(self.value)
class Coordinator: NSObject {
  var parent: ColorUISlider
  init(_ parent: ColorUISlider) {
    self.parent = parent
  }
}
func makeCoordinator() -> ColorUISlider.Coordinator {
  Coordinator(self)
}
@objc func updateColorUISlider(_ sender: UISlider) {
  parent.value = Double(sender.value)
}
slider.addTarget(context.coordinator,
  action: #selector(Coordinator.updateColorUISlider(_:)), 
  for: .valueChanged)

Making it all happen!

Finally, put your ColorUISlider to work! In ContentView, scroll down to struct ColorSlider, and replace Slider and all its modifiers with this view:

ColorUISlider(color: textColor, value: $value)
var textColor: UIColor
Text("0")
  .foregroundColor(Color(textColor))
...
Text("255")
  .foregroundColor(Color(textColor))
VStack {
  ColorSlider(value: $rGuess, textColor: UIColor.red)
  ColorSlider(value: $gGuess, textColor: UIColor.green)
  ColorSlider(value: $bGuess, textColor: UIColor.blue)
}.padding(.horizontal)
RGBullsEye with UISliders.
HLWacqhAvu hikn OIXtijesk.

Challenge

Challenge: Data dependency on a UIKit control

The challenge/starter folder contains a SwiftUI BullsEye app that changes the slider’s background color opacity to provide feedback to the user. Your challenge is to replace the Slider with UISlider, then change the alpha value of thumb.tintColor to provide feedback.

SwiftUI BullsEye with opacity feedback in slider thumb.
TcimnUA RulygOta tehb iwojelm jiehsecl ug lciqiv hsopq.

Key points

To host a SwiftUI view in a UIKit project:

Where to go from here?

You’ve learned how to integrate SwiftUI views into your UIKit apps, as well as the other way around: You now know how to integrate your existing view controllers and UIKit views and controls into your new SwiftUI apps.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now