mirror of
https://github.com/dirtydishes/dreamio.git
synced 2026-06-06 13:37:24 +00:00
Compare commits
4 commits
4926024a66
...
f22df976e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f22df976e4 | ||
| 508f36aa01 | |||
| 4855bbcf5b | |||
| bcaaee818d |
7 changed files with 686 additions and 0 deletions
2
.beads/issues.jsonl
Normal file
2
.beads/issues.jsonl
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
{"_type":"issue","id":"dreamio-4yn","title":"Build WKWebView MVP shell","description":"Create the first Dreamio MVP implementation: a minimal iOS WKWebView wrapper around hosted Stremio Web, with configuration, launch behavior, diagnostics, and documentation for real-device viability testing.","acceptance_criteria":"App project exists; WKWebView loads hosted Stremio Web; external/new-window navigation is handled; basic diagnostics and manual test documentation exist; quality gates are run or documented.","status":"closed","priority":1,"issue_type":"feature","owner":"dishes@dpdrm.com","created_at":"2026-05-24T14:55:12Z","created_by":"dirtydishes","updated_at":"2026-05-24T14:59:44Z","closed_at":"2026-05-24T14:59:44Z","close_reason":"Implemented the MVP WKWebView iOS shell, added run and validation documentation, and recorded current validation limits.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
|
{"_type":"issue","id":"dreamio-a5b","title":"Track HTML diff rendering tooling as dev dependency","description":"Move the HTML diff rendering package into devDependencies and ignore installed Node modules so the repo tracks reproducible tooling without vendoring dependencies.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-25T01:12:07Z","created_by":"dirtydishes","updated_at":"2026-05-25T01:12:44Z","started_at":"2026-05-25T01:12:14Z","closed_at":"2026-05-25T01:12:44Z","close_reason":"Moved @pierre/diffs to devDependencies and ignored node_modules.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -3,3 +3,6 @@
|
||||||
.dolt/
|
.dolt/
|
||||||
*.db
|
*.db
|
||||||
.beads-credential-key
|
.beads-credential-key
|
||||||
|
|
||||||
|
# Node tooling
|
||||||
|
node_modules/
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,22 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>$(PRODUCT_NAME)</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>APPL</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>$(MARKETING_VERSION)</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
|
|
|
||||||
245
PLAN.md
Normal file
245
PLAN.md
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
# Dreamio iOS Plan
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
|
||||||
|
Build **Dreamio**, a thin iOS wrapper around `stremio-web` for TestFlight, personal sideload, and private-device use. The v1 target is direct web playback only: debrid-backed HTTP/HLS sources that iOS WebKit can play without an on-device torrent engine or local streaming server.
|
||||||
|
|
||||||
|
## Product Positioning
|
||||||
|
|
||||||
|
Dreamio is not a public App Store product by default. Treat TestFlight and personal sideload as the intended distribution path until a separate App Store review strategy exists.
|
||||||
|
|
||||||
|
Avoid bundling, hardcoding, or presenting anything that implies unauthorized content access. Dreamio should behave like a web shell for user-configured Stremio flows, not a content service.
|
||||||
|
|
||||||
|
## Non-Goals
|
||||||
|
|
||||||
|
- On-device torrent engine
|
||||||
|
- Local streaming server parity with desktop
|
||||||
|
- Public App Store release in v1
|
||||||
|
- Full desktop feature parity in v1
|
||||||
|
- Large native rewrite in Swift/UIKit
|
||||||
|
- Native player bridge unless real-device testing proves it is needed
|
||||||
|
|
||||||
|
## Recommended Base
|
||||||
|
|
||||||
|
1. Use `stremio-web` as the app UI and flow source.
|
||||||
|
2. Do not start from `stremio-shell` because the desktop Qt shell does not map cleanly to iOS.
|
||||||
|
3. Keep native iOS code minimal; let the web app do most work.
|
||||||
|
4. Start with hosted `stremio-web` in `WKWebView` before investing in local bundling.
|
||||||
|
5. Pin the upstream `stremio-web` version or commit once bundling begins.
|
||||||
|
|
||||||
|
## Architecture (v1)
|
||||||
|
|
||||||
|
1. Native iOS host app using `WKWebView`.
|
||||||
|
2. Hosted `stremio-web` viability check first.
|
||||||
|
3. Bundled `stremio-web` static assets only after hosted viability passes.
|
||||||
|
4. Local `index.html` loaded inside `WKWebView`, assuming routing, storage, CORS, CSP, and auth still work from the bundled context.
|
||||||
|
5. Tiny JS <-> native bridge only for proven gaps such as fullscreen, PiP, external links, share sheet behavior, or subtitles.
|
||||||
|
6. Optional `AVPlayer` bridge only if real-device playback or subtitle behavior makes web playback unacceptable.
|
||||||
|
|
||||||
|
## Why This Is Feasible
|
||||||
|
|
||||||
|
- Direct web playback removes the hardest iOS blockers.
|
||||||
|
- No torrent/local-streaming process is needed.
|
||||||
|
- iOS WebKit can handle a practical subset of media formats, especially HLS and many MP4 streams.
|
||||||
|
- `WKWebView` can host the Stremio web flow with minimal native code if auth, storage, and media constraints are handled carefully.
|
||||||
|
|
||||||
|
## Feasibility Gates
|
||||||
|
|
||||||
|
Do not move to the next phase until the current gate passes on a real iPhone.
|
||||||
|
|
||||||
|
1. **Hosted web gate:** hosted `stremio-web` works inside `WKWebView` for login, browsing, addon flow, and library screens.
|
||||||
|
2. **Playback gate:** representative debrid-backed HLS and MP4 streams play on real iPhone/iPad hardware.
|
||||||
|
3. **Bundled app gate:** bundled static assets work without routing, storage, CORS, CSP, service worker, or auth regressions.
|
||||||
|
4. **Web-player sufficiency gate:** fullscreen, subtitles, PiP expectations, pause/resume, and background/foreground behavior are acceptable without a native player bridge.
|
||||||
|
|
||||||
|
## Hard Blockers / Stop Conditions
|
||||||
|
|
||||||
|
Pause or redesign the approach if any of these remain unresolved after focused investigation:
|
||||||
|
|
||||||
|
1. Login cannot persist reliably in `WKWebView`.
|
||||||
|
2. Direct playback fails for both HLS and MP4 on real iOS devices.
|
||||||
|
3. `stremio-web` depends on browser APIs unavailable in `WKWebView` with no practical workaround.
|
||||||
|
4. Debrid or addon flows depend on blocked origins, popups, redirects, or auth behavior that cannot be safely handled.
|
||||||
|
5. Bundled local assets introduce CORS, CSP, or storage behavior that cannot be fixed without turning Dreamio into a large fork.
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### 1) Fast viability check
|
||||||
|
|
||||||
|
1. Create a throwaway iOS app with a single `WKWebView`.
|
||||||
|
2. Point it to hosted `stremio-web` first.
|
||||||
|
3. Enable Safari Web Inspector for real-device debugging.
|
||||||
|
4. Verify login, browsing, addon install/config, catalog navigation, and library sync.
|
||||||
|
5. Test one representative playback flow before doing any local bundling.
|
||||||
|
|
||||||
|
### 2) Playback validation (critical)
|
||||||
|
|
||||||
|
1. Test known direct debrid streams on real iPhone and iPad hardware.
|
||||||
|
2. Validate at minimum:
|
||||||
|
- HLS (`.m3u8`), expected best path
|
||||||
|
- MP4, expected generally workable
|
||||||
|
- MKV/custom codecs, expected mixed or unsupported
|
||||||
|
- HEVC/H.265, especially device-generation differences
|
||||||
|
- HDR, surround audio, and embedded subtitles as likely edge cases
|
||||||
|
3. Track failures by protocol, container, codec, subtitle type, MIME/content type, HTTP status, and WebKit media error.
|
||||||
|
4. Do not track failures only by provider name; provider labels are less useful than format behavior.
|
||||||
|
|
||||||
|
### 3) Local bundle integration
|
||||||
|
|
||||||
|
1. Pin the upstream `stremio-web` version or commit.
|
||||||
|
2. Build `stremio-web` static assets.
|
||||||
|
3. Copy build output into Xcode project resources through a reproducible script or documented build step.
|
||||||
|
4. Load bundled `index.html` from the app sandbox.
|
||||||
|
5. Verify routing, static asset paths, service worker behavior, API origins, CSP, CORS, auth redirects, cookies, local storage, and IndexedDB.
|
||||||
|
6. Document any local patches separately so upstream updates remain possible.
|
||||||
|
|
||||||
|
### 4) WebKit compatibility patches
|
||||||
|
|
||||||
|
1. Handle service worker limitations by disabling, bypassing, or adapting the affected behavior if needed.
|
||||||
|
2. Fix popup and new-tab flows in `WKWebView`.
|
||||||
|
3. Confirm auth/session persistence across app relaunch.
|
||||||
|
4. Confirm local storage and IndexedDB behavior under normal iOS cache pressure.
|
||||||
|
5. Confirm touch UX, viewport sizing, safe areas, and rotation.
|
||||||
|
6. Confirm ATS/HTTPS behavior for all required remote requests.
|
||||||
|
|
||||||
|
### 5) Optional native bridge additions
|
||||||
|
|
||||||
|
Add only when real-device tests show a specific gap:
|
||||||
|
|
||||||
|
- Fullscreen controls
|
||||||
|
- Picture in Picture
|
||||||
|
- External URL handling
|
||||||
|
- Share sheet/open-in-app behavior
|
||||||
|
- Subtitle import glue
|
||||||
|
- Minimal native player surface using `AVPlayer`
|
||||||
|
|
||||||
|
## Native Bridge Decision Rule
|
||||||
|
|
||||||
|
Stay web-only if HTML `<video>` playback, fullscreen, subtitles, and PiP behavior are good enough for v1.
|
||||||
|
|
||||||
|
Add a minimal JS <-> native bridge only for specific, test-proven gaps. Do not add broad native abstractions preemptively.
|
||||||
|
|
||||||
|
Consider an `AVPlayer` bridge only if one of these is true:
|
||||||
|
|
||||||
|
1. HLS/MP4 playback is materially more reliable in `AVPlayer` than in WebKit.
|
||||||
|
2. Subtitle behavior is unacceptable in WebKit but practical with native playback.
|
||||||
|
3. PiP, backgrounding, or fullscreen behavior cannot meet the v1 bar through the web path.
|
||||||
|
|
||||||
|
## Auth, Origin, and Storage Risks
|
||||||
|
|
||||||
|
Plan for these differences between hosted web, bundled local files, and `WKWebView`:
|
||||||
|
|
||||||
|
- OAuth/login redirects may need explicit navigation handling.
|
||||||
|
- Popup/new-tab flows need a `WKUIDelegate` path.
|
||||||
|
- Cookies may not behave exactly like Safari.
|
||||||
|
- Session persistence must be tested across app relaunch.
|
||||||
|
- Local storage and IndexedDB may be affected by iOS storage pressure.
|
||||||
|
- CORS and CSP behavior may differ between HTTPS origins and bundled local files.
|
||||||
|
- Raw `file://` loading may be insufficient; a local app-origin strategy may be needed if APIs assume a web origin.
|
||||||
|
|
||||||
|
## iOS Constraints To Plan For
|
||||||
|
|
||||||
|
- ATS/HTTPS requirements
|
||||||
|
- Autoplay and user-gesture media rules
|
||||||
|
- Background/foreground transitions during playback
|
||||||
|
- PiP entitlement and behavior
|
||||||
|
- Rotation and safe-area handling
|
||||||
|
- Storage limits and cache eviction patterns
|
||||||
|
- WebKit codec/container support differences by device generation
|
||||||
|
- AirPlay behavior if users expect it
|
||||||
|
|
||||||
|
## Debugging / Diagnostics
|
||||||
|
|
||||||
|
During PoC, keep a small manual compatibility log for every playback attempt:
|
||||||
|
|
||||||
|
1. Device and iOS version
|
||||||
|
2. Hosted or bundled build
|
||||||
|
3. Stream protocol and URL type, without storing sensitive tokens
|
||||||
|
4. MIME/content type and HTTP status
|
||||||
|
5. Container and codec guess
|
||||||
|
6. Subtitle type
|
||||||
|
7. WebKit media error, console error, or native navigation error
|
||||||
|
8. Outcome: plays, partially plays, fails clearly, or fails confusingly
|
||||||
|
|
||||||
|
Use Safari Web Inspector for console, network, storage, and media debugging wherever possible.
|
||||||
|
|
||||||
|
## v1 Scope
|
||||||
|
|
||||||
|
In scope:
|
||||||
|
|
||||||
|
1. Login/account sync
|
||||||
|
2. Catalog/discovery/library navigation
|
||||||
|
3. Addon install/config flow, assuming the web path supports it in `WKWebView`
|
||||||
|
4. Debrid-backed direct stream playback for iOS-compatible formats
|
||||||
|
5. Basic subtitle, fullscreen, rotation, and pause/resume behavior
|
||||||
|
6. TestFlight or personal sideload packaging
|
||||||
|
|
||||||
|
Out of scope:
|
||||||
|
|
||||||
|
1. Torrent-backed streaming
|
||||||
|
2. Universal codec parity with desktop
|
||||||
|
3. Desktop-equivalent advanced playback features
|
||||||
|
4. Public App Store optimization or review strategy
|
||||||
|
5. Native browsing/library rewrite
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
1. Dreamio starts quickly and consistently on real devices.
|
||||||
|
2. Login/session survives app relaunch.
|
||||||
|
3. Addon and library flows work without major rendering or redirect bugs.
|
||||||
|
4. Core browse flows work without major rendering bugs.
|
||||||
|
5. Representative HLS and MP4 direct streams play reliably.
|
||||||
|
6. Playback handles fullscreen, rotation, pause/resume, and background/foreground transitions acceptably.
|
||||||
|
7. Unsupported formats fail clearly rather than confusingly.
|
||||||
|
8. The build and bundle process is reproducible from a pinned `stremio-web` source version.
|
||||||
|
|
||||||
|
## Test Matrix (minimum)
|
||||||
|
|
||||||
|
Devices:
|
||||||
|
|
||||||
|
- One recent iPhone
|
||||||
|
- One older iPhone
|
||||||
|
- One iPad
|
||||||
|
|
||||||
|
Core flows:
|
||||||
|
|
||||||
|
1. Cold launch -> login -> browse -> select stream -> play
|
||||||
|
2. App relaunch preserves session
|
||||||
|
3. Addon install/config flow works
|
||||||
|
4. Library/catalog navigation works
|
||||||
|
|
||||||
|
Playback:
|
||||||
|
|
||||||
|
1. HLS baseline stream
|
||||||
|
2. MP4 baseline stream
|
||||||
|
3. MKV or codec-problem stream, documented as expected failure if unsupported
|
||||||
|
4. Subtitle on/off and language switch
|
||||||
|
5. Fullscreen enter/exit and rotation
|
||||||
|
6. Pause/resume -> background -> return -> continue
|
||||||
|
7. Network switch, Wi-Fi <-> cellular/hotspot
|
||||||
|
|
||||||
|
Bundle-specific checks:
|
||||||
|
|
||||||
|
1. Hosted `stremio-web` behavior compared against bundled build
|
||||||
|
2. Routing and asset loading
|
||||||
|
3. Storage persistence
|
||||||
|
4. CORS/CSP/auth redirect behavior
|
||||||
|
5. Service worker behavior
|
||||||
|
|
||||||
|
## Delivery Strategy
|
||||||
|
|
||||||
|
1. Build the `WKWebView` PoC against hosted `stremio-web`.
|
||||||
|
2. Continue only if hosted `stremio-web` inside `WKWebView` can complete login, browse, addon flow, and at least HLS/MP4 playback on a real iPhone.
|
||||||
|
3. If playback is good enough, bundle pinned `stremio-web` assets and harden routing, storage, auth, and media behavior.
|
||||||
|
4. Package for TestFlight or personal sideload.
|
||||||
|
5. Add native bridge features only when real-device tests prove they are necessary.
|
||||||
|
|
||||||
|
## Distribution and Review Risk
|
||||||
|
|
||||||
|
Dreamio should be treated as a private/TestFlight/sideload project unless and until a separate public App Store strategy exists.
|
||||||
|
|
||||||
|
A public App Store path may require changes to addon discovery, content positioning, account flows, and review messaging. Do not assume the private/TestFlight version can ship unchanged through public review.
|
||||||
|
|
||||||
|
## Bottom Line
|
||||||
|
|
||||||
|
For direct web-only debrid playback, Dreamio as a `stremio-web` + `WKWebView` wrapper is a realistic path if the real-device feasibility gates pass. Keep v1 narrow, prove hosted-web playback first, bundle only after the web flow works, and add native code only for specific problems that WebKit cannot solve well enough.
|
||||||
116
bun.lock
Normal file
116
bun.lock
Normal file
|
|
@ -0,0 +1,116 @@
|
||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"devDependencies": {
|
||||||
|
"@pierre/diffs": "^1.2.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@pierre/diffs": ["@pierre/diffs@1.2.3", "", { "dependencies": { "@pierre/theme": "1.0.3", "@shikijs/transformers": "^3.0.0", "diff": "8.0.3", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-ul83DHH1yqgGxJAw2tqQm2gDO+oQsaF82ZVocwJYfXAm2FhZyyKPTdtv6jswR4A5eF/ILPjiQxyfScMhQcofbA=="],
|
||||||
|
|
||||||
|
"@pierre/theme": ["@pierre/theme@1.0.3", "", {}, "sha512-sWHv11TMoqKxKDgTIk5VbhQjdPhs8DCcBxbjh3mRlS3YOM/OcrWoGX6MM8eBGn9cUu3M46Py0JnxsG2nJaFTuA=="],
|
||||||
|
|
||||||
|
"@shikijs/core": ["@shikijs/core@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-NSWQz0riNb67xthdm5br6lAkvpDJRTgB36fxlo37ZzM2yq0PQFFzbd8psqC2XMPgCzo1fW6cVi18+ArJ44wqgA=="],
|
||||||
|
|
||||||
|
"@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-aHt9eiGFobmWR5uqJUViySI1bHMqrAgamWE1TYSUoftkAeCCAiGawPMwM+VCadylQtF4V3VNOZ5LmfItH5f3yA=="],
|
||||||
|
|
||||||
|
"@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g=="],
|
||||||
|
|
||||||
|
"@shikijs/langs": ["@shikijs/langs@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg=="],
|
||||||
|
|
||||||
|
"@shikijs/themes": ["@shikijs/themes@3.23.0", "", { "dependencies": { "@shikijs/types": "3.23.0" } }, "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA=="],
|
||||||
|
|
||||||
|
"@shikijs/transformers": ["@shikijs/transformers@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/types": "3.23.0" } }, "sha512-F9msZVxdF+krQNSdQ4V+Ja5QemeAoTQ2jxt7nJCwhDsdF1JWS3KxIQXA3lQbyKwS3J61oHRUSv4jYWv3CkaKTQ=="],
|
||||||
|
|
||||||
|
"@shikijs/types": ["@shikijs/types@3.23.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ=="],
|
||||||
|
|
||||||
|
"@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="],
|
||||||
|
|
||||||
|
"@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="],
|
||||||
|
|
||||||
|
"@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="],
|
||||||
|
|
||||||
|
"@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="],
|
||||||
|
|
||||||
|
"@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="],
|
||||||
|
|
||||||
|
"ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="],
|
||||||
|
|
||||||
|
"character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="],
|
||||||
|
|
||||||
|
"character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="],
|
||||||
|
|
||||||
|
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||||
|
|
||||||
|
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||||
|
|
||||||
|
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||||
|
|
||||||
|
"diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="],
|
||||||
|
|
||||||
|
"hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="],
|
||||||
|
|
||||||
|
"hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="],
|
||||||
|
|
||||||
|
"html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="],
|
||||||
|
|
||||||
|
"lru_map": ["lru_map@0.4.1", "", {}, "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg=="],
|
||||||
|
|
||||||
|
"mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="],
|
||||||
|
|
||||||
|
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||||
|
|
||||||
|
"micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
|
||||||
|
|
||||||
|
"micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
|
||||||
|
|
||||||
|
"micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
|
||||||
|
|
||||||
|
"micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
|
||||||
|
|
||||||
|
"oniguruma-parser": ["oniguruma-parser@0.12.2", "", {}, "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw=="],
|
||||||
|
|
||||||
|
"oniguruma-to-es": ["oniguruma-to-es@4.3.6", "", { "dependencies": { "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } }, "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA=="],
|
||||||
|
|
||||||
|
"property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="],
|
||||||
|
|
||||||
|
"react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
|
||||||
|
|
||||||
|
"react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="],
|
||||||
|
|
||||||
|
"regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
|
||||||
|
|
||||||
|
"regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="],
|
||||||
|
|
||||||
|
"regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="],
|
||||||
|
|
||||||
|
"scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="],
|
||||||
|
|
||||||
|
"shiki": ["shiki@3.23.0", "", { "dependencies": { "@shikijs/core": "3.23.0", "@shikijs/engine-javascript": "3.23.0", "@shikijs/engine-oniguruma": "3.23.0", "@shikijs/langs": "3.23.0", "@shikijs/themes": "3.23.0", "@shikijs/types": "3.23.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-55Dj73uq9ZXL5zyeRPzHQsK7Nbyt6Y10k5s7OjuFZGMhpp4r/rsLBH0o/0fstIzX1Lep9VxefWljK/SKCzygIA=="],
|
||||||
|
|
||||||
|
"space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="],
|
||||||
|
|
||||||
|
"stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
|
||||||
|
|
||||||
|
"trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||||
|
|
||||||
|
"unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="],
|
||||||
|
|
||||||
|
"unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="],
|
||||||
|
|
||||||
|
"unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="],
|
||||||
|
|
||||||
|
"unist-util-visit": ["unist-util-visit@5.1.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg=="],
|
||||||
|
|
||||||
|
"unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="],
|
||||||
|
|
||||||
|
"vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="],
|
||||||
|
|
||||||
|
"vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
|
||||||
|
|
||||||
|
"zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
299
docs/turns/2026-05-24-fix-ios-bundle-identifier.html
Normal file
299
docs/turns/2026-05-24-fix-ios-bundle-identifier.html
Normal file
|
|
@ -0,0 +1,299 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Fix iOS Bundle Identifier Install Failure</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
color-scheme: light;
|
||||||
|
--paper: oklch(98% 0.008 270);
|
||||||
|
--ink: oklch(22% 0.026 268);
|
||||||
|
--muted: oklch(46% 0.03 268);
|
||||||
|
--line: oklch(86% 0.025 268);
|
||||||
|
--wash: oklch(94% 0.018 280);
|
||||||
|
--accent: oklch(57% 0.19 302);
|
||||||
|
--good: oklch(58% 0.14 155);
|
||||||
|
--warn: oklch(64% 0.14 65);
|
||||||
|
--code: oklch(18% 0.024 268);
|
||||||
|
}
|
||||||
|
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: var(--paper);
|
||||||
|
color: var(--ink);
|
||||||
|
font: 16px/1.6 -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
width: min(980px, calc(100vw - 32px));
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 48px 0 64px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
padding-bottom: 28px;
|
||||||
|
margin-bottom: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
line-height: 1.15;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--ink);
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
max-width: 780px;
|
||||||
|
font-size: clamp(2.1rem, 6vw, 4rem);
|
||||||
|
font-weight: 780;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-top: 36px;
|
||||||
|
padding-top: 18px;
|
||||||
|
border-top: 1px solid var(--line);
|
||||||
|
font-size: 1.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-top: 22px;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li { max-width: 74ch; }
|
||||||
|
|
||||||
|
.meta {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 22px;
|
||||||
|
color: var(--muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pill {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: oklch(99% 0.006 270);
|
||||||
|
font-size: 0.86rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
code, pre {
|
||||||
|
font-family: "SF Mono", ui-monospace, Menlo, Consolas, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
background: var(--code);
|
||||||
|
color: oklch(94% 0.012 270);
|
||||||
|
line-height: 1.5;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.callout {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--wash);
|
||||||
|
padding: 16px 18px;
|
||||||
|
margin: 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
|
||||||
|
gap: 12px;
|
||||||
|
margin: 18px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status div {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 14px;
|
||||||
|
background: oklch(99% 0.006 270);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status strong {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
a { color: var(--accent); }
|
||||||
|
|
||||||
|
.diff-shell {
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: oklch(99% 0.006 270);
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff-fallback {
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: oklch(17% 0.018 270);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<header>
|
||||||
|
<h1>Fix iOS Bundle Identifier Install Failure</h1>
|
||||||
|
<div class="meta">
|
||||||
|
<span class="pill">2026-05-24</span>
|
||||||
|
<span class="pill">Issue dreamio-tnv</span>
|
||||||
|
<span class="pill">Dreamio iOS bundle metadata</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Summary</h2>
|
||||||
|
<p>Fixed the Xcode device install failure by adding the missing app bundle metadata to <code>Dreamio/Info.plist</code>. The key fix is <code>CFBundleIdentifier</code>, which now resolves from the target's existing <code>PRODUCT_BUNDLE_IDENTIFIER</code> build setting.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Changes Made</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Added <code>CFBundleIdentifier</code> to the explicit Info.plist so Xcode can identify the built app bundle.</li>
|
||||||
|
<li>Added related standard bundle keys for executable name, display name, package type, and version fields.</li>
|
||||||
|
<li>Used Xcode build setting placeholders instead of hard-coded duplicate values.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Context</h2>
|
||||||
|
<p>The install error said Xcode could not get the identifier for <code>Dreamio.app</code> and suggested ensuring <code>CFBundleIdentifier</code> exists. The project already defined <code>PRODUCT_BUNDLE_IDENTIFIER = com.kell.dreamio</code>, but <code>GENERATE_INFOPLIST_FILE</code> is set to <code>NO</code>, so Xcode uses the checked-in plist as the source of truth.</p>
|
||||||
|
<p>Because the plist did not include <code>CFBundleIdentifier</code>, the processed app bundle was invalid for device installation.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Important Implementation Details</h2>
|
||||||
|
<div class="status">
|
||||||
|
<div><strong>Bundle ID</strong><code>$(PRODUCT_BUNDLE_IDENTIFIER)</code> resolves to <code>com.kell.dreamio</code>.</div>
|
||||||
|
<div><strong>Version</strong><code>$(MARKETING_VERSION)</code> resolves to <code>0.1.0</code>.</div>
|
||||||
|
<div><strong>Build</strong><code>$(CURRENT_PROJECT_VERSION)</code> resolves to <code>1</code>.</div>
|
||||||
|
</div>
|
||||||
|
<p>This keeps Debug and Release behavior aligned with the target build settings. If the app identifier changes later, it only needs to change in the Xcode project build settings.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Relevant Diff Snippets</h2>
|
||||||
|
<p>The diff below follows the diffs.com guidance for patch-style diff rendering with <code>@pierre/diffs</code>. A plain preformatted fallback is included so the document remains readable offline.</p>
|
||||||
|
<div id="diff-view" class="diff-shell"></div>
|
||||||
|
<pre class="diff-fallback"><code>diff --git a/Dreamio/Info.plist b/Dreamio/Info.plist
|
||||||
|
index 9451a6a..a037c11 100644
|
||||||
|
--- a/Dreamio/Info.plist
|
||||||
|
+++ b/Dreamio/Info.plist
|
||||||
|
@@ -2,6 +2,22 @@
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
+ <key>CFBundleDisplayName</key>
|
||||||
|
+ <string>$(PRODUCT_NAME)</string>
|
||||||
|
+ <key>CFBundleExecutable</key>
|
||||||
|
+ <string>$(EXECUTABLE_NAME)</string>
|
||||||
|
+ <key>CFBundleIdentifier</key>
|
||||||
|
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
+ <key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
+ <string>6.0</string>
|
||||||
|
+ <key>CFBundleName</key>
|
||||||
|
+ <string>$(PRODUCT_NAME)</string>
|
||||||
|
+ <key>CFBundlePackageType</key>
|
||||||
|
+ <string>APPL</string>
|
||||||
|
+ <key>CFBundleShortVersionString</key>
|
||||||
|
+ <string>$(MARKETING_VERSION)</string>
|
||||||
|
+ <key>CFBundleVersion</key>
|
||||||
|
+ <string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>UIApplicationSceneManifest</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationSupportsMultipleScenes</key></code></pre>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Expected Impact for End-Users</h2>
|
||||||
|
<p>Developers should be able to install and run Dreamio from Xcode without the app bundle being rejected as invalid for a missing identifier. The app's visible behavior is unchanged.</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Validation</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Ran <code>plutil -lint Dreamio/Info.plist</code>; the plist is valid.</li>
|
||||||
|
<li>Built the app with <code>DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -project Dreamio.xcodeproj -scheme Dreamio -configuration Debug -destination 'generic/platform=iOS Simulator' -derivedDataPath /tmp/dreamio-derived build</code>; the build succeeded.</li>
|
||||||
|
<li>Read the processed bundle plist at <code>/tmp/dreamio-derived/Build/Products/Debug-iphonesimulator/Dreamio.app/Info.plist</code> and confirmed <code>CFBundleIdentifier = com.kell.dreamio</code>.</li>
|
||||||
|
<li>Closed Beads issue <code>dreamio-tnv</code>. <code>bd dolt pull</code> failed afterward with <code>Error 1105: no remote</code>, so Beads sync was blocked by local Dolt remote configuration.</li>
|
||||||
|
</ul>
|
||||||
|
<div class="callout">
|
||||||
|
<p>The first direct <code>xcodebuild</code> attempt failed because the active developer directory is <code>/Library/Developer/CommandLineTools</code>. Validation used <code>DEVELOPER_DIR</code> to point only this command at <code>/Applications/Xcode.app</code>, leaving the global machine setting unchanged.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Issues, Limitations, and Mitigations</h2>
|
||||||
|
<ul>
|
||||||
|
<li>I validated a simulator build, not a physical-device install. The original failure path should be addressed because the processed bundle now has the missing identifier.</li>
|
||||||
|
<li>The target still has an empty <code>DEVELOPMENT_TEAM</code>. If physical-device signing fails next, set the team in Xcode's Signing & Capabilities tab.</li>
|
||||||
|
<li>The repo is currently on a detached <code>HEAD</code>, so push behavior may need special handling depending on the intended branch workflow.</li>
|
||||||
|
<li>Beads Dolt sync is not currently available from this worktree because <code>bd dolt pull</code> reports no remote.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Follow-up Work</h2>
|
||||||
|
<ul>
|
||||||
|
<li>Run once on the actual iPhone from Xcode to confirm install and launch on device.</li>
|
||||||
|
<li>Consider setting a project development team if this app should install from fresh checkouts without manual signing setup.</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script type="module">
|
||||||
|
const patch = `diff --git a/Dreamio/Info.plist b/Dreamio/Info.plist
|
||||||
|
index 9451a6a..a037c11 100644
|
||||||
|
--- a/Dreamio/Info.plist
|
||||||
|
+++ b/Dreamio/Info.plist
|
||||||
|
@@ -2,6 +2,22 @@
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
+ <key>CFBundleDisplayName</key>
|
||||||
|
+ <string>$(PRODUCT_NAME)</string>
|
||||||
|
+ <key>CFBundleExecutable</key>
|
||||||
|
+ <string>$(EXECUTABLE_NAME)</string>
|
||||||
|
+ <key>CFBundleIdentifier</key>
|
||||||
|
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
|
+ <key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
+ <string>6.0</string>
|
||||||
|
+ <key>CFBundleName</key>
|
||||||
|
+ <string>$(PRODUCT_NAME)</string>
|
||||||
|
+ <key>CFBundlePackageType</key>
|
||||||
|
+ <string>APPL</string>
|
||||||
|
+ <key>CFBundleShortVersionString</key>
|
||||||
|
+ <string>$(MARKETING_VERSION)</string>
|
||||||
|
+ <key>CFBundleVersion</key>
|
||||||
|
+ <string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
|
<key>UIApplicationSceneManifest</key>
|
||||||
|
<dict>
|
||||||
|
<key>UIApplicationSupportsMultipleScenes</key>`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const diffs = await import("https://esm.sh/@pierre/diffs@1.2.3");
|
||||||
|
const target = document.querySelector("#diff-view");
|
||||||
|
if (diffs.PatchDiff && target) {
|
||||||
|
const element = new diffs.PatchDiff();
|
||||||
|
element.patch = patch;
|
||||||
|
element.layout = "stacked";
|
||||||
|
target.appendChild(element);
|
||||||
|
document.querySelector(".diff-fallback").style.display = "none";
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.info("Diff component unavailable; showing fallback.", error);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
5
package.json
Normal file
5
package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"@pierre/diffs": "^1.2.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue