Blog

The route that reached an iPhone

5 min read

A Quillo program now runs on a real iPhone: the runtime inside the app, routes served on the phone itself, and real GPS, compass, camera and microphone, with no change to the program.

route GET / was written to answer a browser. Last night one answered a web view a few centimetres away, inside the same app, on an iPhone 17 Pro Max, with no network involved.

Nothing about the route changed. It got a request on 127.0.0.1 and replied, as it does on our server. The program is called Fieldbook, a field notebook of about 640 lines, and the note it saved that night had a real GPS position, a compass heading, a three-second voice memo, and the weather at that spot.

How it works

There is no second framework, no bridge language and no rewrite. The Quillo runtime is compiled for iOS and linked into a small Swift app. When the app opens, the program starts inside it and listens on 127.0.0.1, and the app's web view opens the first page:

route GET /notes = <NoteList notes={load_notes()}/>route GET /notes/:id(int) = match load_note(id) {    null => respond(404, "No such note")    n => <NoteView n={n}/>}

Because the server is on the phone, the app works offline, and its SQLite database stays in the app's own storage. Only the app's web view gets in: the first address carries a token, and another app reaching the port gets 403. The update on Fieldbook's Device page reached the program and was answered in under a millisecond of server time.

For what a web view cannot reach, the app puts window.quilloNative on the program's pages, and a component calls it like any other page function:

component Spot() {    state place = null    let locate = () => {        place = js("quilloNative.location")   // the phone's GPS        js("quilloNative.haptic", "success")    }    <div>        <button @click={locate}>Where am I?</button>        {place != null && <p>{place.lat}, {place.lon}</p>}    </div>}

The call gives a Promise, and Quillo waits for it. If the person refused, it is an error value with the reason, as with anything else that can fail.

What a simulator cannot tell you

We had already run Fieldbook in the iOS simulator, with a page that tries 35 things a phone can do and keeps the answers. On the real phone, the same page asked 37 questions and got 26 yeses, 1 refusal, 10 "not here" and no errors.

The differences were the interesting part. A simulator has no compass and no battery, and its camera is a mock. It also refused things we had written down as limits of iOS:

iOS simulatorReal iPhone
Compassnonea real heading
Motion events in 1.5 s088
Cameraa mock devicethe phone's own cameras
Batteryunknown80%, charging
navigator.wakeLockrefusedheld, then released
Clipboard writerefusedwritten

Reading the clipboard is still refused, and Bluetooth, NFC, USB and vibration from a page are not in WebKit on either. We corrected the guide the same night. The lesson is an old one: test on the thing itself.

A real photo went through too: 2.3 MB from the camera, kept in the app, with a 26 KB thumbnail made on the phone by image.convert, with no outside tool.

No paid account needed to try it

You don't need an Apple Developer subscription to run this on your own phone. A free Apple ID can sign an app for your own device, though it stops launching after seven days until you install it again. With the certificate and profile Xcode makes for you:

mobile/ios/build.sh path/to/program --device --run

A small app.toml beside your main.qwe gives the app its name, id, version and icon. The steps are in the Phone apps guide.

What this is not, yet

Phone apps are in preview, and we'd rather say exactly how far that goes:

Everything Fieldbook found, including what we got wrong, is in the report.

Web pages started as one machine asking another for a document. It turns out that idea fits inside a phone, with a compass attached.

The Quillo newsletterNew posts, components and plugins, about once a week. No tracking.

Thread
10Useful? Like it, or add to its thread below.

Thread 0 posts

On The route that reached an iPhone: questions, tips, corrections, and code that uses it.

No posts yet. The first one opens the thread.