iOS 6 Web Simulator

Developer help · HTML Apps SDK

Build a little piece of 2012.

Start with one HTML file. Use the same shared controls and checked services as the simulator, then let people install your app from a repository you host.

First, try a working app

This is an independent web-app platform inside the simulator—not Apple’s SDK, a native jailbreak, or Debian/APT. Real Cydia sources and .deb packages are not compatible.

  1. Open the simulator, unlock it and launch Cydia.
  2. Choose Manage → Sources → iOS 6 SDK Demo → Hello World.
  3. Tap Install, review its storage and notifications grants, then Confirm. Open Hello World from the completion screen or Home.
  4. Try an alert, save/read a greeting, or change the navigation bar. Restore bars brings hidden chrome back. Notifications stay inside the simulator.

Download Hello World HTML · View the working demo manifest · Download SDK Lab HTML

The optional SDK Lab in that same source explores the larger API and shared UI. Its grants are deliberately broad; review them before installing. Your own app should request only what it uses.

Jump to your first app, a custom repository, SDK recipes, or security and troubleshooting.

1. Create your first HTML app

Save the following exact document as hello.html, or download the starter HTML. It mounts real shared AppScreen, ScrollView, Group and Button controls and opens a host-rendered alert. No optional permission is needed.

The host injects window.iOS6 and its styles before your markup. Do not add a remote SDK, React or Swiper script tag. await iOS6.ready establishes the authenticated connection; opening the HTML directly in a browser does not grant simulator access.

View the complete, copyable starter
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Starter Hello</title>
  <style>
    html, body, #app { height: 100%; margin: 0; }
    #launch-note { padding: 16px; font: 16px sans-serif; }
    .starter-scroll .ios-scroll-content { padding: 16px; box-sizing: border-box; }
    .starter-copy { display: block; padding: 14px; margin: 0; line-height: 1.5; }
    .starter-action { margin: 0 14px; }
  </style>
</head>
<body>
  <p id="launch-note">Install this HTML app through Cydia in the simulator. The host supplies its SDK.</p>
  <div id="app"></div>
  <script>
    (async () => {
      const sdk = window.iOS6;
      const note = document.getElementById('launch-note');
      if (!sdk) return;
      try {
        await sdk.ready;
        // AppScreen draws shared navigation; hide the extra host bar.
        await sdk.navigation.set({ hidden: true });
        let result = 'Ready. No optional permissions requested.';
        let view;
        const report = (error) => {
          result = (error.code || 'ERROR') + ': ' + error.message;
          view.update(screen());
        };
        const screen = () => ({
          type: 'AppScreen',
          props: { title: 'Starter Hello', back: 'Home', onBack: () => sdk.app.close().catch(report) },
          children: {
            type: 'ScrollView', props: { label: 'Starter content', className: 'starter-scroll' }, children: {
              type: 'Group', props: {}, children: [
                { type: 'Text', props: { as: 'p', className: 'starter-copy', text: 'Your first app uses the same shared controls as the simulator.' } },
                { type: 'Button', props: { label: 'Say hello', className: 'nav-button starter-action', onClick: async () => {
                  try {
                    await sdk.dialog.alert({ title: 'Hello from your app', message: 'This is a host-rendered SDK alert.' });
                    result = 'Alert dismissed. Your app is connected.';
                    view.update(screen());
                  } catch (error) { report(error); }
                } } },
                { type: 'Text', props: { as: 'output', className: 'starter-copy', text: result } }
              ]
            }
          }
        });
        view = sdk.ui.mount(document.getElementById('app'), screen());
        note.remove();
      } catch (error) {
        note.textContent = (error.code || 'ERROR') + ': ' + error.message;
      }
    })();
  </script>
</body>
</html>

AppScreen draws its own shared navigation, so the example hides the extra host bar. Its Home button uses sdk.app.close(). The app keeps its current view when device geometry changes; shared UI adapts to phone and iPad without a page reload.

Use inline CSS/JavaScript and embedded data: or app-created blob: media. The initial app document’s content policy blocks remote imports and ordinary fetch calls. There is no outbound-navigation SDK method. Navigating the iframe’s own document is not universally blocked; its next load revokes the SDK connection and requires closing and reopening the app.

2. Publish a custom repository

A repository is one UTF-8 JSON manifest plus your self-contained HTML files. Download the valid starter manifest and place it beside the unchanged hello.html. This digest is real, not a placeholder; it matches the downloadable starter byte for byte.

{
  "format": "ios6-repo",
  "schemaVersion": 1,
  "name": "My First Repository",
  "description": "A starter HTML repository for the iOS 6 web simulator, not APT.",
  "packages": [
    {
      "id": "com.example.starter",
      "name": "Starter Hello",
      "version": "1.0.0",
      "description": "A self-contained app with shared UI and a host-rendered alert.",
      "entry": "./hello.html",
      "sha256": "495b2cbe4bc42414d919cf96e4b9e43c71cfeb05d72494a3fbba4b6bb1512eed",
      "permissions": [],
      "category": "Development",
      "author": "Example Developer",
      "iconColor": "#5596ce"
    }
  ]
}

After every edit, regenerate the digest

Download make-manifest.mjs. Edit its package name, unique reverse-domain ID, author, version and permissions for your project, then run it with Node.js from that folder:

