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
andtraits
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 theScreenKeyedContainer
protocol and initialize it with this key. See theScreenKeyedContainer
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 thetraits
property. For example, chat screens may be different if they have different chat IDs. In this case, thetraits
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 thebuild(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
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
andUINavigationController
are subclasses ofUIViewController
, 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
- Window container : instances of
-
А 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 theScreen
protocol.See also
Screen
.See also
ScreenKeyedContainer
.Declaration
Swift
public struct ScreenKey : Hashable, 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 theScreen
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
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
andUIViewController
classes and all their subclasses already implement this protocolSee also
ScreenContainer
Declaration
Swift
public protocol ScreenVisibleContainer : ScreenContainer
-
Undocumented
See moreDeclaration
Swift
public protocol ScreenIterableContainer : 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 also
ScreenPayloadedContainer
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 forNSObject
subclasses uses theobjc_setAssociatedObject
method to associate the payload with the container.See also
ScreenContainer
Declaration
Swift
public protocol ScreenPayloadedContainer : ScreenContainer
-
A screen that performs type erasure by wrapping another screen.
AnyScreen
is a concrete implementation ofScreen
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 withAnyScreen
:func chatScreen(chatID: Int) -> AnyModalScreen { ChatScreen(chatID: chatID).eraseToAnyScreen() }
See also
Screen
Declaration
Swift
public struct AnyScreen<Container> : Screen where Container : ScreenContainer
-
Declaration
Swift
extension AnyModalScreen
-
Declaration
Swift
extension AnyStackScreen
-
Declaration
Swift
extension AnyTabsScreen