Screen

  • A protocol for a factory that builds a screen module and returns its container.

    This protocol provides a universal interface for building a screen module, independent of its architectural pattern: MVC, VIPER, MVVM, etc.

    Since the factory is the external entry point to the screen, the factory is called the Screen for convenience.

    External Parameters

    Most screens require some external parameters for their configuration. All such parameters should be declared as properties of the implementation of this protocol.

    For example, a chat screen requires a chat ID:

    class ChatViewController: UIViewController {
    
        let chatID: Int
    
        init(chatID: Int) {
            self.chatID = chatID
    
            super.init(nibName: nil, bundle: nil)
        }
    }
    

    Then the implementation of its factory might look like this:

    struct ChatScreen: Screen {
    
        let chatID: Int
    
        func build(navigator: ScreenNavigator) -> UIViewController {
            ChatViewController(chatID: chatID)
        }
    }
    

    To create this chat screen, you will need to pass the ID to the initializer:

    let chatScreen = ChatScreen(chatID: 123)
    
    navigator.navigate(fromTop: .stack) { route in
        route.push(chatScreen)
    }
    

    Screen Key

    Each screen has the name and traits properties, which are used to create a screen key. You can use this key to create a container that can be found in the hierarchy. Just conform your view controller to the ScreenKeyedContainer protocol and initialize it with this key. See the ScreenKeyedContainer protocol for an example of this approach.

    Default implementation of the name property returns type name:

    let chatScreen = ChatScreen(chatID: 123)
    
    print(chatScreen.name) // Prints: "ChatScreen"
    

    Usually you don’t need to implement the name property yourself. If you want to distinguish between screens of the same type, implement the traitsproperty. For example, chat screens may be different if they have different chat IDs. In this case, the traits property must include the chat ID:

    struct ChatScreen: Screen {
    
        let chatID: Int
    
        var traits: Set<AnyHashable> {
            [chatID]
        }
    
        func build(navigator: ScreenNavigator) -> UIViewController {
            ChatViewController(chatID: chatID)
        }
    }
    

    Then the keys for 2 screens with different chat ID will not be equal:

    let firstScreen = ChatScreen(chatID: 1)
    let secondScreen = ChatScreen(chatID: 2)
    
    print(firstScreen.key == secondScreen.key) // Prints: "false"
    

    Simple Screens

    If your screen consists only of a view controller, implementing a factory can be an extra complication. In this case, you can conform your view controller to the Screen protocol:

    class SimpleViewController: UIViewController, Screen { }
    

    The default implementation will return self in the build(navigator:) method, and an instance of this controller can be used as a factory in navigation:

    navigator.navigate(fromTop: .stack) { route in
        route.push(SimpleViewController())
    }
    

    See also

    ScreenKey

    See also

    ScreenContainer

    See also

    AnyScreen
    See more

    Declaration

    Swift

    public protocol Screen : CustomStringConvertible
  • A screen container on which the navigation is performed.

    Any navigation is performed on containers, for UIKit they are divided into several types:

    • Window container : instances of UIWindow
    • Tabs container: instances of UITabBarController
    • Stack container :  instances of UINavigationController
    • Modal container :  instances of UIViewController

    Since UITabBarController and UINavigationController are subclasses of UIViewController, they are also modal containers.

    This protocol is already implemented by the UIWindow, UIViewController classes and all their subclasses.

    See also

    Screen

    Declaration

    Swift

    public protocol ScreenContainer
  • А screen key that can be used to search for a container in the container hierarchy.

    Usually you don’t need to create an instance of this type. It can be obtained from the key property of the Screen protocol.

    See also

    Screen.
    See more

    Declaration

    Swift

    public struct ScreenKey : Equatable, CustomStringConvertible
  • A screen container that can be found in the container hierarchy by its key.

    Implement this protocol in your container class if you want to find it in the container hierarchy. For example:

    class SomeViewController: ScreenKeyedContainer {
    
        let screenKey: ScreenKey
    
        init(screenKey: ScreenKey) {
            self.screenKey = screenKey
    
            super.init(nibName: nil, bundle: nil)
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    The screen key can be obtained from the key property of the Screen protocol. For example:

    struct SomeScreen: Screen {
    
        func build(navigator: ScreenNavigator) -> UIViewController {
            SomeViewController(screenKey: key)
        }
    }
    

    To find a container in the hierarchy just use its key in the action predicates:

    navigator.navigate { route in
        route
            .top(.container(key: someScreen.key))
            .presenting
            .dismiss()
    }
    

    See also

    Screen

    See also

    ScreenKey

    See also

    ScreenContainer
    See more

    Declaration

    Swift

    public protocol ScreenKeyedContainer : ScreenContainer
  • A screen container that can be visible.

    This is a simple protocol that is used to determine the visibility of a container. The UIWindow and UIViewController classes and all their subclasses already implement this protocol

    See also

    ScreenContainer
    See more

    Declaration

    Swift

    public protocol ScreenVisibleContainer : ScreenContainer
  • Payload associated with the screen container.

    This is a helper class that is used to store navigation data that should be in memory until the screen container itself is released.

    See more

    Declaration

    Swift

    public final class ScreenPayload
  • A screen container that can store navigation data.

    This protocol is used in screen decorators, which can generate data that needs to be stored in memory until the container itself is released.

    This protocol is already implemented by the UIViewController class and all its subclasses. The default implementation for NSObject subclasses uses the objc_setAssociatedObject method to associate the payload with the container.

    See also

    ScreenContainer
    See more

    Declaration

    Swift

    public protocol ScreenPayloadedContainer : ScreenContainer
  • A screen that performs type erasure by wrapping another screen.

    AnyScreen is a concrete implementation of Screen that has no significant properties of its own, and passes through elements from its wrapped screen.

    Use AnyScreen to wrap a screen whose type has details you don’t want to expose across API boundaries, such as different modules. When you use type erasure this way, you can change the underlying screen implementation over time without affecting existing clients.

    To make the constructions as compact as possible, you can use aliases for AnyScreen with a specific type of its container:

    typealias AnyModalScreen = AnyScreen<UIViewController>
    typealias AnyStackScreen = AnyScreen<UINavigationController>
    typealias AnyTabsScreen = AnyScreen<UITabBarController>
    

    You can use eraseToAnyScreen() method to wrap a screen with AnyScreen:

    func chatScreen(chatID: Int) -> AnyModalScreen {
        ChatScreen(chatID: chatID).eraseToAnyScreen()
    }
    

    See also

    Screen
    See more

    Declaration

    Swift

    public struct AnyScreen<Container> : Screen where Container : ScreenContainer
  • Alias for AnyScreen with modal container.

    See also

    AnyScreen

    Declaration

    Swift

    public typealias AnyModalScreen = AnyScreen<UIViewController>
  • Declaration

    Swift

    extension AnyModalScreen
  • Alias for AnyScreen with stack container.

    See also

    AnyScreen

    Declaration

    Swift

    public typealias AnyStackScreen = AnyScreen<UINavigationController>
  • Declaration

    Swift

    extension AnyStackScreen
  • Alias for AnyScreen with tabs container.

    See also

    AnyScreen

    Declaration

    Swift

    public typealias AnyTabsScreen = AnyScreen<UITabBarController>
  • Declaration

    Swift

    extension AnyTabsScreen