Seed PocketCastsSharedModels with two cross-target DTOs#4255
Draft
mokagio wants to merge 3 commits into
Draft
Conversation
`CommonUpNextItem` and `SiriPodcastItem` are passed across process boundaries (main app ↔ widget extension, main app ↔ intents extension) via App Group storage and Siri shortcut payloads. The Codable representation is the wire format, so a field rename or type change would silently break the consumers at runtime. Lock the contract with explicit round-trip tests now, before extracting the types into a new shared SPM module. The tests will move alongside the types in a follow-up commit. --- Generated with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Seeds a new `PocketCastsSharedModels` SPM module and moves `CommonUpNextItem` into it as the kernel of cross-target sharing. The DTO was previously a member of three Xcode targets via target membership in `podcasts.xcodeproj` (`podcasts`, `WidgetExtension`, `PodcastsIntents`). That pattern compiles the same source three times and gives every target a private copy of the type, which is brittle: a rename or signature change must be coordinated across copies, and divergence is invisible until something fails at the App Group / process boundary the type was meant to cross. Owning the type in a real module replaces three copies with one, gives us a place to test the wire format, and proves the wiring for follow-up moves. `SiriPodcastItem` lands next in a separate commit using the same pattern; `SharedConstants` is a future move that will pull `Share Extension` into the SPM `XcodeSupport` list. The module test target is registered in `UnitTests.xctestplan` so `-only-testing:PocketCastsSharedModelsTests` runs under the `Pocket Casts Staging` scheme alongside the other module tests. --- Generated with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same playbook as `CommonUpNextItem`: lift the DTO out of the multi-target-membership pattern and into `PocketCastsSharedModels`. The file was a member of `podcasts`, `PodcastsIntents`, and `PodcastsIntentsUI` in `podcasts.xcodeproj`, but `PodcastsIntentsUI` contains no source that actually references the type — that membership was stale. The new module is added as a SwiftPM dependency only on `podcasts` and `PodcastsIntents`; `PodcastsIntentsUI` keeps the dependency list it already had. --- Generated with the help of Claude Code, https://code.claude.com Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Collaborator
Generated by 🚫 Danger |
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.
Fixes PCIOS-
Why
We have a handful of small Codable DTOs that are physically members of two or more Xcode targets via target membership in
podcasts.xcodeproj(e.g.CommonUpNextItemis inpodcasts,WidgetExtension, andPodcastsIntents).That pattern compiles the same source three times, gives every target a private copy of the type, and makes a rename or signature change a coordination problem across Xcode targets — with the failure mode being silent runtime breakage at the App Group / process boundary the type was meant to cross.
This PR seeds a new SPM module,
PocketCastsSharedModels, and moves two of those DTOs (CommonUpNextItem,SiriPodcastItem) into it.The goal is not these specific files — it's to break ground on the pattern, so future shared types land in a real module instead of accreting more multi-target memberships.
The commit messages have the rationale per step; the PR is intentionally three small commits so it's easy to walk:
Add Codable round-trip tests for shared DTOs— locks the wire format in the existing test target before the move.Extract CommonUpNextItem into shared module— creates the module, the test target, the test plan entry, and moves the first file.Move SiriPodcastItem into shared module— same playbook.Intentional tradeoffs
publicinitializers andpublicproperties.No behaviour change, just access.
PodcastsIntentsUIhad a stale target membership ofSiriPodcastItembut no source actually referencing it.The membership is dropped without adding a SwiftPM dependency.
SharedConstantsis a sensible follow-up but stays out of this PR.Moving it would touch seven targets and require pulling
Share ExtensionintoXcodeSupport.targets, which is meaningfully larger surgery than this kernel should carry.Gotchas
UnitTests.xctestplanusescontainerPath = container:Modulesfor the new test target.The other module test entries use decorative paths like
container:Modules/Utilsthat don't resolve to real directories — Xcode tolerates them historically, but a fresh entry needs the actual package path or xcodebuild fails the build with "isn't a member of the specified test plan or scheme".podcasts.xcodeproj/project.pbxprojto drop file references, build files, group children, and target memberships for the moved files.Each removal is a single-line surgical delete;
plutil -lintis clean.How to test
make build_stagingshould succeed (verified locally on iPhone 17 / iOS 26.4 simulator, all extensions + watch + app clip + TV app).make test_staging ONLY_TESTING=PocketCastsSharedModelsTestsshould pass 3 tests (2CommonUpNextItemround-trips + 1SiriPodcastItemround-trip).make test_staging ONLY_TESTING=PocketCastsTestsshould still be green (323 passed, 0 failures, 1 expected failure locally).Checklist
CHANGELOG.mdif necessary. (no user-facing change)Posted by Claude (Opus 4.7) on behalf of @mokagio with approval.