{"id":18616519,"url":"https://github.com/davidask/stateviewcontroller","last_synced_at":"2025-07-13T06:40:54.620Z","repository":{"id":63907692,"uuid":"144625735","full_name":"davidask/StateViewController","owner":"davidask","description":"Stateful view controller containment for iOS and tvOS","archived":false,"fork":false,"pushed_at":"2020-11-12T21:07:08.000Z","size":5587,"stargazers_count":311,"open_issues_count":0,"forks_count":15,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-04-09T23:16:32.840Z","etag":null,"topics":["container-viewcontroller","ios","stateful","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidask.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-13T19:39:55.000Z","updated_at":"2025-03-10T00:18:45.000Z","dependencies_parsed_at":"2022-11-28T22:49:58.648Z","dependency_job_id":null,"html_url":"https://github.com/davidask/StateViewController","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidask%2FStateViewController","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidask%2FStateViewController/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidask%2FStateViewController/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidask%2FStateViewController/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidask","download_url":"https://codeload.github.com/davidask/StateViewController/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125593,"owners_count":21051771,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["container-viewcontroller","ios","stateful","swift"],"created_at":"2024-11-07T03:35:33.579Z","updated_at":"2025-04-09T23:16:36.984Z","avatar_url":"https://github.com/davidask.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"![Build](https://github.com/davidask/StateViewController/workflows/Build/badge.svg)\n# StateViewController\n\nWhen creating rich view controllers, a single view controller class is often tasked with managing the appearance of many other views, controls, and other user interface elements based on a state. That state, in turn, is often derived from multiple sources that need to be synchronized to correctly represent a single reliable state. Usually the end result is known as the *Massive View Controller* problem, often solved by attempts to abandon the [MVC](https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html) pattern, the primary design pattern in UIKit. While other patterns, such as [MVVM](https://en.wikipedia.org/wiki/Model–view–viewmodel) or [MVP](https://en.wikipedia.org/wiki/Model–view–presenter), can solve some issues, going with the grain rather than against makes interacting with UIKit more accommodating. \n\nThis repository houses a `UIViewController` subclass, enabling modularization and decoupling of view controllers, reducing the size of individual view controllers substantially, without the need for abandoning MVC as a design pattern.\n\n\n\n## Requirements\n\n* iOS 8.0+\n* tvOS 9.0+\n\n## Overview\n`StateViewController` is a container view controller that presents one or more view controllers for any given state that you define, such as `loading`, `list`, or `editing`. It manages the appearance cycles of each child view controller, making sure that the view life cycle of the child view controllers are intact and in order, notifying you about state transitions and which child view controllers are about to appear or disappear from the view hierarchy. This allows you to compose multiple view controllers and re-use them throughout the app. The state view controller also provides extensive support for animating the transition between states.\n\nWhich view controller(s) are visible on screen is dictated by `children(for:)`. \n\n### State transitions during appearance transition\nWhen presented on screen, the a state view controller requires an initial state as a starting point. During its appearance transition, the `loadAppearanceState()` method is invoked to query the state appropriate to transition to as the state view controller appears on screen.\nIf the appearance transition is animated, the state transition animation is respected, and target child view controllers have the option to appear asynchronously. If the appearance transition is not animated, all child view controllers are immediately placed on screen.\n\n`loadAppearanceState()` must execute synchronously, and is a good place to query any persistence layer for available data, determining whether a final state is ready.\n\n\n![During appearance cycle](https://raw.githubusercontent.com/davidask/StateViewController/master/images/during-lifecycle.png \"StateViewController during appearance cycles\")\n\n### State transitions while on screen\n\nWhen on-screen, invoking `setNeedsTransition:to:` will trigger a transition from the current state to the target state. A common practice is to have the transition from one state to another to trigger an an asynchronous operation (such as a network call), which upon completion, requests a third state based on the success of the asynchronous operation.\n\n![Between appearance cycle](https://raw.githubusercontent.com/davidask/StateViewController/master/images/between-lifecycle.png \"StateViewController between appearance cycles\")\n\n## Documentation\n\nThe source code documentation can be found [here](https://davidask.github.io/StateViewController/).\n\n## Installation\nThis module is available via [Carthage](https://github.com/Carthage/Carthage). Modify your [Cartfile](https://github.com/Carthage/Carthage#quick-start) to include `StateViewController`:\n\n```\ngithub \"davidask/StateViewController\"\n```\n\n## Usage\n\n```swift\nimport StateViewController\n```\n\n### Subclassing StateViewController\n\nTo use `StateViewController` you must override it. The class specifies a generic with a subtype of `State`. The state type can be designed to house the actual model data required by your view controller, but that's an optional design decision. For instance, you can create a state that simply determines an abstract state:\n\n```swift\nenum MyState {\n    case loading\n    case ready\n    case error\n}\n```\n\nOr, you can define a state which in itself contains model data:\n```swift\nenum MyState {\n    case loading\n    case ready(MyModel)\n    case error(Error)\n}\n```\n\nOnce you have a state, create a subclass of `StateViewController`.\n\n```swift\nclass MyStateViewController: StateViewController\u003cMyState\u003e\n```\n\nEach time `StateViewController` is about to appear on screen it will call its `loadAppearanceState()` method. This method returns a state which should be ready for display as soon as the view controller is on screen. Override this method to determine what state is appropriate to display immediately, depending on cached data or the contents of your database.\n\n```swift\noverride func loadAppearanceState() -\u003e MyState {\n    if let myModel = MyCache.cachedModel {\n        return .ready(myModel)\n    } else {\n        return .loading\n    }\n}\n```\n\nEach state can be represented by zero or more view controllers. To provide which view controllers are visible for what state, override `children(for:)`.\n\n```swift\noverride func children(for state: MyState) -\u003e [UIViewController] {\n    switch state {\n        case .loading:\n            return [ActivityIndicatorViewController()]\n        case .ready:\n            return [myTableViewController]\n        case .error:\n            return [ErrorViewController()]\n    }\n}\n```\n\nYou receive callbacks for when a state transition will begin, and when it has finished.\n`willTransition(to:animated:)` is a good place to prepare your child view controllers for appearance.\n\n```swift\noverride func willTransition(to nextState: MyState, animated: Bool) {\n    switch nextState {\n        case .ready(let model):\n            navigationItem.setRightBarButton(myBarButtonItem, animated: animated)\n            myTableViewController.setRows(model.objects)\n        default:\n            navigationItem.setRightBarButton(nil, animated: animated)\n    }\n}\n```\n\nWhen `didTransition(from:animated:)` is called, a state transition has finished successfully. This is a good time to invoke other methods which in turn will trigger another state transition.\n\n```swift\noverride func didTransition(from previousState: State?, animated: Bool) {\n    switch currentState {\n        case .loading:\n            fetchData { model in\n                self.setNeedsTransition(to: .ready(model), animated: true)\n            }\n        defualt:\n            break\n    }\n}\n```\n\nYour `StateViewController` is now ready, and will switch between view controllers depending on state. Using `setNeedsTransition(:to:animated:)` you can transition between various states during the life cycle of your state view controller subclass.\n\nMultiple other callbacks are available for determining when a child view controller is appearing or disappearing. Please reference the documentation or the [Example](/Example).\n\n\n### Providing transitions between child view controllers\n\nChild view controllers of `StateViewController` conforming to the [`StateViewControllerTransitioning`](Sources/StateViewController/StateViewControllerTransitioning.swift) protocol can individually control their own transition. The available methods provide functionality for:\n\n- Specifying duration and delayed start of an animated transition\n- Preparing the view controller for presentation\n- Performing animations along side an animated transition\n- Performing operations after a transition\n\n\n## Example\n\nIn the example application included in this project the state view controller switches between two view controllers. Firstly, it displays and animates the transition of an activity indicator view controller while a network call is being performed. Once the network call is successfully completed it transitions into a state displaying a table view with the loaded content.\n\n## Contribute\n\nPlease feel welcome contributing to **StateViewController**, check the ``LICENSE`` file for more info.\n\n## Credits\n\nDavid Ask\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidask%2Fstateviewcontroller","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidask%2Fstateviewcontroller","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidask%2Fstateviewcontroller/lists"}