SwiftUI from within Objective-C Legacy codebase
Whether you're working on an Objective-C legacy codebase, or you just get a kick out of interoperability; this article is for you.
By the end of this article you will be able to build SwiftUI Views and use them from within your good ol' Objective-C UIViewControllers.
Being torn between catching up with SwiftUI and working on an old Objc project is something from the past.
In your existing Objective-C code base do the following steps:
1- Xcode -> project Navigator -> right click on a folder -> Create New File -> Choose a template for your new file -> User Interface -> SwiftUI View
2- When asked for creating a bridging header press "Create Bridging Header"
If you're not prompted with the bridging header, check your project navigator for a file named [yourProjectName]-Bridging-Header.h
Later whenever you want to expose Objective-C code to your Swift files you go to that bridging header and #import the Objective-C file in the Bridging-Header.
Now we have a Hello World SwiftUIView right in the middle of UIKIT/Objective-C project.
UIKit can't render a SwiftUIView on its own, it only understands UIViewControllers. Enter UIHostingController
A UIKit view controller that manages a SwiftUI view hierarchy.
Before we get too excited; UIHostingController belongs to the SwiftUI framework which can't be -directly- used from within Objective-C code. But thanks to the interoperability of Swift and Objective-C, all we need to do is to Create some sort of a Swift wrapper.
Create a Swift file and give it a name of your choice then add the below function to it.
// SwiftUIViewFactory.swift // Created by abanoub keriackus on 5/9/21. import Foundation import SwiftUI @available(iOS 13.0, *) class SwiftUIViewWrapper : NSObject { @objc static func createSwiftUIView(name: String, text:String) -> UIViewController { return UIHostingController(rootView: SwiftUIView(text)) }
}
If your deployment target is iOS 13+ then skip the next paragraph.
Objective-C projects might have a lower deployment target by nature. If that's your case then you must make sure that your deployment target is at least iOS 11. If your deployment target is less than iOS 11 Xcode won't build your project, you can't use SwiftUI in your Objective-C project until It's no longer a requirement to support iOS 9 and iOS 10. If your deployment target is less than iOS 13 then you will have to add @available(iOS 13.0.0, *) in SwiftUIView as well.
Add the @available on top of the structs and add a var textComingFromObjectiveC to test passing date from Objective-C to swiftUIViews.
Looks good...and useless.
Now back to our SwiftUIViewWrapper class
@objc static func createSwiftUIView(name: String, text: String) -> UIViewController { return UIHostingController(rootView: SwiftUIView(text)) }
}
This one line method basically creates a new instance of UIHostingController -Which is a subclass of UIViewController- The UIHostingController constructor takes a SwiftUIView instance as it's only parameter. Here you can also pass any values from Objective-C to initialize your SwiftUIView. The name parameter in createSwiftUIView can be used conditionally to return different SwiftUIViews.
Now open your Objective-C viewController and try to access SwiftUIViewWrapper, and you won't be able to.
This is because Xcode generates for you an interface header that needs to be imported before accessing Swift code from within Objective-C code. You have to import this header in each and every .m file you intend to call Swift code from. The file name can be found by selecting your project in the project navigator > Build Settings tab > then search for generated interface header name by default it's named {project_name}-Swift.h
Import the header in viewController.m -It's auto-generated so don't expect autocomplete to work-
Now you can gracefully access your Swift code from your Objective-C code. I used the returned UIHostingController as a ChildViewController that matches the width and height of our Objective-C UIViewController. You can use it in any way you would use a UIViewController.
Run it on a device or a simulator:
Takeaway:
By depending on UIHostingController and the interoperability of Swift and Objective-C programming languages, you can use SwiftUI framework from within your Objective-C legacy code.
Over and Out.
Freelance Composer, Sound Designer and Engineer at DemonDrum Studios
3 年I'm getting a "Cannot find type 'UIViewController' in scope" error in my wrapper file. Not sure what to do.