Skip to content

Improve iOS app detection resistance#155

Open
Quaii wants to merge 1 commit intomainfrom
cursor/improve-ios-app-detection-resistance-54a6
Open

Improve iOS app detection resistance#155
Quaii wants to merge 1 commit intomainfrom
cursor/improve-ios-app-detection-resistance-54a6

Conversation

@Quaii
Copy link
Owner

@Quaii Quaii commented Nov 20, 2025

Improve iOS app's undetectability by aligning its network request patterns and error handling with the desktop app.

The iOS app was experiencing significantly higher rates of 403 errors and rate limits compared to the desktop app. This PR implements several changes to mimic the desktop app's network behavior, including more frequent and randomized proxy rotation, human-like request timing, consistent header ordering, and robust session/cookie management upon encountering authentication or rate-limiting errors, thereby reducing detection.


Open in Cursor Open in Web

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced error handling with improved session recovery mechanisms for failed requests
    • Optimized proxy rotation and request timing to increase reliability and reduce connection failures
    • Strengthened cookie management to ensure consistent session state across requests

✏️ Tip: You can customize this high-level summary in your review settings.

Co-authored-by: luis.dee16 <luis.dee16@gmail.com>
@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Walkthrough

The VintedAPI service's anti-detection mechanisms have been refactored: proxy rotation shifted from round-robin indexing to probabilistic random selection with scheduled rotation every 3-5 requests; request timing now includes asymmetric jitter with an 80% delay probability; error handling (401/404/429/403) now triggers immediate proxy rotation and explicit cookie refresh; and session management aligned with desktop behavior.

Changes

Cohort / File(s) Summary
Proxy Selection & Rotation Logic
iOSApp/VintedNotifications/Services/VintedAPI.swift
Replaced currentProxyIndex with requestCount and nextProxyRotationAt for probabilistic rotation scheduling. Changed getRandomProxy() to select randomly instead of cycling. Added shouldRotateProxy() for 3-5 request intervals. Integrated proxy rotation into main request flow with post-rotation delay.
Request Timing & Jitter
iOSApp/VintedNotifications/Services/VintedAPI.swift
Narrowed min/max delay ranges; added probabilistic delays with asymmetric jitter and 80% probability to delay; delay now computed from time since last request rather than fixed intervals.
Error Handling & Session Management
iOSApp/VintedNotifications/Services/VintedAPI.swift
Enhanced 401/404/429 handling with explicit setCookies() calls and immediate proxy rotation. Added 403 error path with session reset/recreation and proxy rotation. setCookies() now clears cookies before refreshing. rotateProxySession() logs new proxy string.
Header & Cookie Behavior
iOSApp/VintedNotifications/Services/VintedAPI.swift
getRandomizedHeaders() now only randomizes Accept-Language if not pre-set in default headers. Cookie clearing behavior aligned with desktop implementation.

Sequence Diagram

sequenceDiagram
    participant Client
    participant VintedAPI
    participant ProxySelector
    participant Session
    
    Client->>VintedAPI: request()
    VintedAPI->>VintedAPI: increment requestCount
    VintedAPI->>VintedAPI: shouldRotateProxy()?
    
    alt Rotation Needed
        rect rgb(200, 220, 255)
            VintedAPI->>ProxySelector: getRandomProxy()
            ProxySelector-->>VintedAPI: new proxy
            VintedAPI->>VintedAPI: rotateProxySession()
            VintedAPI->>Session: create with new proxy
            VintedAPI->>VintedAPI: apply post-rotation delay
            VintedAPI->>VintedAPI: reset nextProxyRotationAt
        end
    end
    
    VintedAPI->>VintedAPI: computeDelay (time-based)
    VintedAPI->>VintedAPI: 80% chance: add asymmetric jitter
    VintedAPI->>VintedAPI: apply delay
    VintedAPI->>Session: executeRequest()
    Session-->>VintedAPI: response
    
    alt Error (401/404/429)
        rect rgb(255, 200, 200)
            VintedAPI->>VintedAPI: setCookies() [clear + refresh]
            VintedAPI->>ProxySelector: getRandomProxy()
            VintedAPI->>VintedAPI: rotateProxySession()
            VintedAPI->>VintedAPI: retry with new proxy
        end
    end
    
    alt Error (403)
        rect rgb(255, 220, 180)
            VintedAPI->>Session: reset/recreate session
            VintedAPI->>VintedAPI: setCookies()
            VintedAPI->>VintedAPI: rotateProxySession()
        end
    end
    
    VintedAPI-->>Client: result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Probabilistic rotation logic: The shouldRotateProxy() integration and random interval generation (3-5 requests) requires careful verification of distribution correctness and edge cases around request counting and rotation reset.
  • Timing and delay calculations: Asymmetric jitter implementation with 80% probability and time-delta-based delay computation need validation for correctness and consistency with anti-detection intent.
  • Error handling paths: Multiple interconnected error handlers (401/404/429/403) each triggering different cookie/session/proxy behaviors—verify no race conditions or unintended state inconsistencies across retry flows.
  • Session and proxy state management: Ensure rotateProxySession(), setCookies(), and proxy selection maintain consistent state; verify logging accuracy matches new proxy semantics.