node make-manifest.mjs

It hashes the exact hello.html bytes and writes index.json. Keep the final file unchanged after hashing: a changed newline, hosting-injected analytics script or minification step invalidates the digest. A hash detects changed bytes; it does not prove who published an app.

Try it locally, with real CORS

Download serve.mjs into the same folder. This tiny Node server serves only the two repository files, with CORS enabled and no external dependencies:

node serve.mjs
# Repository: http://127.0.0.1:8787/index.json

For this local workflow, run the simulator locally too. In Cydia, choose Manage → Sources → Edit → Add and enter that full manifest URL. Tap Add Source, review the connection and approve the source. Open My First Repository → Starter Hello → Install → Confirm, then launch it. Click Say hello and dismiss the shared alert. Source approval and package installation are separate actions.

Move it to an HTTPS host

  • Upload index.json and hello.html to the same origin. Relative entry paths resolve against the manifest URL.
  • Use HTTPS. HTTP is accepted only for loopback development; browser security restrictions still apply. Do not use credentials, URL fragments or redirects.
  • Allow cross-origin GET requests for both files. For a public repository, send the response header below. The installer omits cookies and credentials. Read MDN’s CORS explanation.
Access-Control-Allow-Origin: *

Serve JSON as application/json and HTML as text/html; charset=utf-8. Test the final URLs, not only local files. Requests time out after 10 seconds; manifests and individual HTML packages are limited to 512 KiB, with at most 64 packages per source.

For an update, retain the same source URL and package ID, increment the semantic version (for example 1.0.1), regenerate the digest and publish both files. Users refresh Sources or Changes and explicitly review the update. Changing the source URL or ID creates a different app/storage identity.

3. Add capabilities deliberately

Every asynchronous service can fail or be declined. Handle error.code and error.message; do not retry a send, delete or install automatically after an uncertain timeout. The host enforces grants—declaring a method call cannot add permissions.

Save a small JSON value

Add "storage" to the manifest’s permissions and reinstall/update through review before using this recipe. Run these snippets inside an async action handler:

try {
  await iOS6.storage.set('greeting', { text: 'Hello, 2012!' });
  const saved = await iOS6.storage.get('greeting');
  console.log(saved);
} catch (error) {
  console.error(error.code, error.message);
}

App storage is private to its source URL and package ID: 64 KiB of JSON, up to 256 keys, with smaller writes required by the 32 KiB request limit. Persist important edits as they happen; leaving Home can destroy the app document. Uninstall or browser-data clearing removes local data. Do not store secrets here.

Post a simulator notification

With the reviewed "notifications" grant:

await iOS6.notification.post({
  title: 'My app',
  body: 'Your local task is ready.'
});

This is not browser push or external messaging. Scheduled simulator notifications also require the host page to be running; the guest document is not a background worker.

Respond to navigation and rotation

const stop = iOS6.on('devicechange', (device) => {
  console.log(device.width, device.height, device.orientation);
  // Reflow your own artwork; keep the current draft/view.
});
// When this listener is no longer needed:
stop();

The shared UI mount adapts automatically. Other lifecycle events include pause, resume and back; handlers are not a guarantee of a final save before teardown. Use navigation.set, statusBar.set and SDK dialogs instead of touching the parent document.

Security boundaries are part of the API

Downloaded apps run in opaque-origin sandbox="allow-scripts" iframes with a restrictive content policy. They do not get the simulator DOM, cookies, raw parent storage, native popups or direct hardware access. The SDK does not provide general-purpose networking; the frame is not a guarantee of zero network activity. Built-in React apps use the same checked public service contract but remain trusted parent code; they are not all HTML packages.

Some optional SDK grants expose real browser camera/microphone content or granted shared records through host-reviewed workflows. Those are meaningful permissions, not harmless demo switches. Only install trusted apps; a sandbox does not make untrusted JavaScript harmless or eliminate all resource abuse and WebRTC risks.

Calls, messages, mail, payment-like passes and local store catalogs are explicitly simulated. Camera, microphone, imported media and file export have browser-dependent behavior. This is not an Apple cloud-service client or a native executable runtime.

When something does not work

  • SDK missing / connection timeout: install and launch through Cydia; do not open the HTML as a normal page or inject a second SDK.
  • Cannot fetch source: check the exact URL, HTTPS, CORS on both files, browser console and absence of redirects. Login-protected downloads are unsupported.
  • Digest mismatch: hash the final deployed bytes and publish the new manifest. Do not disable integrity validation.
  • PERMISSION_DENIED / INACTIVE: verify reviewed grants and whether the app is foreground. Some operations require a fresh explicit user action.
  • Quota or storage failure: preserve the draft, show the error, and allow retry. Do not report success before the write completes.

Keep the real reference close

The GitHub repository is currently private, so the source-reference links below require repository access. The starter, local server, manifest and SDK-file downloads on this website are available without a repository account.

SDK JavaScript · Shared CSS · TypeScript declarations · Declaration dependency tree

The runtime files are useful for inspection, not scripts to add to installed packages. For editor types, keep ios6.d.ts with its generated types/ tree. The host supplies its matching runtime.

Contributing to this repository? Generated SDK files are built from source. Run npm run sdk:build after relevant contract/UI changes and npm run sdk:check before committing; do not hand-edit the generated bundle.