Replace CocoaPods with Swift Package Manager - #113
Merged
Conversation
andiwand
force-pushed
the
modernize/05-deployment-target
branch
from
July 26, 2026 08:15
ac50eb6 to
e990efc
Compare
andiwand
force-pushed
the
modernize/06-spm
branch
from
July 26, 2026 08:15
87d7b89 to
2562a58
Compare
andiwand
force-pushed
the
modernize/05-deployment-target
branch
from
July 26, 2026 08:37
e990efc to
f00e7b6
Compare
CocoaPods has been in maintenance mode since 2024 and only two dependencies were left after dropping Firebase. Google Mobile Ads comes from googleads/swift-package-manager-google-mobile-ads, pinned to the 12 line so this is not also a major SDK bump. Package.resolved is committed. ScrollableSegmentedControl has no SPM support: upstream was archived in February 2022. It is 832 lines of MIT licensed UIKit, so it moves into OpenDocumentReader/Vendor with its license and a note on where it came from. The storyboard now instantiates it from the app module. Falling out of this: - the .xcworkspace is gone, the project builds on its own again - the four configs/*.xcconfig files existed only to include the matching Pods xcconfig and then undo its LIBRARY_SEARCH_PATHS. They collapse into one configs/conan.xcconfig - the ENABLE_BITCODE = NO post_install hook is gone with the Podfile. Bitcode was removed in Xcode 14, so it had been dead for years - the test target no longer inherits its build settings from a Pods xcconfig - CI does not run pod install any more Unrelated but adjacent: the conan resources phase carried CocoaPods' [CP] prefix and declared no outputs even though conandeployer.py writes output-files.xcfilelist. It now declares them, so it stops rsyncing every odrcore asset on each build.
andiwand
force-pushed
the
modernize/06-spm
branch
from
July 26, 2026 08:41
2562a58 to
e6a0f6e
Compare
andiwand
added a commit
that referenced
this pull request
Jul 26, 2026
conan installs happily on python 3.11, and then conan/setup-all.sh calls the conan-odr-index helper scripts and dies on a SyntaxError, because those use PEP 701 nested quotes in f-strings. CI already pins 3.12 for exactly this reason; only the README was silent about it. The documented venv lives inside the checkout, so .venv/ joins .gitignore. Without it, following the setup top to bottom leaves every clone with a dirty tree and a large machine-specific directory one `git add .` away from being committed. The step reordering this branch also carried is now redundant: #113 moved the submodule checkout ahead of the pip install while landing SPM. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019978mDv7veoaSpByQPCnNe
andiwand
added a commit
that referenced
this pull request
Jul 26, 2026
conan installs happily on python 3.11, and then conan/setup-all.sh calls the conan-odr-index helper scripts and dies on a SyntaxError, because those use PEP 701 nested quotes in f-strings. CI already pins 3.12 for exactly this reason; only the README was silent about it. The documented venv lives inside the checkout, so .venv/ joins .gitignore. Without it, following the setup top to bottom leaves every clone with a dirty tree and a large machine-specific directory one `git add .` away from being committed. The step reordering this branch also carried is now redundant: #113 moved the submodule checkout ahead of the pip install while landing SPM. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
andiwand
added a commit
that referenced
this pull request
Jul 26, 2026
#113 vendored ScrollableSegmentedControl because its upstream was archived in 2022 and it never gained SPM support, and noted that the alternative was reimplementing the page tabs ourselves. This does that, so 832 lines of third-party UIKit leave the repository. PageTabBar is ~180 lines against the same UICollectionView the vendored control used, and implements only the four things the document view ever asked for: text tabs, an underline under the selection, a horizontal scroll once they no longer fit, and .valueChanged on a tap. Two behaviour changes fall out of it: - Tabs are only as wide as their own title once the row has to scroll. The old control gave every tab the width of the longest one, so a single long sheet name pushed everything else off screen. While they all fit they still share the width evenly, which is what the common case looked like before. - Labels use .label/.secondaryLabel and the bar uses secondarySystemBackground instead of hardcoded dark grey, black and #F9F9F9, so the tabs are legible in dark mode. Tab cells are also accessibility elements now, with the selected trait. Adjacent fixes the rewrite made obvious: - documentPagesChanged appended segments and activated a fresh height constraint every time it ran, so a second announcement would have listed every page twice under conflicting constraints. Titles are assigned wholesale and the height constraint is created once. - The .valueChanged target was added in viewWillAppear, so a second appearance parsed the document twice per tap. It moves to viewDidLoad. - initialSelect is gone: selecting a tab in code no longer sends .valueChanged, following UISegmentedControl, so there is nothing to suppress. - doc.pageNames was force-unwrapped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011MhKU2kWm1cPW4GBq9gon5
andiwand
added a commit
that referenced
this pull request
Jul 26, 2026
* Replace ScrollableSegmentedControl with our own page tab bar #113 vendored ScrollableSegmentedControl because its upstream was archived in 2022 and it never gained SPM support, and noted that the alternative was reimplementing the page tabs ourselves. This does that, so 832 lines of third-party UIKit leave the repository. PageTabBar is ~180 lines against the same UICollectionView the vendored control used, and implements only the four things the document view ever asked for: text tabs, an underline under the selection, a horizontal scroll once they no longer fit, and .valueChanged on a tap. Two behaviour changes fall out of it: - Tabs are only as wide as their own title once the row has to scroll. The old control gave every tab the width of the longest one, so a single long sheet name pushed everything else off screen. While they all fit they still share the width evenly, which is what the common case looked like before. - Labels use .label/.secondaryLabel and the bar uses secondarySystemBackground instead of hardcoded dark grey, black and #F9F9F9, so the tabs are legible in dark mode. Tab cells are also accessibility elements now, with the selected trait. Adjacent fixes the rewrite made obvious: - documentPagesChanged appended segments and activated a fresh height constraint every time it ran, so a second announcement would have listed every page twice under conflicting constraints. Titles are assigned wholesale and the height constraint is created once. - The .valueChanged target was added in viewWillAppear, so a second appearance parsed the document twice per tap. It moves to viewDidLoad. - initialSelect is gone: selecting a tab in code no longer sends .valueChanged, following UISegmentedControl, so there is nothing to suppress. - doc.pageNames was force-unwrapped. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Address review: uneven tab titles and accessibility text sizes Two findings from the Codex review on #122. Sharing the width evenly is only fair while an even share is wide enough for the longest title. With "Q4 Revenue Forecast" next to "A" and "B" the three fit a 320 point bar comfortably, but an even third would truncate the long one for no reason. Tabs now take an even share only while that fits every title; failing that they keep their own widths and share out whatever is left over, so the row still fills without truncating. The tab bar's height was pinned at 40 points while the label scaled with Dynamic Type, so accessibility text sizes were clipped. PageTabBar now reports the height it needs -- which works out to the same 40 points at the default text size -- and the document view follows it, including when the text size changes while a document is open. Co-Authored-By: Claude Opus 5 (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.
Stacked on #111.
CocoaPods has been in maintenance mode since 2024, and after #110 only two dependencies were left.
Google Mobile Ads
From
googleads/swift-package-manager-google-mobile-ads, pinned to12.14.0..<13.0.0so this is a package-manager change and not also a major SDK bump.Package.resolvedis committed (GMA 12.14.0, UserMessagingPlatform 3.1.0).ScrollableSegmentedControl
No SPM support — upstream was archived in February 2022. It was the only thing keeping CocoaPods alive.
It is 832 lines of MIT-licensed UIKit, so it moves to
OpenDocumentReader/Vendor/ScrollableSegmentedControl/verbatim, with itsLICENSEand a README recording the provenance. The storyboard now instantiates it from the app module (customModule="OpenDocumentReader"), andimport ScrollableSegmentedControlis gone.If you would rather not own this file, the alternative is reimplementing the page tabs on
UICollectionView— happy to do that instead, but it is a behavior change and belongs in its own PR.What falls out
.xcworkspaceis gone; the project builds standalone again.configs/*.xcconfigfiles existed only to include the matching Pods xcconfig and then blank out itsLIBRARY_SEARCH_PATHS. They collapse into a singleconfigs/conan.xcconfig.ENABLE_BITCODE = NOpost_installhook goes with the Podfile. Bitcode was removed in Xcode 14, so it had been dead for years.pod install; fastlane and the build job use-project.dependabot.yml:cocoapods→swift.Adjacent fix: the conan resources build phase carried CocoaPods'
[CP]prefix and declared no outputs, even thoughconandeployer.pywritesoutput-files.xcfilelistnext to the input list. It now declares them, so it stops rsyncing every odrcore asset on every build.Verification
ODR Full/Debug andODR Lite/Debug Lite build green.GADBannerViewfrom the SPM framework.Frameworks/contains exactlyGoogleMobileAds.frameworkandUserMessagingPlatform.framework; noPods_*.framework.Aside, not part of this PR
SwiftPM could not resolve any package on this machine —
swift-argument-parserfailed identically to GMA withno versions match. It works with a cleanHOME, so something in~/Library/Caches/org.swift.swiftpm/~/Library/org.swift.swiftpmis wedged. I verified using an isolatedHOMErather than deleting your caches;swift package purge-cachewould likely clear it.