Skip to content

Lifecycle

Adam Share edited this page Oct 5, 2020 · 8 revisions

Lifecycle provides protocols and implementations that can be used to facilitate building safe and scalable application architecture.

Types

LifecycleState

initialized Lifecycle has been initialized but not yet active.
active Lifecycle is currently active.
inactive Lifecycle is currently inactive.
deinitialized Lifecycle deinit called.

LifecyclePublisher

Todo: description

Conforming Types

  • LifecycleOwner
  • LifecycleDependent
  • ScopeLifecycle
  • ViewLifecycle

Scope

ScopeLifecycle

Todo: description

LifecycleState

LifecycleOwner

Todo: description

class MyLifecycleOwner: LifecycleOwner {
    let scopeLifecycle: ScopeLifecycle = ScopeLifecycle()
}

LifecycleSubscriber

Todo: description

class MyLifecycleSubscriber: LifecycleSubscriber {
    init(scopeLifecycle: ScopeLifecycle) {
        subscribe(to: scopeLifecycle)
    }

    func didLoad(_ lifecyclePublisher: LifecyclePublisher) {
        // First active
    }

    func didBecomeActive(_ lifecyclePublisher: LifecyclePublisher) {
        // On active
    }

    func didBecomeInactive(_ lifecyclePublisher: LifecyclePublisher) {
        // On inactive
    }
}

LifecycleOwnerRouting

Todo: description

class MyParentLifecycleOwner: LifecycleOwner, LifecycleOwnerRouting {
    let scopeLifecycle: ScopeLifecycle = ScopeLifecycle()
    let childLifecycleOwner: LifecycleOwner

    init(childLifecycleOwner: LifecycleOwner) {
        self.childLifecycleOwner = childLifecycleOwner
    }

    func makeChildActive() {
        if attachChild(childLifecycleOwner) {
            print("Child is now active")
        }
    }

    func makeChildInactive() {
        detachChild(childLifecycleOwner)
    }
}

LifecycleDependent

Todo: description

class MyLifecycleDependent: LifecycleDependent {
    weak var scopeLifecycle: ScopeLifecycle?
}

Todo: description

class MyParentLifecycleDependent: LifecycleDependent, LifecycleOwnerRouting {
    weak var scopeLifecycle: ScopeLifecycle?

    let childLifecycleOwner: LifecycleOwner

    init(scopeLifecycle: ScopeLifecycle,
         childLifecycleOwner: LifecycleOwner) {
        self.scopeLifecycle = scopeLifecycle
        self.childLifecycleOwner = childLifecycleOwner
    }

    func makeChildActive() {
        if attachChild(childLifecycleOwner) {
            print("Child is now active")
        }
    }

    func makeChildInactive() {
        detachChild(childLifecycleOwner)
    }
}

AutoCancel

Todo: description

class AutoCancelLifecycleSubscriber: MyLifecycleSubscriber {
    
    private let textStream: RelayPublisher<String>

    init(scopeLifecycle: ScopeLifecycle,
         textStream: RelayPublisher<String>) {
        self.textStream = textStream
        super.init(scopeLifecycle: scopeLifecycle)
    }

    override func didBecomeActive(_ lifecyclePublisher: LifecyclePublisher) {
        super.didBecomeActive(lifecyclePublisher)
        
        textStream
            .autoCancel(lifecyclePublisher)
            .sink { value in
                print("""
                Safely capturing \(self) as retain cycle is broken when 
                \(lifecyclePublisher) becomes inactive deataching from parent scope.
                """)
            }
    }
}

View

ViewLifecycle

Todo: description

LifecycleState

ViewLifecycleOwner

Todo: description

import SwiftUI

class MyViewLifecycleOwner: ViewLifecycleOwner {
    let viewLifecycle: ViewLifecycle = ViewLifecycle()

    var view: some View {
        tracked {
            Text("Hello World")
        }
    }
}

ViewLifecycleSubscriber

Todo: description

class MyViewLifecycleSubscriber: ViewLifecycleSubscriber {
    init(viewLifecycle: ViewLifecycle) {
        subscribe(to: viewLifecycle)
    }

    func viewDidLoad() {
        // View initialized
    }

    func viewDidAppear() {
        // On appear
    }

    func viewDidDisappear() {
        // On disappear
    }
}

Example MVC Integration

Create your own LifecycleViewController

Todo: description

import Lifecycle
import SwiftUI

protocol LifecycleViewController:
    LifecycleOwner,
    LifecycleSubscriber,
    LifecycleOwnerRouting,
    ViewLifecycleOwner,
    ViewLifecycleSubscriber,
    ObservableObject
{
    var view: AnyView { get }
}

extension LifecycleViewController {
    func didLoad(_ lifecyclePublisher: LifecyclePublisher) {}
    func didBecomeActive(_ lifecyclePublisher: LifecyclePublisher) {}
    func didBecomeInactive(_ lifecyclePublisher: LifecyclePublisher) {}
    func viewDidLoad() {}
    func viewDidAppear() {}
    func viewDidDisappear() {}
}

class MyViewController: LifecycleViewController {

    @Published var text: String = ""
    
    let scopeLifecycle: ScopeLifecycle = ScopeLifecycle()
    let viewLifecycle: ViewLifecycle = ViewLifecycle()

    init() {
        subscribe(to: scopeLifecycle)
        subscribe(to: viewLifecycle)
    }

    var view: AnyView {
        AnyView(tracked {
            ContentView(viewController: self)
        })
    }
    
    func didBecomeActive(_ lifecyclePublisher: LifecyclePublisher) {
        text = "Lifecycle is active."
    }

    struct ContentView: View {
        @ObservedObject var viewController: MyViewController

        var body: some View {
            Text(viewController.text)
        }
    }
}

RootLifecycle

Todo: description

class SceneDelegate: UIResponder, UIWindowSceneDelegate, RootLifecycle {

    let root: LifecycleViewController = LifecycleViewController()

    var rootLifecycleOwner: LifecycleOwner {
        return root
    }

    var window: UIWindow?

    func scene(_ scene: UIScene, 
               willConnectTo session: UISceneSession,
               options connectionOptions: UIScene.ConnectionOptions) {

        guard let windowScene = scene as? UIWindowScene else { return }

        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: root.view)
        window.makeKeyAndVisible()

        activateRoot()

        self.window = window
    }
}