Article

Building watchOS App Interfaces with SwiftUI

Create your app’s user interface by programmatically composing single-responsibility views.

Overview

SwiftUI is a declarative framework for building user interfaces on all of Apple’s platforms. You can create rich user interfaces by composing single-responsibility views into larger, more complex layouts. Your app describes the correct layout for your view based on your its current state. SwiftUI detects changes to the state, and updates the views accordingly.

On watchOS, SwiftUI also gives you considerably more flexibility than storyboard-based layouts. For example, List has a number of features that aren’t supported by WKInterfaceTable, such as the platter style, swipe actions, and row reordering.

A screenshot showing a list’s delete swipe action.

Additionally, you can preview your SwiftUI code in Xcode’s canvas. You can design, build, and test your interfaces without ever running your app.

A screenshot showing Xcode with the circle image selected in the preview.

To learn more about SwiftUI, see Learn to Make Apps Using SwiftUI. For more information, see SwiftUI.

Combine SwiftUI and Storyboards

Use WKHostingController to host an SwiftUI view in an interface controller:

class HostingController: WKHostingController<WatchLandmarkView> {
    override var body: WatchLandmarkView {
        // Return the view structure that the hosting controller displays.
        return WatchLandmarkView(landmark: landmarkData[0],
                                 isFavorite: .constant(false))
    }
}

Subclass WKHostingController and override its body property to return a SwiftUI view. You can add the controller to your project’s storyboard, and present it, just like any other interface controller.

You can also use WKInterfaceObjectRepresentable to create an SwiftUI view that displays and manages WatchKit interface objects.

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)
    }
}

Create a structure that adopts the WKInterfaceObjectRepresentable protocol. Implement the makeWKInterfaceObject(context:) method to instantiate and return an interface object. Implement the updateWKInterfaceObject(_:context:) method to update the interface object whenever changes occur.

For most interface objects, this protocol is not necessary. Just create the SwiftUI version of the object. For example, Button creates a WKInterfaceButton in watchOS. However, interface objects that don’t have a corresponding SwiftUI view, such as WKInterfaceSKScene, use the interface object’s SwiftUI initializer to programmatically create the object and return it in your makeWKInterfaceObject(context:) method.

See Also

Essentials

Creating an Effective watchOS Experience

Develop a complete watch experience that emphasizes brief interactions and presents the right information at exactly the right time.

Creating Independent watchOS Apps

Set up a watchOS app that installs and runs without an iOS companion app.

Creating a watchOS App with SwiftUI

Use common SwiftUI elements and watch-specific features in a comprehensive sample app.