{"id":17223201,"url":"https://github.com/dentelezhkin/ariadne","last_synced_at":"2025-04-14T00:19:30.728Z","repository":{"id":56902386,"uuid":"151076782","full_name":"DenTelezhkin/Ariadne","owner":"DenTelezhkin","description":"Elegant and extensible routing framework in Swift","archived":false,"fork":false,"pushed_at":"2021-12-02T16:15:27.000Z","size":1906,"stargazers_count":23,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-11T03:23:50.894Z","etag":null,"topics":["carthage","cocoapods","dependency-injection","navigation","router","routing","swift","swiftgen","uiviewcontroller","xcode"],"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/DenTelezhkin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-10-01T11:20:46.000Z","updated_at":"2025-02-26T17:12:15.000Z","dependencies_parsed_at":"2022-08-20T18:20:38.819Z","dependency_job_id":null,"html_url":"https://github.com/DenTelezhkin/Ariadne","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/DenTelezhkin%2FAriadne","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DenTelezhkin%2FAriadne/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DenTelezhkin%2FAriadne/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DenTelezhkin%2FAriadne/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DenTelezhkin","download_url":"https://codeload.github.com/DenTelezhkin/Ariadne/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248799985,"owners_count":21163404,"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":["carthage","cocoapods","dependency-injection","navigation","router","routing","swift","swiftgen","uiviewcontroller","xcode"],"created_at":"2024-10-15T04:07:44.043Z","updated_at":"2025-04-14T00:19:30.689Z","avatar_url":"https://github.com/DenTelezhkin.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"![CI](https://github.com/DenTelezhkin/Ariadne/workflows/CI/badge.svg)\n[![codecov.io](http://codecov.io/github/DenTelezhkin/Ariadne/coverage.svg?branch=main)](http://codecov.io/github/DenTelezhkin/Ariadne?branch=main)\n[![Platform](https://img.shields.io/cocoapods/p/Ariadne.svg?style=flat)](https://dentelezhkin.github.io/Ariadne)\n[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Ariadne.svg)](https://img.shields.io/cocoapods/v/Ariadne.svg)\n[![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)\n[![Packagist](https://img.shields.io/packagist/l/doctrine/orm.svg)]()\n\n\u003cp align=\"left\"\u003e\n  \u003cimg height=\"160\" src=\"logo.png\" /\u003e\n\u003c/p\u003e\n\n# Ariadne\n\n\u003e Ariadne's thread, named for the legend of Ariadne, is the solving of a problem with multiple apparent means of proceeding - such as a physical maze, a logic puzzle, or an ethical dilemma - through an exhaustive application of logic to all available routes.\n\n\u003cp align=\"right\"\u003e\n  \u003ca href=\"https://en.wikipedia.org/wiki/Ariadne%27s_thread_(logic)\"\u003eWikipedia\u003c/a\u003e\n\u003c/p\u003e\n\n\nAriadne is an extensible routing framework, built with composition and dependency injection principles in mind. It helps to create transitions and routes, that abstract away view controller building and presentation logic to make it reusable and compact.\n\n## Motivation\n\nUIKit has a routing problem. All view controller presentation and dismissal methods happen in view controller, which a lot of times leads to bloated view controller, because all view controller building, dependency injection and transitions that also happen there.\n\nThis leads to massive view controllers, that cannot be easily tested, and code, that is hard to reuse across different view controller instances. One solution to those problems is to separate view controller building, and transition code in separate object, commonly called `Router`. And even though only some architectures like VIPER promote `Router` as required component, I would argue that app with any other architecture can be drastically improved by having at least some form of routing.\n\nThis is where `Ariadne` comes in. It is a framework, that provides view controller building mechanisms, transition and routing classes to abstract all this logic from view controller in architecture-agnostic way.\n\n# Example\n\nLet's say, for example, that you need to present user profile inside `UINavigationController`. Usually, in MVC app without any libraries, you would do something like this:\n\n```swift\nlet storyboard = UIStoryboard(named: \"User\", bundle: nil)\nlet userController = storyboard.instantiateViewController(withIdentifier: \"UserViewController\")\nuserController.user = user\nlet navigation = UINavigationController(rootViewController: userController)\npresent(navigation, animated: true)\n```\n\nWith Ariadne, this code is no longer tied to current view controller and can look like this:\n\n```swift\nlet route = Storyboards.User.userViewController.builder.embeddedInNavigation().presentRoute()\nrouter.navigate(to: route, with: user)\n```\n\n\u003e Note: this specific example requires SwiftGen integration, described in [SwiftGen integration](Guides/SwiftGen-integration.md) guide.\n\u003e Without SwiftGen `Storyboards.User.userViewController.builder` could be replaced by any custom view controller builder.\n\n## Requirements\n\n* iOS 10+\n* macOS 10.12+\n* tvOS 10+\n* watchOS 3+\n* Xcode 10 / Swift 4.2 and higher\n\n## Installation\n\n### Swift Package Manager(requires Xcode 11)\n\n* Add package into Project settings -\u003e Swift Packages\n\n### CocoaPods\n\n```ruby\npod 'Ariadne'\n```\n\n## Overview\n\n`Ariadne` architecture fundamentally starts with `ViewBuilder`. Because view controllers are so tightly coupled with their views on iOS, `UIViewController` is considered to be a view and is typealiased to `ViewController`.\n\n\u003e Note: on watchOS `ViewController` is a typealias for `WKInterfaceController`, and on macOS for `NSViewController` for similar reasons.\n\nDefinition of `ViewBuilder` is simple - it builds a `ViewController` out of provided `Context`:\n\n```swift\nprotocol ViewBuilder {\n    associatedtype ViewType: ViewController\n    associatedtype Context\n    func build(with context: Context) throws -\u003e ViewType\n}\n```\n\nOut of the box, `Ariadne` provides builders for:\n\n* UINavigationController\n* UITabBarController\n* UISplitViewController\n\nSecond building block of the framework are `ViewTransition` objects, that are needed to perform transition between views. Out of the box, following transitions are supported:\n\n* UINavigationController transitions - push, pop, pop to root, pop to view controller, replace controllers in navigation stack\n* UIViewController presentations - present, dismiss\n* UIWindow root view controller transition to perform switch of the root view controller with animation.\n\n`ViewBuilder` and `ViewTransition` object can be combined together to form a performable `Route`. For example, given `AlertBuilder`, here's how creating a route for an alert might look like with `Ariadne`:\n\n```swift\nlet alertRoute = AlertBuilder().presentRoute()\n```\n\nNotice how `presentRoute` method is called identically for `AlertBuilder` and any `UIViewController` builders. By leveraging protocol extensions on `ViewBuilder` any transitions and routes can be reused on `ViewBuilder` instance. To see examples of how `ViewBuilder` protocol can be implemented and extended, please refer to [Implementing view builders](Guides/Implementing-view-builders.md) guide.\n\nLast, but not least, `Router` object ties everything together and allows you to actually perform routes:\n\n```swift\nrouter.navigate(to: alertRoute, with: alertModel, completion: { _ in\n    // Route has completed\n})\n```\n\nRouter uses `RootViewProvider` to find which view controller is a root one in a view hierarchy. On iOS and tvOS `RootViewProvider` is an interface for `UIWindow` and allows `Router` to get root view controller of view hierarchy. But on other platforms as well as application extensions UIApplication shared window is not accessible, and in that cases `RootViewProvider` may be different, for example in iMessage apps `MSMessagesAppViewController` may play similar role.\n\n`ViewFinder` object traverses view hierarchy starting from root view to find view controller that is currently visible on screen. On iOS and tvOS `Ariadne` provides implementation of `CurrentlyVisibleViewFinder` class, that recursively searches `UIViewController`, `UINavigationController` and `UITabBarController` to find which view controller is currently visible, but on other platforms and in other scenarios you might want to roll with your implementation or subclass of `CurrentlyVisibleViewFinder`, if your view hierarchy contains other view controller containers.\n\n## SwiftGen integration\n\n[SwiftGen][swiftgen] is a powerful code generator, that can be used to set you free from using String-based API, that is cumbersome and error-prone. For example with storyboards, SwiftGen is able to generate code required for instantiating view controllers and makes this code to guarantee on compile-time that storyboard and view controller exist. `Ariadne` can build on top of that, producing a neat syntax for route building, like so:\n\n```swift\nlet route = Storyboards.User.userViewController.builder.embeddedInNavigation().presentRoute()\n```\n\nTo find out, how this can be achieved, refer to [SwiftGen integration](Guides/SwiftGen-integration.md) guide.\n\n## Dependency injection\n\nDifferent applications can have completely different architectures and requirements. To see examples of simple dependency injection see [SwiftGen integration](Guides/SwiftGen-integration.md) and for more advanced dependency injection with dependency containers like [Dip][dip], head to [Advanced dependency injection examples](Guides/Advanced-dependency-injection.md) guide.\n\n## Vision\n\nTo find out more about project future goals and vision, please read the [Vision](VISION.md) document.\n\n## Documentation\n\nYou can find complete project documentation [here](https://dentelezhkin.github.io/Ariadne/).\n\n## Example project\n\niOS Example project can be found in Ariadne.xcodeproj and contains:\n\n* Root view controller animated change\n* Push/pop, present/dismiss\n* Peek \u0026 Pop\n* Custom transition and presentation\n* Update currently visible view.\n\n## License\n\nAriadne is released under a MIT license. See [LICENSE](LICENSE) for more information.\n\n[viper]: https://www.objc.io/issues/13-architecture/viper/\n[swiftgen]: https://github.com/SwiftGen/SwiftGen\n[dip]: https://github.com/AliSoftware/Dip\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdentelezhkin%2Fariadne","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdentelezhkin%2Fariadne","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdentelezhkin%2Fariadne/lists"}