Teabyte

Mobile app development for iOS and Swift

Create your app with SwiftUI only

2020-06-28

WWDC 2020 is just over and Apple announced a lot new interesting features. One of these new features is the ability to create apps entirely with SwiftUI. Up until this year's release it was necessary to leverage elements from UIKit and AppKit to define the entry points for your apps. With iOS 14 it will be possible to create apps without the need of this workaround.

Imagine we want to build an ice cream ordering app with two tabs, one for ordering and one for the settings. With the @main annotation and conforming to the new App protocol we tell iOS to use the following code as the main entry point to our application.

@main
struct IceCreamApp: App {
     var body: some Scene {
        WindowGroup {
            TabView {
                OrderView()
                SettingsView()
            }
        }
    }
}

Just a few lines were necessary to create a fully functional entry point for our app. No more AppDelegate, SceneDelegate or UIHostingViewController, only plain and concise SwiftUI code.

Scene protocol

In the code above we used the new WindowGroup type which conforms to the new Scene protocol. The Scene protocol is the SwiftUI equivalent to the UIScene API that was introduced with iOS 13. It is even possible to create custom Scene conforming types, but if you always want to render the same view hierarchy you can stick to WindowGroup, which is a great default.

Alongside the new Scene protocol a new environment variable, called scenePhase, is introduced. With that, we can observe when and how the phase of a scene is changed. For example when a scene has become active or inactive.

@main
struct IceCreamApp: App {
 
    @Environment(\.scenePhase) private var scenePhase
 
     var body: some Scene {
        WindowGroup {
            ...
        }.onChange(of: scenePhase) { newPhase in
            switch newPhase {
            case .active:
                print("Scene is in the foreground and is active")
            case .inactive:
                print("Scene is in the foreground but should pause its work")
            case .background:
                print("Scene is currently not visible in the UI")
            @unknown default:
               print("Scene case not handled yet")
            }
        }
    }
}

What's about AppDelegate ?

Now you may ask what do to with code that needs to run on certain AppDelegate lifecycle methods. No worries - there is still a way to access the lifecycle methods of the AppDelegate. For that we create a class that conforms to the UIApplicationDelegate protocol and hook it up the @UIApplicationDelegateAdaptor property wrapper into our app.

 
class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
        print("didFinishLaunchingWithOptions")
        return true
    }
}
 
 
@main
struct IceCreamApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
 
    ...
}

Within the newly created class you can access every lifecycle method that you need and execute the code you want.

Conclusion

With iOS 14 the SwiftUI frameworks gained a lot functionality. It is a huge step forward for the SwiftUI framework and enables developer to write their apps with SwiftUI only. It is clearly visible that Apple is pushing SwiftUI to make it the default way to write apps in the future.

Thanks a lot for reading 👋