My time with SwiftUI

     

SwiftUI Icon

Back in September I got a notice from Apple that one of my personally developed apps, the WRD Calculator, would be removed from the store in one month if I didn’t update it. Apple also released MacOS Catalina that month. Given this conjunction of events my mission was clear: update my Macbook Pro’s OS and rebuild my app using SwiftUI (which requires Catalina to develop with). Working mornings and weekends I hacked together a new app, converting what had been an Ionic abomination of HTML, CSS, and Typescript into a purely native mobile application. Along the way I learned a lot about SwiftUI’s strengths and weaknesses.

One of the biggest shocks for me was that in SwiftUI there are no longer ViewControllers. You can still override view lifecycle methods, either through the View itself or the SceneDelegate, but other than that the traditional ViewController logic is absent.

SwiftUI still has a lot of issues with compositing multiple elements. A view cannot have more than 10 child views. If you ever get an odd error message that XCode is “Unable to infer complex closure return type; add explicit type to disambiguate”, then you need to combine the views into either VStack, ZStack, or HStack subviews. Alternatively you can create an entirely separate view class that you can embed into the initial view. A full list of official SwiftUI issues is available here.

Even though SwiftUI has entered production, there are still a lot of missing UI components (a full list of the UIKit/SwiftUI widgets is available here). To get around this SwiftUI has a UIViewControllerRepresentable, an interoperability protocol for creating custom SwiftUI Views with UIKit. This allows you to work around most of the issues with SwiftUI, but exposes others. As one example, UITextFields cannot receive focus if they are nested in a SwiftUI ScrollView. To get around this you should use SwiftUI TextFields. When this is not possible (I.e. you need UITabBarItems above the keyboard) you should use a VStack parent instead. SwiftUI tags will also not be visible to UIViews and vice versa. To get around the limitation you must manually inject the tag as a constructor argument into your UIViews (As demonstrated here).

SwiftUI is not like Cocoa. It is not event-driven, and there is no communication from one interface object to another. There is only data. Data flows thru state variables. It flows up by a state variable’s binding and down by a state variable’s value.

When creating your SwiftUI view models you can use either `@EnvironmentObject` or `@ObservedObject`. In the former case the object is available throughout the environment. In the latter it’s available only to the object that it’s passed into. To create an EnvironmentObject you label the View’s view model property as an `@EnvironmentObject` and pass in an instance using the View’s ‘environmentObject()` function. To create an ObservedObject you label the View’s view model property as an `@ObservedObject` and pass it as a constructor argument. In both cases the view model itself must conform to the ObservableObject protocol, use `@Published` properties for fields that will need to update with user input, and use computed properties for those that rely on those fields for output. Generally ObservedObject is a more self-contained solution than EnvironmentObject, and therefore preferred.

SwiftUI TextFields need a Binding<*> object in order to notify the VM that they have been updated by the user, otherwise the field will be populated by the initial VM value, but will never update with the user input updates. You can convert a @Published property into a bound property by prefixing it with the ‘$’ character.

The TabView accent color does not affect the color of tab icons that you provide yourself through `Image(“myImage”)`. Instead you can only use system provided images i.e. `Image(systemName: “house”)`. You must install the “SF Symbols” application to see the full list of 1500 available symbols. The application also allows you to customize existing symbols to in order to create your own.

comments powered by Disqus