Repository navigation
DeallocTests 4.0 (1/5): groundwork – cleanup, CI, Dependency Injection as a package trait - #25
Open
DanielCech wants to merge 1 commit into
Open
DanielCech wants to merge 1 commit into
DanielCech wants to merge 1 commit into
Conversation
…kage trait Removes tooling that no longer runs (Travis, Danger, Carthage bootstrap, jazzy, .swift-version, .ruby-version, Objective-C headers, an empty file and a Quick spec that never compiled). The package fetches ios-dependency-injection over https, so it resolves without SSH access to GitHub, and GitHub Actions builds it. The DeallocTestsDIFree product compiled the same sources through a symlinked target and switched Dependency Injection on with canImport, which depends on whatever else is in the build. One target now does it with the DependencyInjection package trait (Swift 6.1), on by default, accepting STRV Dependency Injection 1.0.4 up to 2.x. The DIFree sample uses the trait and gets the @MainActor/await it needed to compile under Swift 6. BREAKING CHANGE: the DeallocTestsDIFree product is removed; link DeallocTests with the default traits disabled. Swift 6.1 is required. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 5, 2026
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The package, sample, conditional-compilation, cleanup, and CI changes are internally consistent with the stated groundwork scope.
Review effort: Balanced
Findings: None
What changed in this PR
Modernizes package infrastructure by replacing the DIFree target with a package trait, adding CI, and removing obsolete tooling.
Changes:
- Adds conditional Dependency Injection support through a default package trait.
- Updates sample apps to use local package references.
- Replaces legacy tooling with GitHub Actions builds.
| File | Description |
|---|---|
.github/workflows/ci.yml |
Adds macOS trait-matrix and iOS builds. |
.jazzy.yml |
Removes obsolete Jazzy configuration. |
.ruby-version |
Removes obsolete Ruby pin. |
.swift-version |
Removes obsolete Swift pin. |
.travis.yml |
Removes legacy Travis CI. |
Dangerfile |
Removes unused Danger checks. |
Package.resolved |
Uses HTTPS and updates the DI pin. |
Package.swift |
Adds the DI trait and removes DIFree. |
SampleApps/DeallocTestsAppDIFreeSPM/DeallocTestsAppSPM.xcodeproj/project.pbxproj |
Migrates DIFree sample to the trait-disabled product. |
SampleApps/DeallocTestsAppDIFreeSPM/DeallocTestsAppSPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved |
Removes obsolete sample dependency pins. |
SampleApps/DeallocTestsAppDIFreeSPM/DeallocTestsAppSPMTests/DeallocTestConformances.swift |
Updates the module import. |
SampleApps/DeallocTestsAppDIFreeSPM/DeallocTestsAppSPMTests/MainCoordinatorDeallocTester.swift |
Updates imports and actor isolation. |
SampleApps/DeallocTestsAppSPM/DeallocTestsAppSPM.xcodeproj/project.pbxproj |
Adds a local package reference. |
Sources/DeallocTests.h |
Removes an obsolete umbrella header. |
Sources/DeallocTests/DeallocTests.h |
Removes an obsolete umbrella header. |
Sources/DeallocTests/DeallocTests.swift |
Removes an empty placeholder source. |
Sources/DeallocTests/DeallocTester.swift |
Uses the package trait for conditional DI support. |
Tests/DeallocTestsSpec.swift |
Removes the non-compiling empty test. |
scripts/bootstrap |
Removes the obsolete Carthage bootstrap. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
DanielCech
added this pull request to stack #30
October 9, 2026 08:03
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.
Part 1 of 5. DeallocTests 4.0 series (merge in order): #25 groundwork → #26
expectDeallocation→ #27 tracking, hints, DI → #28 configuration → #29 remove legacy, docs. Why 4.0, the breaking changes and the alternatives we looked at: see #25.DeallocTests 4.0 in 5 PRs
DeallocTests checks that objects (screens, view models, coordinators, services) go away after use, so retain cycles fail a unit test instead of shipping. The idea is unchanged and still good. The implementation from 2019 is not: some checks give wrong answers, it can't be used from Swift Testing, and its failures don't say where the leak is. 4.0 keeps the idea and replaces the implementation.
This is a series of 5 stacked PRs from
masterto 4.0.0, one per theme. Each one builds, passes its tests (macOS with and without the Dependency Injection trait, and the iOS Simulator for the UI parts) and can be reviewed in one sitting. The last one ends at the reference branchdc/feat/v4-grace-period(ca911ab) plus the fixes from reviewing this series.Why change it
3.0.1 has bugs that affect projects using it today:
objectCreationreturnsnil(easy with[weak self]), the test crashes; a non-DeallocTestableobject hangs it.FooViewControlleris freed and another leaks, the test passes.DeallocTestsDIFreeis a symlinked copy of the target that switches DI on withcanImport, which depends on whatever else is in the build.And it doesn't fit how we write tests now:
extension X: @retroactive DeallocTestable {}; every test subclassesDeallocTesterand waits for an expectation.What 4.0 looks like
A leak fails on that line and says where to look:
.loadView,.present,.push,.hosting(SwiftUI),.custom. Many leaks only appear on screen.trackForDeallocationchecks a screen's view model together with the screen, and the system under test of any unit test.expectDeallocation(of: APIManaging.self, resolvedFrom: container).Why the breaking changes
4.0 is a major version, and it uses that:
DeallocTester,DeallocTestableand friends are removed, not deprecated. Everything they did is covered byexpectDeallocation, and keeping them means two ways to write the same test plus a buggy code path to maintain.DeallocTestsDIFreeis replaced by a package trait. Projects with STRV Dependency Injection change nothing; others turn the trait off.DefaultInitializableis removed; it wasn't related to dealloc testing.Migration is mechanical: each
DeallocTestbecomes oneexpectDeallocationcall and the conformance file is deleted (mapping table in the README). Projects that can't migrate yet stay on 3.x, which keeps working.Proof on a real app: the JustFlip branch
dc/feat/dealloc-tests-v4has about 130 dealloc tests on 4.0: repositories, services, managers, every store in its SwiftUI view, every coordinator flow and the screens it opens. They run in about 45 s and found a real bug: ten store types shared one state object between all their instances.Alternatives we looked at
trackForMemoryLeaksidiom (XCTest teardown block)trackForDeallocationis this idiom for both frameworks.swift-issue-reportingsolves failure reporting, which 4.0 needs only a 60-line slice of.No maintained library does unit-test leak checks with screen lifecycles, Swift Testing and SwiftUI support.
The PRs
DeallocTestsDIFreeexpectDeallocationwith.custom,.loadView,.present,.push,.hosting; both test frameworkstrackForDeallocationand.checksDeallocation, leak hints,expectDeallocation(of:resolvedFrom:)DeallocTester& co., migrate the sample apps, README, migration guide, CHANGELOGEach PR targets the previous one. 2–4 only add API next to the old one, so the library works at every step; 5 removes the old API once the new one is complete. Each PR description has a "How to review" order, so the larger ones (2 and 3) can be read file by file.
Not in scope
This PR: groundwork
Groundwork: remove tooling that no longer runs, make the package resolvable everywhere, get CI, and turn Dependency Injection into a package trait.
Why
.swift-version5.0,.ruby-version, Objective-C umbrella headers and an empty source file. None of it runs.Tests/DeallocTestsSpec.swiftimports Quick and Nimble, which the package doesn't declare, so the test target never compiled.git@github.com:strvcom/ios-dependency-injection.git. SwiftPM resolves every dependency, so consumers without SSH access to GitHub, including many CI runners andDeallocTestsDIFreeusers, couldn't resolve the package.DeallocTestsDIFreecompiled the same sources through a symlinked target and switched DI on with#if canImport(DependencyInjection), which depends on what else is in the build graph. Swift 6.1 package traits (SE-0450) do this properly.What changes
Package.swift: tools 6.1, https URL, aDependencyInjectiontrait on by default, STRV Dependency Injection 1.0.4 up to 2.x accepted (DeallocTests only uses API both have).DeallocTesterchecks#if DependencyInjectioninstead ofcanImport(7 one-word changes).DeallocTestswith the trait off, and gets the@MainActor/awaitit needed to compile under Swift 6.How to review
Package.swiftand.github/workflows/ci.ymlDeallocTester.swift:canImport(DependencyInjection)→DependencyInjectionproject.pbxprojswitches only the package reference and productBreaking change
DeallocTestsDIFreeis removed. LinkDeallocTests,import DeallocTests, and disable the default traits:Testing
The package builds on macOS with and without the trait, and for the iOS Simulator. Both sample apps build for testing.
🤖 Generated with Claude Code