How to run a PWA on iOS device in local environment
I’m building a PWA that runs inside a native iOS wrapper, and I wanted to test my locally running Nuxt frontend with my Django backend on my iPhone.
I recently discovered that it's not that easy to get that running in a local environment. I'm writing this post to save you some headaches and to remind myself of how to do it for the future.
The problem is that you can't just use your localhost in the iOS app, since the app is running on a different device that can't communicate with your local server. Plus, things like CORS, CSRF protection, and App Transport Security (ATS) make it even trickier.
I first tried to solve this by using ngrok to expose my local server to the internet, but that didn't work for me. After a bunch of trial and error, I finally got it working, so I’m sharing the solution here.
The Solution: Use Your Local Network IP
Since localhost
only works on your Mac, the trick is to make your Mac's local server accessible over your Wi-Fi network. Your iPhone and your development machine need to communicate using a real IP address that both can access.
Step 1: Find Your Local IP
On macOS, open a terminal and run:
ipconfig getifaddr en0
This will return something like this:
192.168.1.100
This is your Mac’s IP address on your Wi-Fi network. You'll use this instead of localhost. Make sure to replace the IP address in the following steps with your actual IP address.
Step 2: Run Nuxt & Django to Accept External Connections
By default, both Nuxt and Django bind to localhost
, meaning they won’t accept connections from other devices. You need to explicitly allow external access.
Run Nuxt with:
npm run dev -- --host 0.0.0.0
This makes your frontend accessible at:
http://192.168.1.100:3000
Run Django with:
python manage.py runserver 0.0.0.0:8000
This makes your backend accessible at:
http://192.168.1.100:8000
Step 3: Update API URLs in Your Frontend
Since your frontend is making requests to the backend, update the .env file or API config to use your local IP while you are testing on your iPhone:
API_URL='http://192.168.1.100:8000/api/'
BACKEND_URL='http://192.168.1.100:8000'
If you’re caching URLs in a service worker, whitelist them there too:
const allowedDomains = ['http://192.168.1.100', 'http://192.168.1.100:3000']
Step 4: Fix CORS and CSRF Issues in your backend
Depending on what you are using for your backend, you might need to add your local IP to the allowed origins in your backend settings.
For example, if you are using Django like me, you can add the following to your settings.py
file (scope this to your development environment):
CORS_ORIGIN_WHITELIST = [
"http://192.168.1.100:3000",
"http://192.168.1.100:8000"
]
CSRF_TRUSTED_ORIGINS = [
"http://192.168.1.100:3000",
"http://192.168.1.100:8000"
]
Step 5: Update the iOS App Configuration
Since my Nuxt app is running inside an iOS wrapper, I also had to update my Swift settings to allow the local IP.
Modify Settings.swift
in the iOS Wrapper
// This is the URL where your PWA is running
let rootUrl = URL(string: "http://192.168.1.100:3000")!
// This tells the iOS app to allow requests from your local IP
let allowedOrigins: [String] = ["192.168.1.100"]
Make sure that you scope this to your development environment and don't accidentally push it to production.
Update Info.plist for iOS Security
iOS blocks HTTP connections unless explicitly allowed. To enable communication with your local backend, modify Info.plist
:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>WKAppBoundDomains</key>
<array>
<string>192.168.1.100</string>
</array>
Step 6: Test on Your iPhone
- Connect your iPhone to the same Wi-Fi network as your Mac.
- Connect your iPhone to your Mac via USB and trust the computer.
- Open Xcode and select your device from the sidebar.
- Build and run the app.
If everything is set up correctly, it should just work! 🎉
I hope this helps you get your Nuxt PWA running on your iPhone! If you have any questions, feel free to reach out to me at pk@pkundr.com.
Below, I've listed some common problems and solutions that you might encounter.
Troubleshooting
If you encounter issues, here are some common problems and solutions:
Why can’t my iOS app connect to my backend?
Check the following:
- Is your backend running with 0.0.0.0 and accessible via your local IP?
- Are your iPhone and Mac on the same Wi-Fi network?
- Can you open http://192.168.1.66:8000 in Safari on your iPhone?
- Have you updated CORS and CSRF settings in Django?
- Did you allow NSAllowsArbitraryLoads in Info.plist?
I updated my Swift code, but the app still runs the old version. Why?
Xcode caches builds, so try:
- Stop the app, then rebuild (⌘ + R).
- Manually delete the app from your iPhone and reinstall it.
- If issues persist, clean the Xcode build (⌘ + Shift + K) and restart Xcode.
I see a “403 Forbidden” error when calling my API. What’s wrong?
Your Django CSRF settings might be blocking requests. Try:
- Add your local IP to CSRF_TRUSTED_ORIGINS in settings.py.
- If you’re testing without authentication, set:
CSRF_COOKIE_SECURE = False
(Just don’t do this in production!)
My service worker is caching the wrong API URL. How do I fix it?
If you switched from a production backend to a local backend, your service worker might be caching old data. Try:
navigator.serviceWorker
.getRegistrations()
.then((regs) => regs.forEach((reg) => reg.unregister()))
This will unregister all service workers and allow you to start fresh.
How do I debug network requests on iOS?
When using a physical device, you first need to enable the Web Inspector in the iOS settings for your Safari browser.
- Open Safari on your iPhone.
- Go to Settings > Safari > Advanced.
- Enable Web Inspector.
Then, while your app is running:
- Open Safari on your Mac.
- Enable Develop Mode: Safari > Preferences > Advanced > Show Develop menu.
- Plug in your iPhone and open the app.
- In Safari, go to Develop > Your Device > WebView Instance.
- Check Console & Network for errors.