# How we reproduced an iOS Firefox only bug without an iPhone 

Oh, the glamour of frontend engineering chasing pixels across screens while battling invisible gremlins

### BackStory

> your app works flawlessly on desktop Chrome, kinda sorta on Android, but explodes into a caching catastrophe on iOS Safari. Sound familiar? That's exactly what bit me during a recent debugging marathon. We were simulating iOS environments, only to unearth ancient legacy logic that treated API responses like a `choose your own adventure` book based on...

Spoiler: it introduced sneaky inconsistencies that made our "responsive" design look like a bad magic trick. Buckle up, because this post dives into how user agent-based debugging saved the day, and why it's a double-edged sword

## **What is a user agent?**

The user agent, often shortened to UA, is an HTTP request header that says what browser, engine, OS, and sometimes device is making the request. Example:

`Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1`

Browsers also expose this string client side as `navigator.userAgent`. Newer APIs expose structured data via `navigator.userAgentData` where supported.

Why you should treat user agent data with suspicion

*   It is easy to spoof.
    
*   Browser vendors change the format for privacy.
    
*   It contains lots of historical cruft, making regex detection fragile.
    
*   Relying on it for core logic creates brittle branches that are hard to test and maintain.
    

## Simulating the Beast: DevTools to the Rescue

We went to finding solution on how we could debug this on a specific browser on a device, tried BrowserStack ,but it didn't help because we were looking for firefox on ios device  
Can't afford a device farm? No problem. Modern dev tools let you override UAs like a pro, exposing those hidden branches without touching hardware.

Here's a dead simple way to spoof an iPhone UA in Chrome DevTools runnable right now:

```javascript
// Open DevTools > Network > Network conditions > Custom user agent
// Paste this iPhone 14 Pro Max UA (real-world example)
const iOSUA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1';

// Test it in console:
console.log(navigator.userAgent.includes('iPhone')); // true after override
```

**Step by step breakdown**:

1.  Fire up DevTools (F12), hit Network tab.
    
2.  Click "More network conditions" (or Cmd+Shift+P > "Network conditions").
    
3.  Uncheck "Use browser default," paste the UA, done.
    

We did this in a Next.js app that simulates an iOS WebView. Suddenly, API calls skipped the cache on "mobile," but fetched stale data on desktop.

### How do we get this string

A buried `isMobile()` function sniffing UA for iOS/Android, then helping us fix it:

```javascript
// DON'T DO THIS Classic legacy trash
function isMobile(ua = navigator.userAgent) {
  return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
}

// Detect Firefox on iPhone
function isFirefoxOniPhone(ua = navigator.userAgent) {
  return /iPhone/i.test(ua) && /FxiOS/i.test(ua);
}

// simple api call we want on iphone to help us get some data 
function fetchData(url) {
  const ua = navigator.userAgent;

  // Special case: Firefox on iPhone
  if (isFirefoxOniPhone(ua)) {
    console.log("Firefox on iPhone detected");
    return fetch(url).then(res => res.json());
  }
}

console.log(fetchData(www.example.com))
```

## Why User Agent Spoofing Helped Us Debug the Logic

In our case, the issue wasn’t really about caching or Safari itself, it was about **code paths that depended on the browser environment**.

Modern applications often contain conditional logic like this:

```plaintext
if (isMobile()) {
  bypassCache();
}
```

The problem is that these branches only execute **under specific environments**. If you’re testing on desktop Chrome, that part of the code may never run.

By overriding the **User Agent string**, we essentially tricked the application into believing it was running on a different device, in this case an iPhone browser.

Once the browser reported itself as iOS, our application started executing the **mobile-specific code path**, which immediately exposed the hidden logic responsible for the inconsistent behavior.

In other words, UA spoofing allowed us to:

*   **Reproduce a device-specific environment**
    
*   **Trigger code paths that normally wouldn’t run on our machine**
    
*   **Observe how the application behaves under those conditions**
    

Without it, we would have needed a physical device or a remote testing environment to reproduce the issue.

* * *

## The Real Lesson

User agent sniffing is often considered a fragile practice for production logic, but it can still be a **powerful debugging technique**.

It allows developers to simulate:

*   different devices
    
*   different browsers
    
*   different environments
    

and verify how their application responds.

In our case, the browser wasn’t the real problem; **The assumptions inside our code were**.
