Add iOS 26 Liquid Glass tab bar enhancements#1
Conversation
Adds two independent toggles to replicate Apple Music's iOS 26 tab bar behavior: - Move Search Tab to Right: Reorders viewControllers so Search is always last - Shrink Tab Bar on Scroll: Controls minimizationBehavior (0=automatic collapse, 1=always expanded) Changes: - UserDefaultConstants.h: Add UDKeySearchTabRight and UDKeyTabBarShrinkOnScroll - Tweak.xm: Extend ApolloTabBarController hook with viewDidLoad logic and two helper methods - CustomAPIViewController.m: Add two toggle rows to General section and their handler methods Both toggles default to OFF and require app restart to apply.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 44aef1ce3d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| SEL setter = NSSelectorFromString(@"setMinimizationBehavior:"); | ||
| if ([self.tabBar respondsToSelector:setter]) { | ||
| // 0 = automatic (shrink on scroll), 1 = never (always expanded) | ||
| NSInteger value = shrinkOnScroll ? 0 : 1; | ||
| [self.tabBar setValue:@(value) forKey:@"minimizationBehavior"]; |
There was a problem hiding this comment.
Apply the native minimize API to the tab controller
When the new “Shrink Tab Bar on Scroll” setting is enabled on iOS 26, this code checks self.tabBar for setMinimizationBehavior: and then sets minimizationBehavior on the UITabBar; however the existing iOS 26 implementation in src/ApolloAutoHideTabBar.xm detects setTabBarMinimizeBehavior: on UITabBarController and uses raw value 2 for on-scroll-down. In this form the selector check never reaches the real API, so the setting is a no-op for the scenario it is meant to control.
Useful? React with 👍 / 👎.
| - (void)dealloc { | ||
| %orig; | ||
| [[NSUserDefaults standardUserDefaults] removeObserver:self forKeyPath:UDKeyApolloPostCommentsSnapshots]; |
There was a problem hiding this comment.
Unregister KVO before calling original dealloc
If an ApolloTabBarController instance is deallocated after this observer was added, calling %orig first lets the original dealloc tear down/super-deallocate the object before it is passed back to removeObserver:forKeyPath:. KVO cleanup should happen before %orig; otherwise teardown can use a deallocated observer or leave the registration active, causing a crash during controller destruction.
Useful? React with 👍 / 👎.
…pollo-Reborn#442) * Add Web JSON spike: dormant OAuth-free escape hatch (flag-gated) Spike proving Apollo can browse via cookie-authenticated www.reddit.com/...json instead of oauth.reddit.com + bearer tokens, as a recovery path for Reddit API-key revocation. Dormant by default behind the "Web JSON Mode" flag; flag-off behavior is unchanged. - ApolloWebJSON.{h,m}: whitelist-gated request rewrite (subreddit/front listings, GET only) spliced into the __NSCFLocalSessionTask chokepoint in Tweak.xm — strips Authorization, attaches harvested session cookie. - ApolloWebSessionLoginViewController.{h,m}: WKWebView login that detects auth state via an /api/me.json probe (not cookie presence) and prompts Keep/Re-authenticate/Cancel on an existing session. - Settings: "Web JSON Mode" switch + "Web Session Login" row. - Flag + cookie-header globals (spike-grade NSUserDefaults storage). Transport verified in the iOS 26 simulator (cookie-authed feed renders with zero response transformation). Identity integration for keyless cold start is deferred — see docs/web-json-spike-findings.md. * Web JSON: full OAuth-free migration (reads, writes, identity, lifecycle) Builds out the deferred work from docs/web-json-spike-findings.md so the cookie-session escape hatch is a usable read+write client that works without Reddit API keys. Flag-gated and dormant by default (Web JSON Mode off -> byte-for-byte stock behavior). Item 1 - Full read coverage: ApolloWebJSONClassifyReadPath routes listings, comments, user pages, search, subscriptions, inbox, prefs, about/wiki, and every /api/* GET (no .json suffix for those) to www.reddit.com with cookie auth; unrecognized paths fall through to oauth untouched. Item 2 - Writes via modhash: routable /api/* POST/PUT/DELETE re-point to www.reddit.com with the session cookie + X-Modhash (harvested from /api/me.json at login). Token/media-upload endpoints excluded. Item 3 - Identity (ApolloWebJSONIdentity.xm): RDKClient reports authenticated and gets a synthetic credential; the token mint/refresh methods are short-circuited to an instant success (completion confirmed in Hopper to be void(^)(id, NSError*)) so a keyless cold start doesn't stall on the failing access_token POST. For the no-account case, a signed-in account is synthesized from the cookie identity by writing the on-disk blobs AccountManager's loader reads (RedditAccounts2 [RDKClient], 2RedditAccounts2 Valet [[String:String]], CurrentRedditAccountIndex) - the exact gate the vote/comment UI and account tab check. Runs at login harvest (restart prompt) and in %ctor before AccountManager loads. Verified end-to-end in the iOS 26 simulator: account tab shows the user, personalized reads load, upvote/downvote route to /api/vote with cookie+modhash and register (no "Sign In to Upvote"). Item 4 - Lifecycle + keychain: cookie/modhash/username moved to the keychain (migrated out of NSUserDefaults). A 403 text/html block page on a cookie-authed request posts ApolloWebJSONSessionExpiredNotification -> one-shot "sign in again" prompt. Remaining: on-device validation of the real keychain/Valet account write and live write round-trips (sim uses Tweak.xm's virtualized Valet). * Apply suggestions from code review Co-authored-by: JeffreyCA <jeffreyca16@gmail.com> * Web JSON: address review feedback - Session-expiry detection: replace the latch-on-first-block-page heuristic with a consecutive-streak counter (threshold 3). Any non-block response on a cookie-authed request resets the streak, so a transient Cloudflare / rate-limit / captcha 403 HTML page no longer fires a spurious "session expired" prompt; only a sustained run of block pages declares the cookie dead. Counter also resets on a fresh harvest. - Log unclassified reads that fall through to oauth, so a stray 401 in the keyless case (synthetic dummy bearer) is traceable to an unrouted path. - Centralize the API Keys row-index remapping into ApolloAPIKeyCanonicalRow() + an ApolloAPIKeyRow enum (mirrors the existing ApolloMediaLogicalRow pattern), replacing the four inlined copies across the cell builder, didSelect, shouldHighlight, viewWillAppear, the toggle, and the row count. - Refresh the stale "spike-grade storage" note: the cookie now lives in the keychain; the NSUserDefaults key is retained only for one-time migration. * Web JSON: fix profile listing, comment edit/post, and upload fallback Address PR Apollo-Reborn#442 tester feedback for keyless Web JSON mode. Profile posts/comments empty (Apollo-Reborn#3): the account loaded with a nil currentUser.username (NSKeyedArchiver omits nil values, so the RDKMe was archived with no username key), so /user/<name>/overview could never fire and the profile tab just spun. Backfill the username onto the live currentUser from the harvested session identity at -[RDKClient setCurrentUser:] (the on-disk blob can't be repaired -- RDKMe's MTLModel encoding drops a re-set username), with a belt-and-suspenders backfill in the synthetic-credential install. Comment editing (Apollo-Reborn#4): the Edit affordance returns once the username is non-nil (same root cause as Apollo-Reborn#3). Also fix the empty re-render after an edit/new comment: www.reddit.com's /api/editusertext and /api/comment return each thing in the legacy old-reddit {parent, content:"<html>"} shape instead of the modern comment JSON, so Apollo rendered an empty comment with 0 upvotes. ApolloWebJSONFixupWriteResponseObject (hooked at RDKResponseSerializer) detects the legacy shape, pulls the fullname from the content HTML, re-fetches the modern object via info.json, and swaps it into the things envelope. Image uploads (Apollo-Reborn#5): native keyless Reddit upload isn't viable -- a spike confirmed www.reddit.com/api/media/asset.json returns Reddit's 403 block page for cookie+modhash auth (every modhash placement), it needs a real OAuth bearer. Keep the media lease on the oauth path always (routing it to www would also break uploads for a real-account user with Web JSON on), and in a keyless session short-circuit the upload hooks before the doomed lease to fall back to Apollo's Imgur path, with accurate guidance when no Imgur key is set (the old copy wrongly suggested switching the provider to Reddit to upload without keys). Inert for non-Web-JSON users. Also includes the earlier fixes: the spurious session-expired prompt (#1, verify via /api/me.json probe before announcing) and the post-login restart UX polish (Apollo-Reborn#2). --------- Co-authored-by: JeffreyCA <jeffreyca16@gmail.com>
Introduce configurable toggles to replicate Apple Music's iOS 26 tab bar behavior, allowing users to move the Search tab to the right and control the tab bar's minimization on scroll. Both features default to OFF and require a restart to apply.