{"id":15030461,"url":"https://github.com/quickbirdeng/xcoordinator","last_synced_at":"2025-05-15T10:01:07.805Z","repository":{"id":40693765,"uuid":"141122309","full_name":"QuickBirdEng/XCoordinator","owner":"QuickBirdEng","description":"🎌 Powerful navigation library for iOS based on the coordinator pattern","archived":false,"fork":false,"pushed_at":"2025-05-09T07:40:30.000Z","size":50364,"stargazers_count":2318,"open_issues_count":20,"forks_count":187,"subscribers_count":36,"default_branch":"master","last_synced_at":"2025-05-09T08:37:36.893Z","etag":null,"topics":["coordinator","coordinator-pattern","ios","ios-swift","mvvm","mvvm-architecture","mvvm-c","mvvm-coordinator","rxswift","rxswift-extensions","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/QuickBirdEng.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-07-16T10:14:32.000Z","updated_at":"2025-05-04T08:55:07.000Z","dependencies_parsed_at":"2023-07-14T10:34:22.866Z","dependency_job_id":"14d9297c-70ab-4ffd-9f9e-b9e15166a8c9","html_url":"https://github.com/QuickBirdEng/XCoordinator","commit_stats":{"total_commits":541,"total_committers":18,"mean_commits":"30.055555555555557","dds":0.3641404805914972,"last_synced_commit":"fc4fd2d4e5718ea7579bd9022d93af7edecd7b52"},"previous_names":["quickbirdstudios/xcoordinator","quickbirdstudios/rxcoordinator"],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuickBirdEng%2FXCoordinator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuickBirdEng%2FXCoordinator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuickBirdEng%2FXCoordinator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/QuickBirdEng%2FXCoordinator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/QuickBirdEng","download_url":"https://codeload.github.com/QuickBirdEng/XCoordinator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254319715,"owners_count":22051072,"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":["coordinator","coordinator-pattern","ios","ios-swift","mvvm","mvvm-architecture","mvvm-c","mvvm-coordinator","rxswift","rxswift-extensions","swift"],"created_at":"2024-09-24T20:13:25.436Z","updated_at":"2025-05-15T10:01:07.156Z","avatar_url":"https://github.com/QuickBirdEng.png","language":"Swift","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/15239005/221913790-9c2b89fc-7497-49a1-9087-c3277f3ab4f2.png\"\u003e\n\u003c/p\u003e\n\n# [![Build Status](https://travis-ci.com/quickbirdstudios/XCoordinator.svg?branch=master)](https://travis-ci.com/quickbirdstudios/XCoordinator) [![CocoaPods Compatible](https://img.shields.io/cocoapods/p/XCoordinator)](https://cocoapods.org/pods/XCoordinator) [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Documentation](https://img.shields.io/badge/documentation-100%25-brightgreen)](https://quickbirdstudios.github.io/XCoordinator) [![Platform](https://img.shields.io/badge/platform-iOS-lightgrey.svg)](https://github.com/quickbirdstudios/XCoordinator) [![License](https://img.shields.io/cocoapods/l/XCoordinator.svg)](https://github.com/quickbirdstudios/XCoordinator/blob/master/LICENSE)\n\n⚠️ We have recently released XCoordinator 2.0. Make sure to read [this section](#when-to-use-which-router-abstraction) before migrating. In general, please replace all `AnyRouter` by either `UnownedRouter` (in viewControllers, viewModels or references to parent coordinators) or `StrongRouter` in your `AppDelegate` or for references to child coordinators. In addition to that, the rootViewController is now injected into the initializer instead of being created in the `Coordinator.generateRootViewController` method.\n\n“How does an app transition from one view controller to another?”.\nThis question is common and puzzling regarding iOS development. There are many answers, as every architecture has different implementation variations. Some do it from within the implementation of a view controller, while some use a router/coordinator, an object connecting view models.\n\nTo better answer the question, we are building **XCoordinator**, a navigation framework based on the **Coordinator** pattern.\nIt's especially useful for implementing MVVM-C, Model-View-ViewModel-Coordinator:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://user-images.githubusercontent.com/15239005/221913797-1ebf0fc8-36d5-4a93-b6da-0a86b6105e6a.png\"\u003e\n\u003c/p\u003e\n\n## 🏃‍♂️Getting started\n\nCreate an enum with all of the navigation paths for a particular flow, i.e. a group of closely connected scenes. (It is up to you when to create a `Route/Coordinator`. As **our rule of thumb**, create a new `Route/Coordinator` whenever a new root view controller, e.g. a new `navigation controller` or a `tab bar controller`, is needed.).\n\nWhereas the `Route` describes which routes can be triggered in a flow, the `Coordinator` is responsible for the preparation of transitions based on routes being triggered. We could, therefore, prepare multiple coordinators for the same route, which differ in which transitions are executed for each route.\n\nIn the following example, we create the `UserListRoute` enum to define triggers of a flow of our application. `UserListRoute` offers routes to open the home screen, display a list of users, to open a specific user and to log out. The `UserListCoordinator` is implemented to prepare transitions for the triggered routes. When a `UserListCoordinator` is shown, it triggers the `.home` route to display a `HomeViewController`.\n\n```swift\nenum UserListRoute: Route {\n    case home\n    case users\n    case user(String)\n    case registerUsersPeek(from: Container)\n    case logout\n}\n\nclass UserListCoordinator: NavigationCoordinator\u003cUserListRoute\u003e {\n    init() {\n        super.init(initialRoute: .home)\n    }\n\n    override func prepareTransition(for route: UserListRoute) -\u003e NavigationTransition {\n        switch route {\n        case .home:\n            let viewController = HomeViewController.instantiateFromNib()\n            let viewModel = HomeViewModelImpl(router: unownedRouter)\n            viewController.bind(to: viewModel)\n            return .push(viewController)\n        case .users:\n            let viewController = UsersViewController.instantiateFromNib()\n            let viewModel = UsersViewModelImpl(router: unownedRouter)\n            viewController.bind(to: viewModel)\n            return .push(viewController, animation: .interactiveFade)\n        case .user(let username):\n            let coordinator = UserCoordinator(user: username)\n            return .present(coordinator, animation: .default)\n        case .registerUsersPeek(let source):\n            return registerPeek(for: source, route: .users)\n        case .logout:\n            return .dismiss()\n        }\n    }\n}\n```\n\nRoutes are triggered from within Coordinators or ViewModels. In the following, we describe how to trigger routes from within a ViewModel. The router of the current flow is injected into the ViewModel.\n\n```swift\nclass HomeViewModel {\n    let router: UnownedRouter\u003cHomeRoute\u003e\n\n    init(router: UnownedRouter\u003cHomeRoute\u003e) {\n        self.router = router\n    }\n\n    /* ... */\n\n    func usersButtonPressed() {\n        router.trigger(.users)\n    }\n}\n```\n\n### 🏗 Organizing an app's structure with XCoordinator\n\nIn general, an app's structure is defined by nesting coordinators and view controllers. You can transition (i.e. `push`, `present`, `pop`, `dismiss`) to a different coordinator whenever your app changes to a different flow. Within a flow, we transition between viewControllers.\n\nExample: In `UserListCoordinator.prepareTransition(for:)` we change from the `UserListRoute` to the `UserRoute` whenever the `UserListRoute.user` route is triggered. By dismissing a viewController in `UserListRoute.logout`, we additionally switch back to the previous flow - in this case the `HomeRoute`.\n\nTo achieve this behavior, every Coordinator has its own `rootViewController`. This would be a `UINavigationController` in the case of a `NavigationCoordinator`, a `UITabBarController` in the case of a `TabBarCoordinator`, etc. When transitioning to a Coordinator/Router, this `rootViewController` is used as the destination view controller.\n\n### 🏁 Using XCoordinator from App Launch\n\nTo use coordinators from the launch of the app, make sure to create the app's `window` programmatically in `AppDelegate.swift` (Don't forget to remove `Main Storyboard file base name` from `Info.plist`). Then, set the coordinator as the root of the `window`'s view hierarchy in the `AppDelegate.didFinishLaunching`. Make sure to hold a strong reference to your app's initial coordinator or a `strongRouter` reference.\n\n```swift\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    let window: UIWindow! = UIWindow()\n    let router = AppCoordinator().strongRouter\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -\u003e Bool {\n        router.setRoot(for: window)\n        return true\n    }\n}\n```\n\n## 🤸‍♂️ Extras\n\nFor more advanced use, XCoordinator offers many more customization options. We introduce custom animated transitions and deep linking. Furthermore, extensions for use in reactive programming with RxSwift/Combine and options to split up huge routes are described.\n\n### 🌗 Custom Transitions\n\nCustom animated transitions define presentation and dismissal animations. You can specify `Animation` objects in `prepareTransition(for:)` in your coordinator for several common transitions, such as `present`, `dismiss`, `push` and `pop`. Specifying no animation (`nil`) results in not overriding previously set animations. Use `Animation.default` to reset previously set animation to the default animations UIKit offers.\n\n```swift\nclass UsersCoordinator: NavigationCoordinator\u003cUserRoute\u003e {\n\n    /* ... */\n    \n    override func prepareTransition(for route: UserRoute) -\u003e NavigationTransition {\n        switch route {\n        case .user(let name):\n            let animation = Animation(\n                presentationAnimation: YourAwesomePresentationTransitionAnimation(),\n                dismissalAnimation: YourAwesomeDismissalTransitionAnimation()\n            )\n            let viewController = UserViewController.instantiateFromNib()\n            let viewModel = UserViewModelImpl(name: name, router: unownedRouter)\n            viewController.bind(to: viewModel)\n            return .push(viewController, animation: animation)\n        /* ... */\n        }\n    }\n}\n```\n\n### 🛤 Deep Linking\n\nDeep Linking can be used to chain different routes together. In contrast to the `.multiple` transition, deep linking can identify routers based on previous transitions (e.g. when pushing or presenting a router), which enables chaining of routes of different types. Keep in mind, that you cannot access higher-level routers anymore once you trigger a route on a lower level of the router hierarchy.\n\n```swift\nclass AppCoordinator: NavigationCoordinator\u003cAppRoute\u003e {\n\n    /* ... */\n\n    override func prepareTransition(for route: AppRoute) -\u003e NavigationTransition {\n        switch route {\n        /* ... */\n        case .deep:\n            return deepLink(AppRoute.login, AppRoute.home, HomeRoute.news, HomeRoute.dismiss)\n        }\n    }\n}\n```\n\n⚠️ XCoordinator does not check at compile-time, whether a deep link can be executed. Rather it uses assertionFailures to inform about incorrect chaining at runtime, when it cannot find an appropriate router for a given route. Keep this in mind when changing the structure of your app.\n\n### 🚏 RedirectionRouter\n\nLet's assume, there is a route type called `HugeRoute` with more than 10 routes. To decrease coupling, `HugeRoute` needs to be split up into multiple route types. As you will discover, many routes in `HugeRoute` use transitions dependent on a specific rootViewController, such as `push`, `show`, `pop`, etc. If splitting up routes by introducing a new router/coordinator is not an option, XCoordinator has two solutions for you to solve such a case: `RedirectionRouter` or using multiple coordinators with the same rootViewController ([see this section for more information](#using-multiple-coordinators-with-the-same-rootviewcontroller)).\n\nA `RedirectionRouter` can be used to map a new route type onto a generalized `ParentRoute`. A `RedirectionRouter` is independent of the `TransitionType` of its parent router. You can use `RedirectionRouter.init(viewController:parent:map:)` or subclassing by overriding `mapToParentRoute(_:)` to create a `RedirectionRouter`.\n\nThe following code example illustrates how a `RedirectionRouter` is initialized and used.\n\n```swift\nclass ParentCoordinator: NavigationCoordinator\u003cParentRoute\u003e {\n    /* ... */\n    \n    override func prepareTransition(for route: ParentRoute) -\u003e NavigationTransition {\n        switch route {\n        /* ... */\n        case .child:\n            let childCoordinator = ChildCoordinator(parent: unownedRouter)\n            return .push(childCoordinator)\n        }\n    }\n}\n\nclass ChildCoordinator: RedirectionRouter\u003cParentRoute, ChildRoute\u003e {\n    init(parent: UnownedRouter\u003cParentRoute\u003e) {\n        let viewController = UIViewController() \n        // this viewController is used when performing transitions with the Subcoordinator directly.\n        super.init(viewController: viewController, parent: parent, map: nil)\n    }\n    \n    /* ... */\n    \n    override func mapToParentRoute(for route: ChildRoute) -\u003e ParentRoute {\n        // you can map your ChildRoute enum to ParentRoute cases here that will get triggered on the parent router.\n    }\n}\n```\n\n### 🚏Using multiple coordinators with the same rootViewController\n\nWith XCoordinator 2.0, we introduce the option to use different coordinators with the same rootViewController.\nSince you can specify the rootViewController in the initializer of a new coordinator, you can specify an existing coordinator's rootViewController as in the following:\n\n```swift\nclass FirstCoordinator: NavigationCoordinator\u003cFirstRoute\u003e {\n    /* ... */\n    \n    override func prepareTransition(for route: FirstRoute) -\u003e NavigationTransition {\n        switch route {\n        case .secondCoordinator:\n            let secondCoordinator = SecondCoordinator(rootViewController: self.rootViewController)\n            addChild(secondCoordinator)\n            return .none() \n            // you could also trigger a specific initial route at this point, \n            // such as `.trigger(SecondRoute.initial, on: secondCoordinator)`\n        }\n    }\n}\n```\n\nWe suggest to not use initial routes in the initializers of sibling coordinators, but instead using the transition option in the `FirstCoordinator` instead. \n\n⚠️ If you perform transitions involving a sibling coordinator directly (e.g. pushing a sibling coordinator without overriding its `viewController` property), your app will most likely crash.\n\n### 🚀 RxSwift/Combine extensions\n\nReactive programming can be very useful to keep the state of view and model consistent in a MVVM architecture. Instead of relying on the completion handler of the `trigger` method available in any `Router`, you can also use our RxSwift-extension. In the example application, we use Actions (from the [Action](https://github.com/RxSwiftCommunity/Action) framework) to trigger routes on certain UI events - e.g. to trigger `LoginRoute.home` in `LoginViewModel`, when the login button is tapped.\n\n```swift\nclass LoginViewModelImpl: LoginViewModel, LoginViewModelInput, LoginViewModelOutput {\n\n    private let router: UnownedRouter\u003cAppRoute\u003e\n\n    private lazy var loginAction = CocoaAction { [unowned self] in\n        return self.router.rx.trigger(.home)\n    }\n\n    /* ... */\n}\n\n```\n\nIn addition to the above-mentioned approach, the reactive `trigger` extension can also be used to sequence different transitions by using the `flatMap` operator, as can be seen in the following:\n\n```swift\nlet doneWithBothTransitions = \n    router.rx.trigger(.home)\n        .flatMap { [unowned self] in self.router.rx.trigger(.news) }\n        .map { true }\n        .startWith(false)\n```\n\nWhen using `XCoordinator` with the `Combine` extensions, you can use `router.publishers.trigger` instead of `router.rx.trigger`.\n\n## 📚 Documentation \u0026 Example app\n\nTo get more information about XCoordinator, check out the [documentation](https://quickbirdeng.github.io/XCoordinator/).\nAdditionally, this [repository](https://github.com/quickbirdstudios/XCoordinator-Example) serves as an example project using a MVVM architecture with XCoordinator.\n\nFor a MVC example app, have a look at [some presentations](https://github.com/quickbirdstudios/XCoordinator-Talks) we did about the Coordinator pattern and XCoordinator.\n\n## 👨‍✈️ Why coordinators\n\n* **Separation of responsibilities** with the coordinator being the only component knowing anything related to the flow of your application.\n* **Reusable Views and ViewModels** because they do not contain any navigation logic.\n* **Less coupling between components**\n\n* **Changeable navigation**: Each coordinator is only responsible for one component and does not need to make assumptions about its parent. It can therefore be placed wherever we want to.\n\n\u003e [The Coordinator](http://khanlou.com/2015/01/the-coordinator/) by **Soroush Khanlou**\n\n\n## ⁉️ Why XCoordinator\n\n* Actual **navigation code is already written** and abstracted away.\n* Clear **separation of concerns**:\n  - Coordinator: Coordinates routing of a set of routes.\n  - Route: Describes navigation path.\n  - Transition: Describe transition type and animation to new view.\n* **Reuse** coordinators, routers and transitions in different combinations.\n* Full support for **custom transitions/animations**.\n* Support for **embedding child views** / container views.\n* Generic `BasicCoordinator` classes suitable for many use cases and therefore **less** need to write your **own coordinators**.\n* Full **support** for your **own coordinator classes** conforming to our Coordinator protocol\n  - You can also start with one of the following types to get a head start: `NavigationCoordinator`, `ViewCoordinator`, `TabBarCoordinator` and more.\n* Generic AnyRouter type erasure class encapsulates all types of coordinators and routers supporting the same set of routes. Therefore you can **easily replace coordinators**.\n* Use of enum for routes gives you **autocompletion** and **type safety** to perform only transition to routes supported by the coordinator.\n\n## 🔩 Components\n\n### 🎢 Route\n\nDescribes possible navigation paths within a flow, a collection of closely related scenes.\n\n### 👨‍✈️ Coordinator / Router\n\nAn object loading views and creating viewModels based on triggered routes. A Coordinator creates and performs transitions to these scenes based on the data transferred via the route. In contrast to the coordinator, a router can be seen as an abstraction from that concept limited to triggering routes. Often, a Router is used to abstract from a specific coordinator in ViewModels.\n\n#### When to use which Router abstraction\n\nYou can create different router abstractions using the `unownedRouter`, `weakRouter` or `strongRouter` properties of your `Coordinator`.\nYou can decide between the following router abstractions of your coordinator:\n\n- **StrongRouter** holds a strong reference to the original coordinator. You can use this to hold child coordinators or to specify a certain router in the `AppDelegate`.\n- **WeakRouter** holds a weak reference to the original coordinator. You can use this to hold a coordinator in a viewController or viewModel. It can also be used to keep a reference to a sibling or parent coordinator. \n- **UnownedRouter** holds an unowned reference to the original coordinator. You can use this to hold a coordinator in a viewController or viewModel. It can also be used to keep a reference to a sibling or parent coordinator.\n\nIf you want to know more about the differences on how references can be held, have a look [here](https://docs.swift.org/swift-book/LanguageGuide/AutomaticReferenceCounting.html).\n\n### 🌗 Transition\n\nTransitions describe the navigation from one view to another. Transitions are available based on the type of the root view controller in use. Example: Whereas `ViewTransition` only supports basic transitions that every root view controller supports, `NavigationTransition` adds navigation controller specific transitions.\n\nThe available transition types include:\n  - **present** presents a view controller on top of the view hierarchy - use **presentOnRoot** in case you want to present from the root view controller\n  - **embed** embeds a view controller into a container view\n  - **dismiss** dismisses the top most presented view controller - use **dismissToRoot** to call dismiss on the root view controller\n  - **none** does nothing, may be used to ignore routes or for testing purposes\n  - **push** pushes a view controller to the navigation stack (only in `NavigationTransition`)\n  - **pop** pops the top view controller from the navigation stack (only in `NavigationTransition`)\n  - **popToRoot** pops all the view controllers on the navigation stack except the root view controller (only in `NavigationTransition`)\n  \n  XCoordinator additionally supports common transitions for `UITabBarController`, `UISplitViewController` and `UIPageViewController` root view controllers.\n\n## 🛠 Installation\n\n#### CocoaPods\n\nTo integrate XCoordinator into your Xcode project using CocoaPods, add this to your `Podfile`:\n\n```ruby\npod 'XCoordinator', '~\u003e 2.0'\n```\n\nTo use the RxSwift extensions, add this to your `Podfile`:\n\n```ruby\npod 'XCoordinator/RxSwift', '~\u003e 2.0'\n```\n\nTo use the Combine extensions, add this to your `Podfile`:\n\n```ruby\npod 'XCoordinator/Combine', '~\u003e 2.0'\n```\n\n#### Carthage\n\nTo integrate XCoordinator into your Xcode project using Carthage, add this to your `Cartfile`:\n\n```\ngithub \"quickbirdstudios/XCoordinator\" ~\u003e 2.0\n```\n\nThen run `carthage update`.\n\nIf this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).\n\n#### Swift Package Manager\n\nSee [this WWDC presentation](https://developer.apple.com/videos/play/wwdc2019/408/) about more information how to adopt Swift packages in your app.\n\nSpecify `https://github.com/quickbirdstudios/XCoordinator.git` as the `XCoordinator` package link. \nYou can then decide between three different frameworks, i.e. `XCoordinator`, `XCoordinatorRx` and `XCoordinatorCombine`. \nWhile `XCoordinator` contains the main framework, you can choose `XCoordinatorRx` or `XCoordinatorCombine` to get `RxSwift` or `Combine` extensions as well.\n\n#### Manually\n\nIf you prefer not to use any of the dependency managers, you can integrate XCoordinator into your project manually, by downloading the source code and placing the files on your project directory.  \n\n## 👤 Author\nThis framework is created with ❤️ by [QuickBird Studios](https://quickbirdstudios.com).\n\nTo get more information on XCoordinator check out [our blog post](https://quickbirdstudios.com/blog/ios-navigation-library-based-on-the-coordinator-pattern/).\n\n## ❤️ Contributing\n\nOpen an issue if you need help, if you found a bug, or if you want to discuss a feature request. If you feel like having a chat about XCoordinator with the developers and other users, join our [Slack Workspace](https://join.slack.com/t/xcoordinator/shared_invite/enQtNDg4NDAxNTk1ODQ1LTkxYzE3MDM5ZGY1MTVmY2NhNjI0Y2JiYmQ5NTdjZDczZDRjZTg1ZmJlOTZmODYyYzMyYWQ0NzhlNGNkMGIzYjQ).\n\nOpen a PR if you want to make changes to XCoordinator.\n\n## 📃 License\n\nXCoordinator is released under an MIT license. See [License.md](https://github.com/quickbirdstudios/XCoordinator/blob/master/LICENSE) for more information.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquickbirdeng%2Fxcoordinator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fquickbirdeng%2Fxcoordinator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fquickbirdeng%2Fxcoordinator/lists"}