Possibly related PRs

Poem

🐰 Round-robin hops to random bounds,
Jitter dances, delays abound!
Every few requests, we leap and sway,
Fresh proxies keep the bots at bay. 🎲✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Improve iOS app detection resistance' directly aligns with the main objective of the PR, which is to reduce detection-triggered errors (403, rate limits) by making the iOS app's network behavior less detectable.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch cursor/improve-ios-app-detection-resistance-54a6

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Quaii Quaii marked this pull request as ready for review November 20, 2025 06:26
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
iOSApp/VintedNotifications/Services/VintedAPI.swift (4)

39-42: Request timing / jitter behavior looks coherent, with a small clarity tweak possible

The min/max delay plus asymmetric jitter and 80% delay probability work together as intended: first request after app launch effectively has no delay (due to lastRequestTime == 0), then subsequent calls are spaced with human‑like gaps using timeSinceLastRequest. Logic is sound.

If you want this to be more explicit/less sentinel‑based, you could initialize lastRequestTime = Date().timeIntervalSince1970 in init() and adjust the comment on minRequestDelay to clarify the “first request is usually immediate” behavior. Optional only.

Also applies to: 431-451


296-304: Proxy rotation schedule is reasonable; consider thread‑safety and rotation bookkeeping

The new rotation scheme (global requestCount, nextProxyRotationAt, random working proxy, rotation every 3–5 requests and immediately at some error paths) matches the PR goal and is much closer to “desktop‑like” behavior. The code paths all terminate correctly and won’t over‑rotate when workingProxies is empty.

Two improvements to consider:

  1. Thread‑safety of counters and session
    If search can be called concurrently from multiple tasks/threads, requestCount, nextProxyRotationAt, workingProxies, and session mutations are unsynchronized. A lightweight fix would be to isolate VintedAPI on a single actor/queue (e.g. mark the class @MainActor if it’s only used from UI, or refactor into an actor), so rotations and counts are serialized.

  2. Avoid “rotations” that keep the same proxy
    rotateProxySession() may pick the same proxy via randomElement(). If you want rotations to always switch IP when more than one proxy is available, you could remember the last proxy string and resample when there is more than one proxy and newProxy == lastProxy.

Both are refinements; current behavior is functionally correct.

Also applies to: 319-325, 328-344, 556-567


403-411: Cookie clearing + refresh behavior is correct but could better sync with lastCookieRefresh

Clearing all cookies before setCookies() and reusing that in 401/404/403 handlers is consistent with the stated “match desktop” intent and should help recover from bad/expired cookie state.

Two minor follow‑ups to consider:

  1. Align lastCookieRefresh with manual refreshes
    Calls to setCookies() in the 401/404 and 403 branches don’t update lastCookieRefresh, so ensureFreshCookies() will still think the last refresh time is the older value. That’s harmless but can cause extra HEAD refreshes earlier than needed. Updating lastCookieRefresh inside setCookies() (on success) would keep the bookkeeping accurate.

  2. Scope of cookie clearing
    Because createSession uses HTTPCookieStorage.shared, setCookies() currently wipes all cookies, not just Vinted‑related ones. If the app ever talks to other domains, you may want to filter deletes to cookies whose domain matches currentLocale/vinted. instead of clearing the entire store.

Both are optimization/behavioral nits rather than blockers.

Also applies to: 608-615, 643-652


621-631: 429/403 handling logic matches the goal; consider slightly stronger backoff on repeated 403s

  • 429: Backoff with jitter plus an immediate proxy rotation when proxies are available is a solid strategy and should noticeably reduce rate‑limit loops.
  • 403: On first 403 per search call, you reset tried to 0, refresh cookies, rotate (or recreate) the session, and back off before retrying, which aligns with the “reset session + new proxy” desktop behavior.

One refinement to think about: subsequent 403s after newSession is set only log and immediately proceed to the next loop iteration, so you’ll send up to maxRetries−1 extra 403s with no extra delay or rotation. You might want to treat “second 403 after reset” as a strong signal and either:

  • bail out early (return empty), or
  • reuse the generic backoff behavior (similar to default) and possibly rotate again.

Not a correctness bug, but could further reduce detection pressure on hard‑blocked sessions.

Also applies to: 637-656

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 47a5fa7 and 4ceb93b.

📒 Files selected for processing (1)
  • iOSApp/VintedNotifications/Services/VintedAPI.swift (9 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
iOSApp/VintedNotifications/Services/VintedAPI.swift (1)
iOSApp/VintedNotifications/Services/LogService.swift (2)
  • info (80-82)
  • warning (84-86)
🔇 Additional comments (2)
iOSApp/VintedNotifications/Services/VintedAPI.swift (2)

377-382: Accept‑Language override guards are sensible

Only randomizing Accept-Language when it’s missing from defaultHeadersList is a good call; this lets persisted/desktop‑aligned headers win, while still giving you variability by default.

No issues here from a correctness standpoint.


686-687: Good call adding human‑like delay to getUserCountry

Reusing addRequestDelay() here keeps user‑info lookups aligned with the rest of your traffic pattern, which should help keep the profile consistent with desktop behavior.

No issues spotted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants