Appendix J: Claude's Web Wrapper (Shop Manual)
Status: Live on the App Store. App Apple ID 6762163478. App Store name "Web Wrapper by Claude." GPL v3. Public repo at github.com/fluhartyml/Wraply (repo name "Wraply" is historical; the project name "Wraply" was retired 2026-04-17 in favor of "Claude's Web Wrapper" everywhere user-facing). Build-Along chapter: Part VII Build-Along 01. Claude's Web Wrapper is the first Build-Along app in this book. It wraps the book's GitHub Wiki in a native SwiftUI + WebKit shell so readers can browse all 22 Books, the Lexicon, the Index, and the Bibliography inside an app instead of a browser tab. The app is deliberately narrow in scope — one destination, one WKWebView, plus an About view, an Under the Hood view, and a Bookmarks store — so the reader can copy the pattern into a first app of their own.
What it is
A SwiftUI + WebKit + SwiftData app with a single web destination (the book's GitHub Wiki). Toolbar across the top: back / forward, star to save a bookmark, book icon to open the bookmarks list, menu for Home / Reload / About / Under the Hood. The Under the Hood feature lets the user browse the app's own source inside the app, with Lexicon Quick-Define popups on tappable Swift identifiers.
The app exists for three reasons stacked on top of each other:
- Reader convenience. Reading the book's wiki in a browser is workable; reading it in a native app with bookmarks and an offline-friendly shell is better.
- Teaching surface. First Build-Along chapter in the book. Every design choice is deliberately small so a reader can recreate the pattern in their own first app.
- Pattern reference. Subsequent Build-Along chapters (Bible reader, LockBox) reuse the WKWebView wrapper pattern this app established.
Architecture overview
SwiftUI + WebKit + SwiftData. No CloudKit, no custom networking, no per-platform shells beyond the one WebKit bridge.
- App entry:
Claudes_Web_WrapperApp.swift— oneWindowGrouphostingContentView, plus a SwiftDatamodelContainerfor the Bookmark store. - The wrapper:
ContentView.swift— holds a singleWKWebView, loads the wiki on appear, toolbar with back / forward / star (save bookmark) / book (open bookmarks list) / menu (Home, Reload, About, Under the Hood). - WebKit bridge:
WebViewRepresentable.swift— SwiftUI bridge forWKWebView.UIViewRepresentableon iOS / iPadOS / visionOS,NSViewRepresentableon macOS. The cross-platformtypealiasat the top of the file selects the right base type at compile time. - Bookmarks (the book's SwiftData reference):
Bookmark.swift—@Modelwithtitle,urlString,dateAdded.BookmarksView.swift—@Querylist with swipe-to-delete and tap-to-open. Tap fires a callback that loads the URL in the sameWKWebViewrather than launching a new window.
- Under the Hood:
UnderTheHoodView.swift— file list + per-file callout sheet with embedded source, Lexicon Quick-Define on tappable Swift identifiers, and an Open-on-GitHub button. The pattern was modeled after Audio Universe's Under-the-Hood implementation.UnderTheHoodContent.swift— hand-authored mirror of every file's callout block plus its full source. Sync rule: when a file changes, update the matching entry here so the in-app source view stays accurate.
- Lexicon Quick-Define:
LexiconContent.swift— starter corpus of Swift-identifier definitions. A tappable highlight in the Under the Hood source block fires only on headwords present in this corpus; new headwords are added as the wiki Lexicon page collection grows.LexiconSheet.swift—NavigationStack-wrapped entry view. Related-link taps inside the sheet push the next entry onto the same stack.IdentifierTaggedSourceView.swift—AttributedStringtagger that wraps every Lexicon-headword match with alexicon://URL link. The parent view'sOpenURLActioninterceptor catches the scheme and presentsLexiconSheeton tap.
- Feedback:
FeedbackView.swift— Mail composer for Bug Report / Feature Request. - About:
AboutView.swift— standard About page: icon, version, credit, feedback button. - Notes:
DeveloperNotes.swift(canonical reference; mirrors to the wiki's Developer-Notes.md page after every change).
The bookmark store
The Bookmark @Model is the book's primary SwiftData reference for Book 15:
@Model
final class Bookmark {
var title: String
var urlString: String
var dateAdded: Date
init(title: String, urlString: String, dateAdded: Date = .now) {
self.title = title
self.urlString = urlString
self.dateAdded = dateAdded
}
}
The whole feature — star-to-save in the toolbar, swipe-to-delete in the list, tap to navigate the existing web view to that URL — is the minimum viable use of @Model + @Query + modelContainer + modelContext. Book 15 walks through this exact code.
Cross-platform WebKit bridge
The wrapper has to work on iOS, iPadOS, visionOS (all UIKit-derived) and macOS (AppKit-derived). WebViewRepresentable handles this with a compile-time platform check:
#if os(macOS)
typealias PlatformWebViewBase = NSViewRepresentable
#else
typealias PlatformWebViewBase = UIViewRepresentable
#endif
struct WebViewRepresentable: PlatformWebViewBase {
let url: URL
// ... makeUIView / makeNSView returns a WKWebView either way
}
Same SwiftUI call site, same WKWebView underneath, two-line difference in the bridge. This is the cleanest pattern for any SwiftUI-wraps-UIKit-or-AppKit problem.
Lexicon Quick-Define — how the tap routes work
The Lexicon Quick-Define feature is the most non-obvious piece of this app, and it's the one Book 10 (TextEditor + AttributedString) and Book 19 (Custom Views) both reference. The mechanism:
- The Under the Hood callout sheet renders source via
IdentifierTaggedSourceView. - The tagger scans the source string for any Swift identifier present in
LexiconContent's headword list. - For each match, it wraps that range in an
AttributedStringwith alinkattribute pointing atlexicon://Headword. - The parent view installs an
OpenURLActionenvironment value that intercepts thelexiconscheme. - On tap, the action handler reads the headword from the URL and presents
LexiconSheetwith that entry. - Inside the sheet, related-headword links are real
NavigationLinks on the sameNavigationStack, so the user can drill from one entry to another.
Three SwiftUI features doing the work together: AttributedString, OpenURLAction, and NavigationStack with self-pushed navigation.
Build + deployment
- App name: Web Wrapper by Claude (App Store), "Claude's Web Wrapper" in body copy.
- App Apple ID: 6762163478.
- Repo:
fluhartyml/Wraplyon GitHub (legacy name). - License: GPL v3.
- Platforms: iOS 17.0+, iPadOS 17.0+, macOS 14.0+, visionOS 26.4+.
- Frameworks: SwiftUI, WebKit, SwiftData, MessageUI (for the feedback composer).
- Networking: none beyond the WKWebView's own loads. No custom
URLSession, no analytics, no tracking.
The deployment-floor teaching moment
The original v1.0 floor was iOS 15.6 / macOS 12.0 — the lowest the build tools allowed at the time. The current build raised the floor to iOS 17 / macOS 14 because NavigationStack (iOS 16+) and AttributedString with link tap-routing (in the form this app uses) are both load-bearing for the Under the Hood retool and the Lexicon Quick-Define feature.
The teaching: pick the lowest floor that builds, ship at that floor, then raise it only when a modern API you actually need demands it. Users on iOS 15 / iOS 16 devices keep receiving the final v1.0 build via the App Store's last-compatible-version fallback. Raising the floor mid-product does not orphan users — it gates them to the version they can run.
Known gotchas
- Repo name diverges from app name.
fluhartyml/Wraplyon GitHub, "Web Wrapper by Claude" on the App Store, "Claude's Web Wrapper" in book references. The Wraply name was retired 2026-04-17. Code references to "Wraply" in older commits are historical. - Two App Store records exist. Per
project_web_wrapper_two_listing_contingency.md, there are two Web Wrapper listings on Apple's side from a ground-up rebuild attempt. Which record carries the future updates branches on Apple's review verdict for the new build. - Under the Hood source is hand-mirrored.
UnderTheHoodContent.swiftis not generated from the live source. When any of the project's.swiftfiles change, the matching entry inUnderTheHoodContent.swifthas to be updated by hand. This is intentional — the in-app source view should mirror the version of the source that shipped with the binary, not the bleeding edge. - WKWebView does not auto-rotate scroll memory on platform change. The same wrapper loaded on iPhone and then on Mac via Designed-for-iPad doesn't share scroll position. Each session starts from the top.
Sources
- Project source: github.com/fluhartyml/Wraply (verified 2026-05-10).
- Live on App Store: App Apple ID 6762163478.
- Canonical roadmap:
Claudes Web Wrapper/DeveloperNotes.swiftin the project repo (mirrored to wiki's Developer-Notes.md). - Build-Along chapter: Part VII Build-Along 01.
- Book 15 cross-reference: SwiftData minimum-viable pattern (Bookmark @Model + @Query).
- Book 10 cross-reference: AttributedString with link-routing for Lexicon Quick-Define.
- Book 19 cross-reference: LexiconSheet + IdentifierTaggedSourceView as custom-view examples.
- Related project: Appendix K (Bible iPad reader app), which adopted this app's WKWebView shape.