SwiftUI for UIKit developers

SwiftUI for UIKit developers

A candid look at fundamental changes SwiftUI brings into day-2-day work of UIKit developers. Presented at Pragma Conference 2019.

Transcript is available on my blog: https://aplus.rs/2019/swiftui-for-uikit-developers/

Transcript

  1. 5.

    ! Principles & Patterns ① Layout & Rendering ② Navigation

    ③ Data flow & management ⑤ UIKit ➜ SwiftUI " Interactions ④
  2. 6.

    • Class-inheritance based • Declarative and/or imperative layout • Obvious,

    reachable view hierarchy • MVC, Target-Action, Delegate UIKit principles & patterns UIKit
  3. 7.

    • Lightweight, composable independent views • Exclusively declarative layout •

    Black box rendering • Reference data semantics, automatic observers SwiftUI principles & patterns SwiftUI
  4. 9.

    UIKit UIResponder ⤷ UIViewController → MyVC ⤷ UINC & friends

    ⤷ UIView → MyView ⤷ UIControl → MyControl ⤷ UILabel ⤷ UITextField
  5. 10.

    SwiftUI MyView: View Text: View Image: View TextField: View Button:

    View List: View public protocol View { associatedtype Body : View var body: Self.Body { get } }
  6. 11.

    UIKit final class GreetingView: UIView { var name: String? {

    didSet { label.text = name } } private var label: UILabel! override init(frame: CGRect) { super.init(frame: frame) let label = UILabel(frame: frame.inset(by: layoutMargins)) addSubview(label) self.label = label } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
  7. 13.

    (1) What do we lose switching from imperative to declarative?

    (2)When do you really need this ability to (re)build your subview hierarchy at will, at runtime?
  8. 16.

    UIKit final class GreetingView: UIView { var name: String? {

    didSet { label.text = name } } private var label: UILabel! override init(frame: CGRect) { super.init(frame: frame) let label = UILabel(frame: frame.inset(by: layoutMargins)) addSubview(label) self.label = label } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }
  9. 17.

    UIKit final class GreetingView: UIView { var name: String? {

    didSet { label.text = name } } @IBOutlet private var label: UILabel! }
  10. 18.
  11. 19.
  12. 20.
  13. 21.
  14. 22.
  15. 23.
  16. 24.
  17. 30.
  18. 31.
  19. 40.
  20. 41.

    UIKit extension UIResponder { var fieldsController: FieldsController? { if let

    c = self as? FieldsController { return c } if let c = next as? FieldsController { return c } return next?.fieldsController } } github.com/radianttap/Fields
  21. 42.

    UIKit extension UIResponder { @objc func accountPerformLogout( onQueue queue: OperationQueue?

    = .main, sender: Any? = nil, callback: @escaping (Error?) -> Void = {_ in} ){ coordinatingResponder?.accountPerformLogout( onQueue: queue, sender: sender, callback: callback) } } github.com/radianttap/Coordinator
  22. 43.

    SwiftUI var body: some View { NavigationView { List {

    ... } .listStyle(GroupedListStyle()) .navigationBarTitle("Menu") } }
  23. 44.

    SwiftUI https://developer.apple.com/videos/play/wwdc2019/216/?time=3160 “And then we can use the navigationBarTitle modifier

    to produce that large beautiful title for our form. Now this modifier is a little bit special. It provides information that’s able to be interpreted by a NavigationView ancestor. …this is an example of one that flows information upwards using something called preferences.” SwiftUI Essentials
  24. 55.

    SwiftUI final class SomeService: ObservableObject {} struct MainView: View {

    @EnvironmentObject var service: SomeService } class SceneDelegate: UIResponder, UIWindowSceneDelegate { lazy var service = SomeService() func scene(_ scene: UIScene, willConnectTo…) { let mainView = MainView().environmentObject(service) } } &
  25. 56.

    SwiftUI final class DataManager: ObservableObject { @Published private(set) var events:

    [Event] = [] } struct EventsList: View { @ObservedObject var dataStore: DataManager private var events: [Event] { return dataStore.events } } '
  26. 57.

    final class DataManager { private(set) var events: [Event] = []

    { didSet { NotificationCenter.default.post( name: NSNotification.Name("DataManagerDidUpdateEventsNotification"), object: self ) } } } final class EventsListController: UIViewController { var events: [Event] = [] { didSet { render() } } override func viewDidLoad() { super.viewDidLoad() render() setupNotificationHandlers() } } private extension EventsListController { func render() {} func setupNotificationHandlers() { NotificationCenter.default.addObserver(forName: NSNotification.Name("DataManagerDidUpdateEventsNotification"), object: [weak self] notification in guard let self = self else { return } guard let dataManager = notification.object as? DataManager else { return } self.events = dataManager.events } } UIKit
  27. 58.

    SwiftUI final class DataManager: ObservableObject { @Published private(set) var events:

    [Event] = [] } struct EventsList: View { @ObservedObject var dataStore: DataManager private var events: [Event] { return dataStore.events } }
  28. 59.

    SwiftUI final class DataManager: ObservableObject { let eventsWillChange = ObservableObjectPublisher()

    private(set) var events: [Event] = [] { willSet { eventsWillChange.send() } } } struct EventsList: View { @ObservedObject var dataStore: DataManager private var events: [Event] { return dataStore.events } } $
  29. 61.

    SwiftUI is UI framework where you 独declare layout but do

    not control the render part 独declare data you need but don’t manage them
  30. 62.

    When you hit roadblock in UIKit, you have options to

    work around somehow. Not so in SwiftUI, you must wait for next iOS update.
  31. 65.

    If you start new projects in next 12 months, I

    recommend to use UIKit, even for iOS 13.