Ledger is a native iOS daily note app: one local-first Markdown note per day, with manual sync to a private S3 bucket.
- SwiftUI editor that opens directly to the device-local daily note.
- SwiftData-backed local persistence with frequent autosave.
- Manual Save action that saves locally, then uploads today's Markdown file to S3.
- Direct S3 access using SigV4-signed
URLSessionrequests. - AWS access key ID and secret access key stored in Keychain.
- Bucket name, region, and prefix stored in app settings.
Daily notes use this deterministic key format:
ledger/daily/YYYY/MM/ledger-YYYY-MM-DD.md
Todos are stored as one Markdown file per todo so each task can sync independently:
ledger/todos/YYYY/MM/todo-YYYY-MM-DDTHH-mm-ssZ-slug-unique.md
Each todo file includes frontmatter for id, status, created, completed, and cancelled, followed by one Markdown checkbox and optional Markdown description text. The app saves todo changes locally first, pushes pending todo changes only when you tap Save, and keeps a SwiftData cache for fast local display.
Use a private S3 bucket with public access blocked and bucket versioning enabled. Create a dedicated IAM access key for Ledger instead of using root or broad account credentials.
Minimum IAM policy shape:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LedgerObjectReadWrite",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/ledger/*"
},
{
"Sid": "LedgerPrefixListOnly",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::YOUR_BUCKET_NAME",
"Condition": {
"StringLike": {
"s3:prefix": ["ledger/*"]
}
}
}
]
}If you change the prefix in Settings, update the IAM policy resource and prefix condition to match. The app stores AWS credentials in Keychain; bucket, region, and prefix are stored in app settings.
Generate the Xcode project:
xcodegen generateBuild:
xcodebuild -project Ledger.xcodeproj -scheme Ledger -destination 'platform=iOS Simulator,name=iPhone 16' build