Disclaimer: This is not the official Gemsloot app. The official Android product is a Capacitor hybrid that ships a build-time
is_android: trueweb bundle. This project is an independent native iOS client engineered to solve attribution and reward tracking failures at the native runtime level.
Left: ATT Status, Copyable IDs & Bridge Lock Modes Β Β |Β Β Right: Live /_api/ff Payload Telemetry, Redirect Hops & Cache Controls
Standard mobile browsers on iOS (Safari, Chrome via WebKit) break reward offer attribution due to cross-site cookie blocking, dynamic anti-fingerprinting noise injection, and aggressive redirect-chain stripping.
Gemsloot iOS resolves those failure vectors at the native runtime level:
- Full-Screen Native Shell: Loads
https://gemsloot.cominWKWebsiteDataStore.default()with a#101012background, native pull-to-refresh, and back/forward swipe gestures. - Fixed Safari Mobile UA: Forces offerwalls to serve iOS App Store inventory instead of Google Play links.
- Injected
fetch/XHRBridge: Intercepts/_api/ffrequests to lock device fingerprint schemas and optionally injectDeviceheaders. - True Popup Lifecycle: Preserves
window.openeracross child WebViews so OAuth and offerwall tracking sheets never break or strand the main view. - Native App Store Handoff: Intercepts
apps.apple.com,itunes.apple.com, anditms-apps://schemes to launch the system App Store cleanly. - Live Diagnostics HUD: Provides real-time visibility into tracking IDs, bridge modes, payload bodies, and redirect hops.
- Not a Capacitor port: Does not reimplement Gemsloot's Android Capacitor plugins (
Playtime/ Adjoe, Install Referrer, push notifications, etc.). Those remain exclusive to Android'sIl.is_androidpath. - Not a general-purpose multi-tab browser: Secondary windows are strictly managed tracker and OAuth sheets.
- Not signed for App Store distribution: CI outputs an unsigned
Gemsloot-iOS.ipaon purpose (no Apple signing certificates stored in GitHub Actions). Sign and sideload it with your own provisioning setup.
The official Android APK is a Capacitor wrapper where feature branching is controlled via a build-time Il.is_android flag rather than runtime Capacitor.isNativePlatform() checks (which have zero call sites in the web bundle).
Relevant /_api/ff payload architectures:
- Branch A (Android Capacitor):
hash:Device.getId().identifier(Settings.Secure.ANDROID_ID, ~16 hex chars)data: FullDevice.getInfo()payloadformated_data:{ browser, browser_ver, os, timezone, language }- Header:
Device: android
- Branch B (Browser / Live Web Bundle):
hash:MurmurHash3_x86_32(JSON.stringify(fingerprint), 0).toString(36)data: Browser fingerprint collectorsformated_data: Expanded browser metadata object
Loading the live site inside a standard WKWebView defaults to Branch B, where dynamic WebKit anti-fingerprinting noise mutates the hash across sessions. WebViewBridge intercepts and locks this payload so your device identity remains rock-solid. (See Phase 1 recon and step-1-ff-payload docs for full payload traces).
A single master UUID seed generated once and persisted in the iOS Keychain drives three deterministic identifiers:
keychainUUID(UUID v4 string) β Master seed stored in Keychain.deterministicBase36Hash(Base-36 uint32) βMurmurHash3_x86_32(seed)matching the exact output shape of Branch B'sZB()hash function.deterministicHex16(16-char lowercase hex) β First 8 bytes ofSHA-256(seed UTF-8), matchingANDROID_IDlength.- Hardware IDFA β Sourced from
ASIdentifierManageronce ATT is authorized (returns zeros/unavailable until granted).
Configured via AppState.ffLockMode (toggleable in the HUD). The injected document-start script patches both window.fetch and XMLHttpRequest for gemsloot.com/_api, ensuring credentials: 'include' is always enforced.
branchB_Locked(Default): Retains the Branch B schema. On the first POST to/_api/ff, replaceshashwithdeterministicBase36Hash, persists the exactJSON.stringify({ hash, data, formated_data })snapshot, and replays that exact string on every subsequent request.branchA_Capacitor: Replaces the POST body with a Capacitor-shaped payload usingdeterministicHex16as thehash.passthrough: Leaves/_api/ffbodies untouched.
Note: Optional
Device: iosorDevice: androidheaders can be toggled viadeviceHeaderMode(defaults tooff). Tapping Reset Snapshot in the HUD clears the persisted Mode 1 JSON so a fresh fingerprint can be captured.
To prevent broken OAuth logins and dead tracking links, webView(_:createWebViewWith:for:windowFeatures:) executes a strict three-step handoff:
- Instantiates
TrackerRedirectViewControllerusing the exactWKWebViewConfigurationpassed in by WebKit. - Presents the controller as a modal sheet.
- Returns that child controller's
webViewinstance back to WebKit.
(Returning nil or forcing navigation into the main WebView severs window.opener.postMessage("oauth_done") and strands the main view on blank tracking URLs).
- Redirect Telemetry: Logs every hop into
redirectLogs(capped at 50 entries). - MMP IDFA Injection: On known Mobile Measurement Partner (MMP) and offerwall hosts, automatically populates empty or all-zero
idfa,ios_idfa,advertising_id, andgaidquery parameters when a valid hardware IDFA is available. - App Store Handoff: Intercepts
apps.apple.com,itunes.apple.com, anditms-apps(s)://URLs, cancels the in-app load, triggersUIApplication.shared.open, and auto-dismisses the popup sheet after 0.6s. - OAuth Auto-Close: Implements
webViewDidCloseto cleanly dismiss the sheet when an OAuth provider fireswindow.close(). - External Schemes: Main WebView routes App Store links,
mailto:,tg://, anddiscord://directly to their native system handlers.
Trigger the HUD anytime via a 3-finger tap or device shake.
- ATT & Identity: View current ATT authorization status (with a manual Request ATT Permission trigger if
.notDetermined) and one-tap copy for all deterministic IDs and hardware IDFA. - Bridge Controls: Live pickers for
deviceHeaderModeandffLockMode. Tapping Apply & Reload rebuilds the document-startWKUserScriptfromAppState.bridgeConfigand performs a hard reload ofhttps://gemsloot.com. - Payload Inspector: Displays the status code and full JSON body of the last intercepted
/_api/ffrequest posted viagemslootBridge. - Redirect Log: Inspect the chronological chain of offerwall/MMP hops.
- Selective Cache Purge: Clears third-party
WKWebsiteDataStorerecords while preserving active session data forgemsloot.comandgoogle.com.
project.yml # XcodeGen spec β single source of truth for the project
scripts/build_ipa.sh # Builds unsigned Release Gemsloot-iOS.ipa
.github/workflows/ # CI workflow (build-ipa.yml)
Gemsloot/
βββ AppDelegate.swift
βββ SceneDelegate.swift
βββ Info.plist
βββ Assets.xcassets/
β βββ AppIcon.appiconset/
βββ Helpers/ # KeychainHelper, MurmurHash3, AppState
βββ Bridge/ # WebViewBridge.swift
βββ WebView/ # Main VC, TrackerRedirect, IDFAURLRewriter
βββ Diagnostics/ # TrackingDiagnosticsSheet.swift
*.xcodeproj is generated dynamically by XcodeGen and is gitignoredβdo not hand-edit it.
Pushing to main, opening a PR, or triggering workflow_dispatch runs the build pipeline on macos-15, installs XcodeGen, executes ./scripts/build_ipa.sh, and uploads the Gemsloot-iOS-unsigned-ipa artifact containing Gemsloot-iOS.ipa.
brew install xcodegen # First-time setup
./scripts/build_ipa.sh
# Output -> build/Gemsloot-iOS.ipaCode signing is explicitly disabled in the build script (CODE_SIGNING_ALLOWED=NO, empty CODE_SIGN_IDENTITY). Sign and install Gemsloot-iOS.ipa using your preferred on-device signer or sideloading tool (KSign, Feather, ESign, LiveContainer, AltStore, SideStore, TrollStore, or Xcode).
xcodegen generate
open Gemsloot.xcodeprojNSUserTrackingUsageDescription: Required for ATT prompt (+1s delay afterviewDidAppear).NSLocationWhenInUseUsageDescription: SupportsWhenInUseWebView geolocation requests.ITSAppUsesNonExemptEncryption: Set tofalse.- No
WKAppBoundDomains: Intentionally omitted so third-party offerwall iframes and cross-domain trackers are never blocked by WebKit domain locks. - Target Specs: iOS 16.0+ deployment target, Swift 5.9, Portrait orientation locked.
Unofficial independent client. Not affiliated with, endorsed by, or sponsored by Gemsloot. Use at your own risk and ensure compliance with Gemsloot's terms of service and applicable offerwall guidelines.






