XCoordinator 3.0.0 & Modernization#16
Open
pauljohanneskraft wants to merge 4 commits into
Open
Conversation
There was a problem hiding this comment.
Pull request overview
This PR modernizes the sample app to XCoordinator 3-era APIs and iOS 16+/Swift 5.9 (goodbye, iOS 13), expands the example surface area with SwiftUI interop + deep linking, and replaces the old “unit tests copied from the library” approach with end-to-end UI tests. In other words: fewer historical artifacts, more “does the actual app work?”
Changes:
- Migrates coordinators/view models to XCoordinator 3 conventions (
any Router,@MainActor,XCoordinatorRx) and refactors coordinator transitions into helper-based factories. - Adds SceneDelegate + URL deep linking (including UI-test injection hooks) and introduces a SwiftUI-based home container (
HomeSwiftUICoordinator/HomeSwiftUIView). - Revamps testing/CI: significantly expands UI tests and removes the duplicated unit-test suite previously copied from XCoordinator itself.
Reviewed changes
Copilot reviewed 53 out of 54 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| XCoordinator-ExampleUITests/XCoordinator_ExampleUITests.swift | Adds comprehensive UI tests for picker flows, SwiftUI interop, deep links, and regressions. |
| XCoordinator-ExampleTests/XCText+Extras.swift | Removes legacy unit-test helper (unit test suite deleted). |
| XCoordinator-ExampleTests/XCTestManifests.swift | Removes Linux test manifest (unit test suite deleted). |
| XCoordinator-ExampleTests/XCoordinator-Example.xctestplan | Updates the test plan to focus on UI tests (drops unit target from plan). |
| XCoordinator-ExampleTests/TransitionTests.swift | Removes copied XCoordinator unit tests. |
| XCoordinator-ExampleTests/TestRoute.swift | Removes unit-test-only route type. |
| XCoordinator-ExampleTests/TestAnimation.swift | Removes unit-test-only animation helper. |
| XCoordinator-ExampleTests/AnimationTests.swift | Removes copied XCoordinator unit tests. |
| XCoordinator-Example/Utils/NibIdentifiable.swift | Documents nib instantiation convention used throughout coordinators. |
| XCoordinator-Example/Utils/BindableType.swift | Documents MVVM-C binding contract and usage across scenes. |
| XCoordinator-Example/Services/UserService.swift | Adds protocol documentation clarifying mock-only intent. |
| XCoordinator-Example/Services/NewsService.swift | Adds protocol documentation and tightens swiftlint suppression scope. |
| XCoordinator-Example/Scenes/UserList/UsersViewModelImpl.swift | Migrates router storage to any Router + @MainActor + XCoordinatorRx. |
| XCoordinator-Example/Scenes/UserList/UsersViewController.swift | Adjusts section marker (“Initialization” → “Overrides”). |
| XCoordinator-Example/Scenes/User/UserViewModelImpl.swift | Migrates router storage to any Router + @MainActor + XCoordinatorRx. |
| XCoordinator-Example/Scenes/User/UserViewController.swift | Adjusts section marker (“Initialization” → “Overrides”). |
| XCoordinator-Example/Scenes/News/NewsViewModelImpl.swift | Migrates router storage to any Router + @MainActor + XCoordinatorRx. |
| XCoordinator-Example/Scenes/Login/LoginViewModelImpl.swift | Migrates router storage to any Router + @MainActor + XCoordinatorRx. |
| XCoordinator-Example/Scenes/Login/LoginViewController.swift | Adds accessibility identifier for UI tests (login button). |
| XCoordinator-Example/Scenes/Home/HomeViewModelImpl.swift | Migrates router storage to any Router, removes peek-registration path, adds @MainActor. |
| XCoordinator-Example/Scenes/Home/HomeViewModel.swift | Documents the Input/Output/composite protocol pattern; removes peek API. |
| XCoordinator-Example/Scenes/Home/HomeViewController.swift | Adds accessibility identifier for UI tests (users button) and removes peek wiring. |
| XCoordinator-Example/Scenes/Home/HomeSwiftUIView.swift | Adds SwiftUI home container view embedding UIKit coordinators via WrappedRouter. |
| XCoordinator-Example/Scenes/About/AboutViewModelImpl.swift | Migrates router storage to any Router + @MainActor + XCoordinatorRx. |
| XCoordinator-Example/PrivacyInfo.xcprivacy | Adds privacy manifest stub for modern iOS requirements. |
| XCoordinator-Example/Info.plist | Adds URL scheme + Scene configuration; removes armv7 requirement. |
| XCoordinator-Example/Extensions/Transitions.swift | Documents custom transitions (presentFullScreen, dismissAll). |
| XCoordinator-Example/Extensions/TransitionAnimation+Defaults.swift | Documents shared animation constants and CGFloat.verySmall. |
| XCoordinator-Example/Extensions/Presentable+Rx.swift | Annotates dismissal observable with @MainActor. |
| XCoordinator-Example/Coordinators/UserListCoordinator.swift | Refactors transitions into factories; removes peek route; adds documentation. |
| XCoordinator-Example/Coordinators/UserCoordinator.swift | Refactors transitions into factories; documents routes + interactive wiring. |
| XCoordinator-Example/Coordinators/NewsCoordinator.swift | Refactors transitions into factories; simplifies animation selection for iOS 16+. |
| XCoordinator-Example/Coordinators/HomeTabCoordinator.swift | Switches to any Router storage and adds test identifiers for container visibility. |
| XCoordinator-Example/Coordinators/HomeSwiftUICoordinator.swift | Adds SwiftUI container coordinator using ViewCoordinator + Transition.withAnimation. |
| XCoordinator-Example/Coordinators/HomeSplitCoordinator.swift | Updates SplitCoordinator init API + adds test identifier for container visibility. |
| XCoordinator-Example/Coordinators/HomePageCoordinator.swift | Updates PageCoordinator init API + adds test identifier and deep-link completion note. |
| XCoordinator-Example/Coordinators/AppCoordinator.swift | Updates routing to XCoordinator 3 patterns, adds home picker + deterministic random selection, adds deep-link routes. |
| XCoordinator-Example/Coordinators/AboutCoordinator.swift | Refactors into factories; extracts “open website” as a side-effect transition. |
| XCoordinator-Example/Common/UITestIdentifiers.swift | Centralizes accessibility IDs and UI-test launch arguments (app target side). |
| XCoordinator-Example/Common/SceneDelegate.swift | Adds SceneDelegate bootstrapping and URL handling (incl. UI-test env var hook). |
| XCoordinator-Example/Common/DeepLinkParser.swift | Adds URL-to-route mapping for deep links. |
| XCoordinator-Example/Common/AppDelegate.swift | Converts AppDelegate to scene-based lifecycle configuration. |
| XCoordinator-Example/Animations/Animation+Swirl.swift | Ensures incoming view is framed properly (important for SwiftUI hosting). |
| XCoordinator-Example/Animations/Animation+Scale.swift | Ensures incoming view is framed properly; removes dead code. |
| XCoordinator-Example/Animations/Animation+Navigation.swift | Updates frame calculations to use finalFrame + autoresizing for SwiftUI safety. |
| XCoordinator-Example/Animations/Animation+Modal.swift | Updates modal animation framing to respect finalFrame and autoresizing. |
| XCoordinator-Example/Animations/Animation+Fade.swift | Ensures presented views get a real frame before fading (SwiftUI hosting fix). |
| XCoordinator-Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved | Updates dependency pins (RxSwift 6.x / Action 5.x / XCoordinator revision). |
| XCoordinator-Example.xcodeproj/project.pbxproj | Raises deployment target to iOS 16, updates Swift to 5.9, adds new sources/resources, adjusts SPM refs. |
| README.md | Major rewrite with expanded docs (currently contains some outdated references). |
| CLAUDE.md | Adds contributor guidance and architecture notes for agent workflows. |
| .swift-version | Pins Swift toolchain version to 5.9. |
| .gitignore | Cleans up and modernizes ignore rules (adds .swiftpm, workspace user data, .DS_Store). |
| .github/workflows/ci.yml | Adds GitHub Actions CI to build + run the test plan on macOS. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # XCoordinator-Example | ||
|
|
||
| XCoordinator-Example is a MVVM-C example app for [XCoordinator](https://github.com/quickbirdstudios/XCoordinator). For a MVC example app, have a look at [a workshop](https://github.com/quickbirdstudios/Mobile-HackNight-XCoordinator) we did with a previous version of XCoordinator. | ||
| A sample iOS app showing **MVVM-C with [XCoordinator](https://github.com/quickbirdstudios/XCoordinator) v2** — built specifically to demonstrate the library's container coordinators side by side and to give you a worked example of MVVM-C scene wiring with RxSwift. |
|
|
||
| ## What this example teaches | ||
|
|
||
| A login → home → detail flow built end-to-end with XCoordinator v2 and MVVM-C. Use it as a reference for: |
|
|
||
| A login → home → detail flow built end-to-end with XCoordinator v2 and MVVM-C. Use it as a reference for: | ||
|
|
||
| - **Four coordinator container types in one app** — `NavigationCoordinator`, `TabBarCoordinator`, `SplitCoordinator`, and `PageCoordinator`. The Home screen lets you pick which one drives the same `HomeRoute`, so you can compare them side-by-side. |
Comment on lines
+29
to
+31
| ## The three-home-coordinator picker | ||
|
|
||
| When you reach the Home screen, the app asks you to pick between `HomeTabCoordinator`, `HomeSplitCoordinator`, `HomePageCoordinator`, or a random one. This picker is the point of the example: all three coordinators expose the **same** `HomeRoute { case news; case userList }`, and `AppCoordinator` wraps whichever one you pick into `AppRoute.home(StrongRouter<HomeRoute>)`. |
|
|
||
| `.multiple` chains the transitions sequentially, and `deepLink(...)` walks the coordinator hierarchy by triggering successive routes, so the app lands on the article from *any* current navigation state (modal stacks included). The users path (`AppRoute.userDetail`) takes the same shape and ends in a modal present. | ||
|
|
||
| One gotcha worth knowing if you deep-link through a `PageCoordinator`: `deepLink` chains the next route inside each transition's completion handler, but `UIPageViewController.setViewControllers(…, animated: true)` silently skips its completion when the requested page is *already* on-screen. A deep link whose page step targets the already-visible page would stall there. This example works around it with `Transition.setReliably(_:direction:)` in [`Extensions/Transitions.swift`](XCoordinator-Example/Extensions/Transitions.swift), a drop-in replacement for `.set` that always fires the completion — see [`HomePageCoordinator`](XCoordinator-Example/Coordinators/HomePageCoordinator.swift). |
| - Xcode (current stable) | ||
| - Swift 5.9 | ||
| - iOS 16+ (iPhone and iPad) | ||
| - Swift Package Manager — `XCoordinator` 2.2.1, `RxSwift` 6.10.2, `Action` 5.0.0 |
Comment on lines
+16
to
+17
| /// Present the home flow. Pass `nil` to show the picker that lets the user choose one of `HomeTabCoordinator`, | ||
| /// `HomeSplitCoordinator`, or `HomePageCoordinator`; pass a concrete router to skip the picker. |
Comment on lines
+74
to
+75
| // three home-flow coordinators; the chosen coordinator is re-triggered through `.home(...)`, | ||
| // demonstrating that a `Transition` can present arbitrary decision UI. |
Comment on lines
+52
to
+53
| let alert = app.alerts.firstMatch | ||
| XCTAssertTrue(alert.waitForExistence(timeout: 5), "Picker alert never appeared") |
- ci.yml: pick an available iPhone simulator dynamically instead of the hardcoded `iPhone 16` (the macOS runner image no longer ships it, which failed destination resolution with exit 70). - README: update to XCoordinator 3 (versions, `any Router`, four home containers incl. the SwiftUI one, removed the obsolete `setReliably` note). - AppCoordinator: doc/comment now mention all four home coordinators. - UI tests: match the picker alert by its title instead of `firstMatch`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.