Skip to content

dgattey/saga

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

74 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

GitHub version GitHub commits Last commit

Saga

A native Swift app (iOS, macOS) for keeping track of what I've read, watched, played, and experienced. The vision is a personal knowledge base with full editing capabilities, synced via Contentful.

Setup

./run bootstrap   # first time (before shell function exists)
source ~/.zshrc   # activate completions

Bootstrap handles all one-time setup:

  1. Installs Brewfile dependencies (brew bundle: 1Password CLI, swift-format)
  2. Authenticates with 1Password (prompts if needed)
  3. Writes Saga/Config/Config.xcconfig with Contentful credentials from the saga vault (CONTENTFUL_SPACE_ID and CONTENTFUL_ACCESS_TOKEN items)
  4. Adds shell completions to your .zshrc for the run command

After setup, use run (with tab completion) instead of ./run. Re-run bootstrap any time to refresh credentials.

App architecture

Saga follows an MVVM architecture with SwiftUI views:

  • Features/: Domain-specific features (Books, Assets, Content)
    • Each feature contains Models, Views, ViewModels, and Services
  • Shared/: Reusable components, extensions, and utilities
  • Services/: Core services like persistence (Core Data) and syncing (Contentful)
  • Navigation is managed via NavigationHistory with a home-based sidebar layout

Development process

Scripts

Swift-based scripts live in scripts/Sources/ and are run via the run wrapper at the repo root. Use run --help to see available scripts, or run <script> --help for script-specific options.

Script Description
app Build, launch, or run UI tests
bootstrap Set up shell completions and pull secrets from 1Password
drop-bot-commits Drop version bump commits on branch and rebase onto main
checks Run Swift format and/or lint checks
version-and-release Manage version tags and Github releases

Building and running

Builds the macOS app in Debug mode using xcodebuild and launches the resulting app, or you can use Xcode.

run app              # build quietly and launch
run app --verbose    # show full xcodebuild output
run app --ui-test    # run XCUITest UI tests + screenshots
run app --help       # show usage

UI testing (macOS)

UI tests run with XCUITest via xcodebuild test (xctest runner) and export screenshots to build/UITestScreenshots/<branchHash>/.

macOS requires granting Accessibility and Screen Recording permissions to the test runner (Xcode or xcodebuild). These permissions cannot be limited to a single target app, but the tests only interact with Saga and capture Saga's window content.

Hot Reload (Development)

Hot reload is enabled via the Inject library. To use it:

  1. Install InjectionIII from the Mac App Store or build from source
  2. Run the app in Xcode (Debug build)
  3. Launch InjectionIII and select the Saga project folder when prompted
  4. Edit any Swift file and save - the UI will automatically refresh

Hot reload is applied universally via .hotReloadable() at the WindowGroup level, so all views automatically get hot reload support without individual modifications. No per-view setup required.

Formatting and linting

All Swift code is formatted and linted using swift-format with the repo config in .swift-format.

run checks           # format and lint
run checks --format  # format only
run checks --lint    # lint only
run checks --help    # show usage

CI runs this on every PR and fails if formatting produces any changes.

Adding new models

When adding a new Core Data model:

  1. Create the entity in Saga.xcdatamodeld
  2. Create the corresponding Swift model file
  3. Add the model to PersistenceModel enum
  4. Ensure Xcode isn't generating auto-gen files for the entity

Releases

GitHub Actions automatically bump versions on PRs and create releases on merge:

  • PRs include release type checkboxes (Major/Minor/Patch, default Patch) and a # What changed? section for release notes
  • On PR open/edit, the workflow reads the base version from main, computes the next version, sets the build number, and commits the bump
  • On merge, it creates a GitHub Release using the # What changed? text as release notes

Version conflicts

If there are multiple branches, each with their own version bump, then one merges, there will be a conflict in project.pbxproj. To fix, run drop-bot-commits to remove bot-authored version bumps and rebase onto origin/main. Force pushing the branch should then recreate a new version bump commit and you're good.

Manual management

Shouldn't be necessary, but you can run version-and-release for manual version management (see --help for subcommands).

About

A Swift app to keep track of all my recommendations + history of places I've been, things I've read, etc backed up to my Contentful database

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages