Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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/

More Decks by Aleksandar Vacić (Radiant Tap)

Other Decks in Programming

Transcript

  1. Aleksandar Vacić @radiantav
    at Pragma Conf 2019
    SwiftUI for
    UIKit developers

    View full-size slide

  2. All new UI framework
    Powered by Swift 5.1
    Declarative layout
    Single point of truth

    View full-size slide

  3. — Team Rx, after the WWDC 2019 keynote
    (also team Redux, React etc)

    View full-size slide

  4. Meanwhile, at
    team UIKit : !

    View full-size slide

  5. !
    Principles & Patterns

    Layout & Rendering

    Navigation

    Data flow & management

    UIKit ➜ SwiftUI "
    Interactions

    View full-size slide

  6. • Class-inheritance based
    • Declarative and/or imperative layout
    • Obvious, reachable view hierarchy
    • MVC, Target-Action, Delegate
    UIKit principles & patterns
    UIKit

    View full-size slide

  7. • Lightweight, composable independent views
    • Exclusively declarative layout
    • Black box rendering
    • Reference data semantics, automatic observers
    SwiftUI principles & patterns
    SwiftUI

    View full-size slide

  8. Layout & Rendering

    View full-size slide

  9. UIKit
    UIResponder
    ⤷ UIViewController → MyVC
    ⤷ UINC & friends
    ⤷ UIView → MyView
    ⤷ UIControl → MyControl
    ⤷ UILabel
    ⤷ UITextField

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  12. SwiftUI
    struct Greeting: View {
    var name: String
    var body: some View {
    Text(name)
    }
    }

    View full-size slide

  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?

    View full-size slide

  14. SwiftUI’s declarative approach
    is the correct approach.
    #

    View full-size slide

  15. SwiftUI
    struct Greeting: View {
    var name: String
    var body: some View {
    Text(name)
    }
    }

    View full-size slide

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

    View full-size slide

  17. UIKit
    final class GreetingView: UIView {
    var name: String? {
    didSet { label.text = name }
    }
    @IBOutlet private var label: UILabel!
    }

    View full-size slide

  18. SwiftUI + Instant Previews
    are far worse tool than
    UIKit + Interface Builder
    for beginners.

    View full-size slide

  19. Instant Previews in Xcode 11 truly shine 

    once you already know SwiftUI.

    View full-size slide

  20. SwiftUI’s main advantage over UIKit
    is ridiculously simple
    composing of controls.
    $

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  25. p.s. Coordinator pattern rulez!
    Navigation is equally horrible
    in both UIKit and SwiftUI.
    %

    View full-size slide

  26. Interactions

    View full-size slide

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

    View full-size slide

  28. UIKit
    UIResponder
    ⤷ UIViewController → MyVC
    ⤷ UIView → MyView

    View full-size slide

  29. UIKit
    UIResponder().next
    UIView().next = .superview
    UIViewController().next = .parent ?? .view.superview
    = upwards pathway through the UI hierarchy

    View full-size slide

  30. UIKit
    Responder chain is UIKit’s
    least used superpower.

    View full-size slide

  31. 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

    View full-size slide

  32. 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

    View full-size slide

  33. SwiftUI
    var body: some View {
    NavigationView {
    List {
    ...
    }
    .listStyle(GroupedListStyle())
    .navigationBarTitle("Menu")
    }
    }

    View full-size slide

  34. 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

    View full-size slide

  35. SwiftUI
    https://swiftui-lab.com/communicating-with-the-view-tree-part-1/

    View full-size slide

  36. Data Flow & Management

    View full-size slide

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

    View full-size slide

  38. View
    View
    View
    isPlaying: Bool
    SwiftUI
    &isPlaying
    @State
    @Binding

    View full-size slide

  39. View
    View
    View
    isPlaying: Bool isPlaying: Bool

    View full-size slide

  40. component
    View
    View
    View
    isPlaying: Bool isPlaying: Bool

    View full-size slide

  41. control component
    View
    View
    View
    isPlaying: Bool isPlaying: Bool

    View full-size slide

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

    View full-size slide

  43. controller
    control component
    View
    View
    View
    isPlaying: Bool isPlaying: Bool
    model
    UIKit
    Model View Controller

    View full-size slide

  44. View View View View View View
    Model Environment
    https://developer.apple.com/videos/play/wwdc2019/226
    Data Flow through SwiftUI

    View full-size slide

  45. 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)
    }
    }
    &

    View full-size slide

  46. 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 }
    } '

    View full-size slide

  47. 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

    View full-size slide

  48. 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 }
    }

    View full-size slide

  49. 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 }
    }
    $

    View full-size slide

  50. Ok. Where we are, then?

    View full-size slide

  51. SwiftUI is UI framework where you
    独declare layout but do not control the
    render part
    独declare data you need but don’t
    manage them

    View full-size slide

  52. When you hit roadblock in
    UIKit, you have options to
    work around somehow.
    Not so in SwiftUI, you must
    wait for next iOS update.

    View full-size slide

  53. Today, SwiftUI is a
    pretty solid ver 0.7

    It’s not ready.

    View full-size slide

  54. SwiftUI is not the framework
    you want to build your app on,
    today.

    View full-size slide

  55. If you start new projects in
    next 12 months,
    I recommend to use UIKit,
    even for iOS 13.

    View full-size slide

  56. Don’t fear you’ll be left
    behind, it’s way too early.
    No one is an expert, yet.

    View full-size slide

  57. With hope this was useful
    — Thank you.

    View full-size slide

  58. SpeakerDeck.com / radianttap
    aplus.rs
    Aleksandar Vacić, radianttap.com
    @radiantav
    slides
    dev blog
    contact
    / radianttap
    open source

    View full-size slide