wordpress-mobile / wordpress-mobile/WordPress-iOS
Social sharing entry points assert on publicize-capable blogs with unusable WP.com auth (split `blog.supports` from v2 reachability)
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 3.9k
- Forks
- 1.2k
- Avg merge
- 23h 51m
- Merged PRs (30d)
- 58
Description
The social sharing entry points conflate two different questions — "does this site support publicize?" (capability) and "can the app reach the v2 Publicize API for this site right now?" (reachability) — and wpAssertionFailure when they disagree. They disagree whenever a publicize-capable blog's WP.com token is unusable, which is a normal runtime state, not a programmer error.
Introduced by #25747: on trunk today socialSharingV2 is debug-only, so production still falls through to the legacy SharingViewController. Once #25747 ships, the legacy fallback is gone and the else branch asserts.
Summary
blog.supports(.publicize)validates capability only. Its auth floor issupportsRestAPI(=account != nil); it does not check that the account'sauthTokenis present/usable.ManageConnectionsHostingController.make(for:)(→JetpackSocialFactory.serviceConfiguration(for:)) validates reachability: it additionally requiresdotComID > 0and a non-emptyauthToken.- Two entry points treat
make(for:) == nilas an assertion + bail, so a capable-but-unreachable blog gets a debug crash / release no-op where the Sharing screen should be.
Root cause
supports(.publicize) and make(for:) answer different questions and diverge on exactly the broken-auth state the code comment claims to guard:
Blog+Features.swift—supportsPublicize→supportsRestAPI(account != nil) +isPublishingPostsAllowed()+ (isHostedAtWPcomand notpublicize_permanently_disabled). No token check.WPAccount.handleInvalidToken(...)nullsauthTokenbut keeps theWPAccount, sosupports(.publicize)staystrue.JetpackSocialFactory.serviceConfiguration(for:)requiresdotComID > 0and!authToken.isEmpty→ returnsnil→make(for:)returnsnil.
Affected entry points
- ❌
BlogDetailsViewController+Swift.swift—showSharing(from:):else { return wpAssertionFailure("social connections service unavailable") } - ❌
SiteStatsInsightsTableViewController.swift—growAudienceEnablePostSharingButtonTapped(): samemake(for:) else { wpAssertionFailure(...) } - ✅
CustomPostSettingsViewModel.resolveSocialConnectionsService(...)already does it correctly — it checksblog.supports(.publicize)andJetpackSocialFactory.shared.connectionsService(for:) != nil, and returnsnilgracefully (the section just doesn't render). This is the model the other two should follow.
Repro
- Sign in to a WP.com-hosted blog and confirm Sharing / the Stats grow-audience "Enable post sharing" nudge is available.
- Put the account into the invalid-token state (server-side token revocation / password change, or drive
WPAccount.handleInvalidTokendirectly —authTokenbecomesnil, theWPAccountremains). - Tap My Site → Sharing (or the Stats "Enable post sharing" nudge).
Expected: the sharing/connections screen opens, or the user is routed to re-auth.
Actual: debug build asserts (wpAssertionFailure); release build presents nothing.
Proposed fix (separate PR)
Split the two checks, per @crazytonyli's / review suggestion:
- Capability gates visibility of the entry point → keep
blog.supports(.publicize). - Reachability is validated at open time by building the service —
ManageConnectionsHostingController.make(for:)/JetpackSocialFactory.shared.connectionsService(for:). Treatnilas an expected outcome, not an assertion. Don't re-derivedotComID/authTokenat call sites — the factory is the single source of truth (the two asserting sites drifting from it is what produced this bug). - On
nil, degrade instead of asserting:- auth problem → route to re-auth.
blog.isAccessibleThroughWPCom(=account?.wordPressComRestApi != nil) is the existing helper; touchingwordPressComRestApion an empty token already posts.wpAccountRequiresShowingSigninForWPComFixingAuthToken. - structurally not v2-serviceable → fall back to
SharingButtonsViewControlleror hide the affordance.
- auth problem → route to re-auth.
- Consider consolidating the three entry points behind one presenter so the "service unavailable" policy (and the duplicated
"social connections service unavailable"string) lives in one place next tomake(for:).
Token validity is ultimately a runtime answer — the client can only check a token is present synchronously; server-side validity is only known once a call returns 200 vs 401 (already handled by wordPressComRestApi.setInvalidTokenHandler). So the model is three tiers — capable → service constructible → first loadConnections() doesn't 401 — degrading at each, asserting at none.
Test plan
- Publicize-capable WP.com blog with a valid token → Sharing / nudge open the v2 screen.
- Same blog after token invalidation → routed to re-auth (no crash, no silent no-op).
-
!blog.supports(.publicize)blog → still getsSharingButtonsViewController. - No
wpAssertionFailureon any reachable auth state.
Related (out of scope for this issue)
Same PR (#25747) also drops the grow-audience Publicize nudge's completion path — the deleted SharingViewControllerDelegate.didChangePublicizeServices() was the only caller of markCurrentNudgeAsCompleted() for that flow, so the nudge no longer auto-dismisses after connecting and the .statsPublicizeNudgeCompleted Tracks event is now never fired. Worth a separate follow-up (re-add a completion signal, or mark complete on present).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with showSharing(from:) in BlogDetailsViewController+Swift.swift and growAudienceEnablePostSharingButtonTapped() in SiteStatsInsightsTableViewController.swift, then compare their factory handling with CustomPostSettingsViewModel.resolveSocialConnectionsService(...). Trace ManageConnectionsHostingController.make(for:) into JetpackSocialFactory.serviceConfiguration(for:). Done means capable blogs no longer assert or silently no-op when the service is unavailable, with graceful auth handling and coverage for the listed states.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- authentication, mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 58/100