refactor!: rename MediaUploadDelegate to MediaProcessor - #630
Open
jkmassel wants to merge 3 commits into
Open
Conversation
This was referenced Sep 5, 2026
XCFramework BuildThis PR's XCFramework is available for testing. Add the following to your .package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/630")Built from 43806eb |
jkmassel
force-pushed
the
refactor/media-processor-rename
branch
from
September 8, 2026 16:12
f6a9bac to
2a835c1
Compare
jkmassel
force-pushed
the
refactor/media-processor-rename
branch
2 times, most recently
from
September 9, 2026 16:48
976ade8 to
3910919
Compare
The protocol no longer uploads anything — the previous commit removed `uploadFile`, leaving `handlesFile` and `processFile`. "UploadDelegate" now describes the one thing it can't do, and next to `MediaUploader` the two names read as variations on the same job rather than the two halves of a deliberate split. `MediaProcessor` says what is left: it transforms bytes, GutenbergKit delivers them. Mechanical throughout — the property becomes `mediaProcessor`, the server parameter `processor`, the file `MediaHandlers.swift` (it holds both protocols now), and Android's demo `DemoMediaProcessor`. Prose follows the types. The `weak_delegate` suppression added when the property became strong goes away with the name: the rule was arguably right that a strongly-held "delegate" is a smell, and the answer was that this was never a delegate. BREAKING CHANGE: `mediaUploadDelegate` is now `mediaProcessor`, and `MediaUploadDelegate` is `MediaProcessor`. Conformances need no changes beyond the name.
`MediaProcessor` and `MediaUploader` were both `AnyObject`-bound, and `EditorViewController` holds both strongly. A conformer that holds the view controller back therefore closes a retain cycle ARC cannot break: the editor is never freed, so `deinit` never runs, so `uploadServer.stop()` — its only caller — never runs either, and a bound loopback `NWListener` outlives the editing session. Nothing needed class-boundness. There is no `weak`, `===`, or `ObjectIdentifier` use against either protocol anywhere in the tree, and every existing conformer is a class, which conforms unchanged. Dropping the requirement lets a host conform with a value type capturing only what the work needs — the shape that avoids the cycle, and the one a class-bound `Delegate` discouraged. This does not make the cycle impossible: a struct that stores the view controller cycles just the same. The docs say so rather than implying the type system settles it.
`retainsDelegateForServerLifetime` names two properties and only tested one. The processor was bound to a strong local for the whole `do` block, so `#expect(weakDelegate != nil)` was satisfied by that local — the server's ownership was never what the assertion depended on. Confirmed by mutation. With `UploadContext(processor: nil, ...)`, so the server holds no reference to the processor at all, the test **passed**. Nil the host's reference before the assert — the way `processesForHostReleasedDelegate` at :557 already does — and the same mutation fails it. The release half was always live and is unchanged: no-op'ing `releaseConnectionHandler()` still fails the trailing `#expect(weakDelegate == nil)`, which is the regression 7124457 added it for. Pre-existing, from 8827ba4 — the commit that introduced the test to pin the strong-ownership fix it could not actually detect.
jkmassel
force-pushed
the
refactor/media-processor-rename
branch
from
September 9, 2026 18:40
3910919 to
43806eb
Compare
jkmassel
marked this pull request as ready for review
September 9, 2026 19:42
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 #629. Seventh of ten PRs splitting #621. A rename, plus the class-bound relaxation that follows from it — see Breaking change.
What?
MediaUploadDelegate→MediaProcessor,mediaUploadDelegate→mediaProcessor, the server parameter →processor, the file →MediaHandlers.swift, and Android's demo →DemoMediaProcessor.Why?
The protocol no longer uploads anything — #629 removed
uploadFile, leavinghandlesFileandprocessFile. "UploadDelegate" now describes the one thing it can't do, and next toMediaUploaderthe two names read as variations on the same job rather than the two halves of a deliberate split.MediaProcessorsays what is left: it transforms bytes, GutenbergKit delivers them.How?
A rename sweep, ~83 sites across both platforms including the demo apps. Prose in doc comments follows the types, including the property abstracts Xcode Quick Help and Android Studio hover actually render.
MediaHandlers.swiftbecause the file holds both protocols now.The
weak_delegatesuppression added in #625 goes away with the name: the rule was arguably right that a strongly-held "delegate" is a smell, and the answer was that this was never a delegate. The rule keys on the identifier suffix, so it cannot fire onmediaProcessor— the suppression and the paragraph arguing with it are both dead, and nothing in CI would ever have said so.Following that through:
MediaProcessorandMediaUploaderalso drop: AnyObject. Nothing needed class-boundness — there is noweak,===, orObjectIdentifieruse against either protocol anywhere in the tree — andEditorViewControllerholds both strongly, so a class-bound protocol was quietly steering hosts toward a conformer that holds the view controller back and closes a retain cycle ARC cannot break. Dropping it lets a host conform with a value type capturing only what the work needs. Every existing conformer is a class and is unaffected.One test change rides along.
retainsDelegateForServerLifetimenamed two properties and tested one. The processor was bound to a strong local for the wholedoblock, so#expect(weakDelegate != nil)was satisfied by that local and never by the server. We could not make it fail against a server that holds nothing: withUploadContext(processor: nil, …)the test passed. It now drops the host's reference before asserting, the wayprocessesForHostReleasedDelegatealready does, and the same mutation fails it. The release half was always live and is unchanged.Testing Instructions
xcodebuild test— 585 + 395 tests green:Gutenberg:testgreen (re-run with--rerun-tasks); Android and iOS demo apps compileretainsDelegateForServerLifetimemutation-checked both ways —UploadContext(processor: nil, …)passed the old assertion and fails the new one; no-op'ingreleaseConnectionHandler()still fails the release assertion, so that half is untouchedBreaking change
mediaUploadDelegateis nowmediaProcessor, andMediaUploadDelegateisMediaProcessor. Conformances need no changes beyond the name.MediaProcessorandMediaUploaderare also no longerAnyObject-bound. Class conformers are unaffected; a host that declared its ownweakreference to one of these existentials would need to hold it strongly instead.Note this relaxes a constraint rather than fixing the cycle outright — a
structthat stores theEditorViewControllercycles just the same. The doc comments say so rather than implying the type system settles it.A value-type conformer also carries a caveat a class did not: it is copied on assignment and captured once when the editor begins loading, so mutating your own instance afterwards changes nothing the editor will run, and re-assigning the property to push the new value traps — in release as well as debug. Configure a
structconformer at init and treat it as frozen; if you need settings the host can change mid-session, read them insideprocessFilethrough a reference the conformer captures. Documented on both protocols.