Upgrading WKInterfaceController classes to new SwiftUI struct/View

Viewed 3211

Currently have a running solution on iOS and watchOS. With the great news of incoming SwiftUI, the possibilities are expanding, along with our doubts about it. My question is: I have plenty of:

import WatchKit
import Foundation

class LoginInterfaceController : WKInterfaceController {
    @IBOutlet weak var label: WKInterfaceLabel!
    var timer: Timer!
    var connection = true
...

and I want to transform these views to this:

import SwiftUI

@available(watchOSApplicationExtension 6.0, *)
struct FirstView: View {
    var body: some View {
        LoginView(email: "", pass: "")
    }
}

@available(watchOSApplicationExtension 6.0, *)
struct LoginView : View {
    @State var email: String
    @State var pass: String
    
    var body: some View {
        VStack(alignment: .leading) {
...

How do I call and present the new View? Today I call something like: presentController(withName: "LoginPlease", context: text), couldn't find something for a old View present a new one...

2 Answers

To present a pure SwiftUI view, use WKHostingController.

More info about this in this great WWDC 2019 video: SwiftUI on watchOS.

Example:

class HostingController: WKHostingController<MyView> {
    override var body: MyView {
       MyView()
    }
}

To reuse an existing interface object, create a struct that conforms to WKInterfaceObjectRepresentable.

Example:

struct WatchMapView: WKInterfaceObjectRepresentable {
    var landmark: Landmark

    func makeWKInterfaceObject(context: WKInterfaceObjectRepresentableContext<WatchMapView>) -> WKInterfaceMap {
        // Return the interface object that the view displays.
        return WKInterfaceMap()
    }

    func updateWKInterfaceObject(_ map: WKInterfaceMap, context: WKInterfaceObjectRepresentableContext<WatchMapView>) {
        // Update the interface object.
        let span = MKCoordinateSpan(latitudeDelta: 0.02,
                                    longitudeDelta: 0.02)

        let region = MKCoordinateRegion(
            center: landmark.locationCoordinate,
            span: span)

        map.setRegion(region)
    }
}

Integrating SwiftUI is the WWDC video you're looking for when it comes to reuse pre-existing views.

This is a great resource too: Building watchOS App Interfaces with SwiftUI.

struct MainView : View {
    weak var host: HostingController?
    var body: some View {
        List {
            ListCell(icon: "list_ic_playing", title: "正在播放").tapAction {
                self.host?.presentNowPlaying()
            }    
        }
        .navigationBarTitle(Text("喜马拉雅"))
    }
}

class HostingController : WKHostingController<MainView> {
    override var body: MainView {
        return MainView(host: self)
    }
    func presentNowPlaying() {
        self.presentController(withNames: ["PlayList", "NowPlaying"], contexts: nil)
    }
}
Related