Back to all docs

Making Web Apps Feel Native on iOS - PWA Setup

Date: 2025-11-08 Context: Setting up Ahaia Music as a Progressive Web App for iOS

What Makes a PWA Feel Native on iOS?

Progressive Web Apps can feel almost indistinguishable from native apps when properly configured. Here's what we implemented:

1. Web App Manifest (/public/manifest.json)

The manifest tells the browser how the app should behave when installed.

{
  "name": "Ahaia Music",
  "short_name": "Ahaia",
  "description": "Interactive music platform",
  "start_url": "/",
  "display": "standalone",           // ← Hides browser UI
  "background_color": "#ffffff",
  "theme_color": "#000000",
  "orientation": "portrait-primary",  // ← Lock orientation
  "icons": [...]
}

Key fields:

2. iOS-Specific Meta Tags

iOS Safari needs special meta tags that aren't in the manifest spec:

export const metadata: Metadata = {
  manifest: "/manifest.json",
  appleWebApp: {
    capable: true,                    // ← Enables standalone mode
    statusBarStyle: "black-translucent", // ← Status bar appearance
    title: "Ahaia Music",             // ← Home screen title
  },
  formatDetection: {
    telephone: false,                 // ← Disable auto phone links
  },
};

Status bar styles:

3. Viewport Configuration

Critical for mobile feel - prevents zooming, fits screen properly:

export const viewport: Viewport = {
  width: "device-width",
  initialScale: 1,
  maximumScale: 1,         // ← Prevents pinch-zoom (like native)
  userScalable: false,     // ← Disables zoom gestures
  viewportFit: "cover",    // ← Extends to notch/safe areas
};

Why these matter:

4. App Icons

iOS requires specific icon sizes:

public/
├── apple-touch-icon.png      # 180x180 (primary iOS icon)
├── icon-192.png              # 192x192 (Android, PWA)
├── icon-512.png              # 512x512 (Android, PWA)
└── icon.svg                  # Vector (fallback)

Icon requirements:

How iOS Users Install

  1. Open app in Safari (Chrome/Firefox don't support PWA install on iOS)
  2. Tap Share button (box with arrow)
  3. Scroll and tap "Add to Home Screen"
  4. App icon appears on home screen
  5. Launching opens in standalone mode (no browser UI)

What You Get

✅ Native-like behavior:

❌ What you DON'T get (yet):

Testing PWA on iOS

Option 1: Real device

# Start production server
npm run build
npm start

# Get your IP
ip addr show | grep "inet 192"

# On iPhone Safari, visit:
http://192.168.x.x:8000

Option 2: iOS Simulator (requires Xcode)

# Open simulator
open -a Simulator

# Safari → open http://localhost:8000
# Follow install steps above

What to test:

Advanced: Safe Area Insets

For devices with notches (iPhone X+), use CSS safe areas:

.header {
  padding-top: env(safe-area-inset-top);
}

.footer {
  padding-bottom: env(safe-area-inset-bottom);
}

This ensures content doesn't hide under notch or home indicator.

Performance Considerations

Why PWAs can feel sluggish:

  1. No offline caching (yet) - Every visit fetches from network
  2. No background refresh - Content can be stale
  3. Memory limits - iOS aggressively kills background tabs

Solutions:

Add Service Worker (future):

// public/sw.js
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open('v1').then((cache) => {
      return cache.addAll([
        '/',
        '/app.css',
        '/app.js',
        '/icon.png'
      ]);
    })
  );
});

This enables:

For easier PWA setup, use next-pwa:

npm install next-pwa
// next.config.ts
const withPWA = require('next-pwa')({
  dest: 'public',
  register: true,
  skipWaiting: true,
});

module.exports = withPWA({
  // your next config
});

Auto-generates:

Android vs iOS: PWA Support Differences

PWAs actually have better support on Android than iOS. Here's what Android gets that iOS doesn't:

What Android Gets (that iOS Doesn't):

1. Automatic Install Prompts

2. Push Notifications

3. Background Sync

4. Better Install Experience

5. Richer Manifest Support

6. Update Prompts

Android Installation Flow:

1. User visits PWA in Chrome
2. Chrome detects PWA criteria met:
   - Has manifest.json ✓
   - Has service worker ✓
   - Served over HTTPS ✓
3. Chrome shows banner: "Add Ahaia Music to Home Screen"
4. User taps "Add"
5. Icon appears on home screen
6. App launches fullscreen with splash screen

iOS Installation Flow:

1. User visits PWA in Safari
2. No automatic prompt (manual only)
3. User taps Share button
4. User scrolls to find "Add to Home Screen"
5. User confirms
6. Icon appears on home screen
7. App launches fullscreen

Bottom line: If your users are mostly Android, PWAs are almost indistinguishable from native. For iOS, they're still good but have more limitations.

Comparison: PWA vs Native App

Feature PWA Native iOS App
Distribution URL/QR code App Store only
Installation Safari share menu App Store download
Updates Instant (reload) App Store approval
Offline Service worker Built-in
Push notifications ❌ iOS, ✅ Android ✅ Both
Device APIs Limited Full access
Performance Good (90% native) Excellent
File size Smaller Larger
Development Web tech (HTML/CSS/JS) Swift/SwiftUI
Cross-platform One codebase Separate iOS/Android

When to Use PWA vs Native

Use PWA when:

Use Native when:

Our Implementation

For Ahaia Music, PWA is perfect because:

  1. Audio playback - Web Audio API is excellent
  2. Fast iteration - Music platform needs frequent updates
  3. No install friction - Users can try immediately
  4. Cross-platform - Works on iOS, Android, desktop
  5. Cost-effective - One codebase

We can always build native apps later if needed for:

Resources


Bottom Line: PWAs on iOS can feel 90% native with proper configuration. The key is viewport settings, standalone mode, and proper icons. Service workers add the final 10% for offline support.