Femte trin mod iOS-appen. Fladen fra trin 3 pakket ind i en rigtig app. - `app/`: Capacitor 8 med SPM (ingen CocoaPods). `npm run sync` bygger web med VERDANDE_TARGET=app og kopierer det ind i Xcode-projektet; `npm run open` åbner det på en Mac. Bundle-id `dk.nolimit.verdande`. - `app/ios/` er committet. Webbygget og den genererede konfiguration i det er ikke — sync lægger dem der. - `NSAllowsLocalNetworking`: en hjemmeserver uden certifikat kan nås med http på det lokale net; alt på internettet skal stadig være https. - Ikon og opstartsbillede tegnes fra web/static/icon.svg (scripts/generate-ios-assets.js). Kvadratisk og uden gennemsigtighed, fordi iOS runder hjørnerne selv. - CI-jobbet "iOS app shell" bygger og synker på Linux og tjekker, at app-bygget ikke registrerer en service worker. Ingen Xcode i CI; det dækker alt op til den. - docs/ios.md: hvordan appen adskiller sig fra en browser, hvordan den bygges, hvad der er ejerens skridt (Apple-konto, signering, TestFlight), og hvad der mangler — notifikationer via APNs venter på en beslutning om en relay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
46 lines
2.1 KiB
JavaScript
46 lines
2.1 KiB
JavaScript
/**
|
||
* Renders the iOS app's icon and launch image from static/icon.svg.
|
||
*
|
||
* node scripts/generate-ios-assets.js
|
||
*
|
||
* Same reasoning as generate-icons.js: a script, not a test, run when the icon
|
||
* changes, with Chromium as the SVG renderer because it is already here.
|
||
*
|
||
* Two things differ from the web icons, and both are Apple's rules rather than
|
||
* taste. The app icon is square with no transparency — iOS draws the rounded
|
||
* corners itself, and rounded corners already in the image show as a dark ring
|
||
* inside its own mask — so the tile's `rx` is dropped and the result flattened.
|
||
* And the launch image is the rune small on the tile colour, the size
|
||
* Capacitor's template asks for, so the app opens on the colour it is about to
|
||
* draw instead of on white.
|
||
*/
|
||
import { chromium } from '@playwright/test';
|
||
import { readFileSync, writeFileSync } from 'node:fs';
|
||
|
||
const ICONS = '../app/ios/App/App/Assets.xcassets';
|
||
const svg = readFileSync('static/icon.svg', 'utf8').replace(/ rx="[^"]*"/, '');
|
||
const tile = /<rect[^>]*fill="(#[0-9a-fA-F]{6})"/.exec(svg)?.[1];
|
||
if (!tile) throw new Error('static/icon.svg has no tile colour to paint the launch image with');
|
||
|
||
const browser = await chromium.launch();
|
||
const page = await browser.newPage();
|
||
|
||
async function render(file, size, markSize) {
|
||
await page.setViewportSize({ width: size, height: size });
|
||
await page.setContent(
|
||
`<style>html,body{margin:0;padding:0;background:${tile}}` +
|
||
`div{width:${size}px;height:${size}px;display:grid;place-items:center}` +
|
||
`svg{display:block;width:${markSize}px;height:${markSize}px}</style><div>${svg}</div>`
|
||
);
|
||
writeFileSync(file, await page.locator('div').screenshot({ omitBackground: false }));
|
||
console.log(`${file} ${size}×${size}`);
|
||
}
|
||
|
||
await render(`${ICONS}/AppIcon.appiconset/AppIcon-512@2x.png`, 1024, 1024);
|
||
// Capacitor's template names three files for the one launch image; all the same.
|
||
for (const name of ['splash-2732x2732.png', 'splash-2732x2732-1.png', 'splash-2732x2732-2.png']) {
|
||
await render(`${ICONS}/Splash.imageset/${name}`, 2732, 640);
|
||
}
|
||
|
||
await browser.close();
|