Skip to content

Fix double-callback and nil category in PostCategoryService create - #25970

Merged
jkmassel merged 5 commits into
trunkfrom
jkmassel/postcategory-nil-success
Sep 2, 2026
Merged

Fix double-callback and nil category in PostCategoryService create#25970
jkmassel merged 5 commits into
trunkfrom
jkmassel/postcategory-nil-success

Conversation

@jkmassel

@jkmassel jkmassel commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Follows the pattern from #25964, and fixes a latent double-callback.

Summary

  • createCategoryWithName:…success:failure: runs its save in performAndSaveUsingBlock:. On the !blog path the save block calls failure(...) and returns — but the return only exits the save block, so completion: still ran and called success(...).
  • completion: resolves the category via lookupWithBlogObjectID:…, which returns nil when the category was never created, and passed that nil into the _Nonnull (PostCategory *) success block.

Root Cause

Two bugs on one path: (1) both failure and success fire when the blog is missing — the alert shows and the screen dismisses via didCreate — and (2) success can receive a nil category.

Fix

  • Gate the success callback on a non-nil looked-up category. On the !blog path the lookup is nil, so only failure fires; on success success receives a real PostCategory, keeping the _Nonnull annotation honest.

Test plan

  • WordPress app builds (generic iOS Simulator).
  • Creating a category on a remote blog succeeds and dismisses once.
  • With the blog missing, the error is shown and the screen does not also dismiss.

Part of the Objective-C non-null nullability audit; see #25964.

@jkmassel jkmassel added this to the 27.3 milestone Sep 1, 2026
@jkmassel jkmassel self-assigned this Sep 1, 2026
@wpmobilebot

wpmobilebot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in WordPress by scanning the QR code below to install the corresponding build.
App NameWordPress
ConfigurationRelease-Alpha
Build Number34254
VersionPR #25970
Bundle IDorg.wordpress.alpha
Commit8eec585
Installation URL59ct1crp42po0
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot

wpmobilebot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor
App Icon📲 You can test the changes from this Pull Request in Jetpack by scanning the QR code below to install the corresponding build.
App NameJetpack
ConfigurationRelease-Alpha
Build Number34254
VersionPR #25970
Bundle IDcom.jetpack.alpha
Commit8eec585
Installation URL40vg62emje1eg
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

The prior fix guarded `success(newCategory)` behind `if (newCategory)`,
which stopped the `success(nil)` crash but left a path where neither
`success` nor `failure` fires: the blog resolves and the category saves,
but the main-context lookup returns nil (e.g. a response that carried no
usable category ID). The only caller, `PostCategoryCreateView`, clears
`isSaving` solely in its failure block, so a dropped callback hangs the
save spinner indefinitely.

Adopt the `__block NSError *error` pattern already used by
`syncCategoriesForBlog:`: the no-blog branch records the error and the
main-queue completion delivers exactly one callback — `failure(error)`,
`success(category)`, or `failure(serviceErrorCategoryNotFound)` when the
created category can't be resolved locally. This also moves the no-blog
failure onto the main queue, matching the success path; the regression
test now asserts that delivery is on the main thread.
`PostCategoryServiceTests` only exercised the no-blog error path; the happy
path — blog resolves, the category is created and handed back through
`success` — was never executed by any test. Save the blog so its objectID
resolves in the background save context, then assert `success` fires once on
the main queue with the created category's ID, name, and parent, and that
`failure` does not fire.
`createCategoryWithName:…:success:failure:` has a single caller —
`PostCategoryCreateView` (Swift) — which always provides both blocks, so the
`nullable` annotations bought nothing but three `if (success)` / `if (failure)`
guards. Mark both blocks non-null: Swift enforces `_Nonnull` at compile time,
so the caller cannot pass nil, and the completion drops the guards while making
"exactly one callback" a hard invariant.

The sibling `syncCategoriesForBlog:` methods stay `nullable` — they are
genuinely called with `success:nil failure:nil` (the XML-RPC post-create
re-sync).
@jkmassel
jkmassel added this pull request to the merge queue Sep 2, 2026
Merged via the queue into trunk with commit d7176ca Sep 2, 2026
28 checks passed
@jkmassel
jkmassel deleted the jkmassel/postcategory-nil-success branch September 2, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants