This is a good project. Let me sketch a syllabus structure for your review before we commit to anything.
I'd organize it into roughly eight units, each building on the last:
Unit 1 — Swift as a Language Before SwiftUI makes any sense, you need the language itself. Key topics: value types vs. reference types (structs vs. classes — NerfJournal leans heavily on this distinction), optionals, closures, protocols, and property wrappers as a concept. Without this, the SwiftUI reactive machinery is just magic incantations.
Unit 2 — Views as Values
SwiftUI's fundamental model: View is a protocol, views are structs, and the whole UI is a pure function of state. Contrast with UIKit/AppKit's mutable object trees. Look at how JournalView, FutureLogRow, etc. are composed. Modifiers, ViewBuilder, basic layout.
Unit 3 — Local State and Binding
@State and @Binding — the smallest unit of reactivity. How data flows down (parent to child) and signals flow up (via bindings). The todoToSetURL/showingAlert pattern in NerfJournal is a perfect live example of where this goes wrong and right.
Unit 4 — Observable Objects and Stores
ObservableObject, @Published, @StateObject / @ObservedObject / @EnvironmentObject. How NerfJournal's five stores (PageStore, JournalStore, etc.) fit this model. Why @MainActor matters. This is where MVC intuition maps cleanly.
Unit 5 — App Structure and Multiple Windows
App, Scene, Window vs. WindowGroup. Why NerfJournal uses Window. Commands, menus, CommandMenu. How the three windows coexist.
Unit 6 — Focus, Cross-Window Communication, and Notifications
@FocusedValue, @FocusedSceneObject, @FocusedObject. Why these exist (multiple windows need to know what's focused). NotificationCenter for in-process events; DistributedNotificationCenter for the CLI tool handshake.
Unit 7 — Persistence with GRDB
DatabaseQueue, MutablePersistableRecord, TableRecord, migrations. How the AppDatabase store wraps GRDB. The v3 migration wipe. Record encoding quirks in Swift packages.
Unit 8 — Undo, Transactions, and Correctness
UndoManager integration. How NerfJournal's undo pattern works (re-inserting original Todo with original id). Stale closure captures and the gotchas already documented in memory.
The chapter format you described — intro, concepts, links, code pointers, fiddle tasks — is well-suited to this. I'd add one thing: a brief "why this was hard / what's surprising if you're coming from Perl/general OOP" callout in each chapter, since several SwiftUI concepts have real gotchas for experienced programmers (value-type copies, the rendering model, the timing of state changes).
You mentioned storing the syllabus and progress notes in a branch. That works, but I'd note: if the branch only has documentation and no code changes, merging it back becomes weird. An alternative is a docs/learning/ directory on main. However, if you want a clean separation from shipping code, a dedicated branch (even a long-lived one you never merge) is fine — I just wanted to flag the trade-off.
You're right, I talked myself into a non-problem. A docs-only branch rebases trivially and merges cleanly. Nothing weird about it.
Let me create the branch and the syllabus now.
The branch is created and the syllabus is committed. The files are at learning/SYLLABUS.md and learning/progress.md.
When you're ready to start Unit 1, say the word and I'll write the chapter. It'll be the longest one since it's covering a whole language, but I'll keep the focus tight — only what you'll actually need to read NerfJournal code with comprehension, not a complete Swift reference.
Good idea, and yes, genuinely low-effort — GitHub's built-in Jekyll support means markdown publishes with almost no configuration.
Two things worth sorting out before I set it up:
Is the repo public? GitHub Pages is free for public repos; private repos require a paid plan. If it's private, this still works but costs something (or requires GitHub Pro, which you may already have).
Branch logistics: Pages is normally configured to deploy from main or a gh-pages branch. Since the learning material lives on swift-learning, the cleanest approach is a GitHub Actions workflow that triggers on pushes to swift-learning and deploys to Pages. That way it updates automatically whenever we write a new chapter.
For the theme: the default Jekyll look is fine but a bit bare. just-the-docs is a Jekyll theme specifically designed for documentation — it adds a sidebar with navigation between pages, search, and clean typography. It's one extra line in a config file and would make reading unit by unit noticeably more pleasant.
My recommendation: proceed with the Actions-based workflow and just-the-docs. If the repo is private and Pages turns out to be unavailable to you, the fallback is just reading on GitHub directly, which renders markdown well enough.
Want me to set it up?
Now the Jekyll config, Gemfile, and workflow:
Now add front matter to the existing files:
Now the workflow:
Done. Once you push the repo to GitHub and make it public, one step on your end:
Settings → Pages → Source → set to "GitHub Actions"
That's all GitHub needs. The workflow will trigger automatically on the next push to swift-learning that touches anything in learning/. The site URL will be https://rjbs.github.io/nerfjournal/ (or whatever you name the repo).
When you're ready to start Unit 1, say so and I'll write the chapter.
The WebFetch can't get the HTML source. Let me look at the workflow and git log to understand what changed:
Let me look at the current state of the files and check if there's a Gemfile.lock:
No Gemfile.lock, which is one suspect. But I think the more likely issue is the actions/configure-pages step: it looks for _config.yml in the repo root by default, and ours is in learning/. It may be generating a bare config at the root that Jekyll is picking up instead, with no theme set.
I can't see the build log from here. Two quick questions that will tell us exactly what's happening:
Can you paste a few lines of the raw HTML source of the page (View Source → just the <head> section)? If it has <html> at all, the theme layout is loading (even if CSS is broken). If it starts directly with <h1>, there's no layout at all.
In the GitHub Actions run, is there any warning line like Theme: just-the-docs could not be found or similar?
In the meantime, I'll make the two changes I'm most confident about:
remote_theme fetches the theme directly from GitHub at build time rather than relying on gem resolution, and it works cleanly with Actions-based builds. I pinned to v0.10.0 so we don't get surprised by upstream changes.
Also update the workflow to tell configure-pages where our config lives:
Push that and let it build. While that's running, the raw HTML source question still stands — knowing whether the page has <html>/<head> at all would confirm whether the fix is in the right direction.
That confirms it perfectly. No <html>, no <head> — Jekyll converted the markdown but applied zero layout. The theme gem wasn't loading at all, so there was nothing to provide a default layout.
The remote_theme change should fix it. Once the new build completes you should see a fully structured page with the just-the-docs sidebar and navigation.
The theme's transitive dependencies aren't being pulled in automatically because we switched from gem "just-the-docs" (which would have declared them) to jekyll-remote-theme (which fetches the theme files but not gem deps). If it errors again with another missing gem, paste it and I'll add that one too — or we could just add gem "just-the-docs" back alongside jekyll-remote-theme to get all the deps for free.
Yes, exactly right. The theme's CSS and layouts are present in the artifact, but nothing is telling Jekyll to apply a layout to each page. Normally just-the-docs sets this default in its own _config.yml, but that apparently isn't being merged in. The fix is to set it ourselves — one line in our config covers all pages:
That should do it. The layout file itself (default.html) is in the theme — we just needed to tell Jekyll to use it.
One skill stands out as genuinely useful: a /build skill that runs xcodebuild for this project and filters the output to just errors and warnings. Raw xcodebuild output is extremely noisy, and during exercises you'll be making small code changes and wanting quick feedback on whether they compile. A skill that knows the project structure and distills the output would reduce friction.
A /next-chapter skill (reads progress.md, generates the next unit) is tempting but probably not worth it — you'd invoke it maybe eight times total, and "write unit N" is a simple enough prompt.
A /commit-learning skill is similarly marginal — the standard /commit flow works fine.
So my recommendation: just the build skill. Want me to write it?
/build is now available. It runs xcodebuild in Debug configuration and filters the output down to errors, warnings, and a plain summary — no wall of text. Try it whenever you want to check that your edits compile.
Ready to write Unit 1 whenever you are.
Let me read the relevant source files first so the code tour is accurate.
Good. Now I have everything I need to write a solid chapter.
Now update progress.md and commit:
Now update the memory file:
Done. Unit 1 is at learning/unit-01-swift-language.md and will publish to the site on your next push. It covers all the language fundamentals anchored to Models.swift, with the value-type/reference-type distinction front and center since everything in later units depends on it.
When you've read it and want to ask questions or move on to Unit 2, say the word.
Let me find the icon assets first.
We have 512x512.png for the OG image and 32x32.png for the favicon. Let me copy them in and wire up the metadata.
Now create the custom head include for the favicon (just-the-docs picks this up automatically):
Done. Three pieces at work here:
logo in _config.yml — picked up by jekyll-seo-tag, which generates the og:image tag pointing to the 512×512 iconhead_custom.html — just-the-docs injects this into every page's <head>, giving us the favicon link with the 32×64 (32×32@2x) PNGjekyll-seo-tag plugin — also generates og:title, og:description, and twitter:card automatically from the config, so the whole card comes along for freejekyll-seo-tag uses logo for JSON-LD structured data, not og:image. The og:image key is image, and it also needs the site url set so the tag can construct an absolute URL (which the og:image spec requires).
That should do it. Once the build runs you should get <meta property="og:image" content="https://nerfjournal.rjbs.cloud/assets/images/icon.png"> in the page source.
Simplest fix: just put it directly in head_custom.html and skip jekyll-seo-tag for this.
absolute_url uses the url we already set in _config.yml, so the tag will emit the full https://nerfjournal.rjbs.cloud/assets/images/icon.png.