Pro Yearly is on sale from $80 to $50! »

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. Aleksandar Vacić @radiantav at Pragma Conf 2019 SwiftUI for UIKit

    developers
  2. All new UI framework Powered by Swift 5.1 Declarative layout

    Single point of truth
  3. — Team Rx, after the WWDC 2019 keynote (also team

    Redux, React etc)
  4. Meanwhile, at team UIKit : !

  5. ! Principles & Patterns ① Layout & Rendering ② Navigation

    ③ Data flow & management ⑤ UIKit ➜ SwiftUI " Interactions ④
  6. • Class-inheritance based • Declarative and/or imperative layout • Obvious,

    reachable view hierarchy • MVC, Target-Action, Delegate UIKit principles & patterns UIKit
  7. • Lightweight, composable independent views • Exclusively declarative layout •

    Black box rendering • Reference data semantics, automatic observers SwiftUI principles & patterns SwiftUI
  8. Layout & Rendering

  9. UIKit UIResponder ⤷ UIViewController → MyVC ⤷ UINC & friends

    ⤷ UIView → MyView ⤷ UIControl → MyControl ⤷ UILabel ⤷ UITextField
  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 } }
  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") } }
  12. SwiftUI struct Greeting: View { var name: String var body:

    some View { Text(name) } }
  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?
  14. SwiftUI’s declarative approach is the correct approach. #

  15. SwiftUI struct Greeting: View { var name: String var body:

    some View { Text(name) } }
  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") } }
  17. UIKit final class GreetingView: UIView { var name: String? {

    didSet { label.text = name } } @IBOutlet private var label: UILabel! }
  18. SwiftUI + Instant Previews are far worse tool than UIKit

    + Interface Builder for beginners.
  19. UIKit

  20. SwiftUI

  21. SwiftUI

  22. SwiftUI

  23. SwiftUI

  24. SwiftUI

  25. Instant Previews in Xcode 11 truly shine 
 once you

    already know SwiftUI.
  26. SwiftUI’s main advantage over UIKit is ridiculously simple composing of

    controls. $
  27. Navigation

  28. UIKit let targetVC = TargetController() show(targetVC, sender: self)

  29. SwiftUI let targetView = TargetView() NavigationLink(destination: targetView) { Text("tap me")

    }
  30. SwiftUI

  31. SwiftUI

  32. SwiftUI let targetView = TargetView() NavigationLink(destination: targetView) { Text("tap me")

    }
  33. SwiftUI NavigationLink(destination: TargetView()) { Text("tap me") }

  34. p.s. Coordinator pattern rulez! Navigation is equally horrible in both

    UIKit and SwiftUI. %
  35. Interactions

  36. SwiftUI Text("Tap me!") .onTapGesture { // do something } Image("some-image")

    .onLongPressGesture { // do something }
  37. UIKit UIResponder ⤷ UIViewController → MyVC ⤷ UIView → MyView

  38. UIKit UIResponder().next UIView().next = .superview UIViewController().next = .parent ?? .view.superview

    = upwards pathway through the UI hierarchy
  39. UIKit Responder chain is UIKit’s least used superpower.

  40. None
  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
  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
  43. SwiftUI var body: some View { NavigationView { List {

    ... } .listStyle(GroupedListStyle()) .navigationBarTitle("Menu") } }
  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
  45. SwiftUI https://swiftui-lab.com/communicating-with-the-view-tree-part-1/

  46. Data Flow & Management

  47. View View View isPlaying: Bool isPlaying: Bool https://developer.apple.com/videos/play/wwdc2019/226 Data Flow

    through SwiftUI
  48. View View View isPlaying: Bool SwiftUI &isPlaying @State @Binding

  49. View View View isPlaying: Bool isPlaying: Bool

  50. component View View View isPlaying: Bool isPlaying: Bool

  51. control component View View View isPlaying: Bool isPlaying: Bool

  52. control component View View View isPlaying: Bool isPlaying: Bool model

  53. controller control component View View View isPlaying: Bool isPlaying: Bool

    model UIKit Model View Controller
  54. View View View View View View Model Environment https://developer.apple.com/videos/play/wwdc2019/226 Data

    Flow through SwiftUI
  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) } } &
  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 } } '
  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
  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 } }
  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 } } $
  60. Ok. Where we are, then?

  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
  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.
  63. Today, SwiftUI is a pretty solid ver 0.7
 It’s not

    ready.
  64. SwiftUI is not the framework you want to build your

    app on, today.
  65. If you start new projects in next 12 months, I

    recommend to use UIKit, even for iOS 13.
  66. Don’t fear you’ll be left behind, it’s way too early.

    No one is an expert, yet.
  67. With hope this was useful — Thank you.

  68. SpeakerDeck.com / radianttap aplus.rs Aleksandar Vacić, radianttap.com @radiantav slides dev

    blog contact / radianttap open source