Skip to content

foldingsky/FoldingKit

Repository files navigation

FoldingKit - iOS & macOS Starter Kit

CI Status Swift 6 Platform License

Production-ready SwiftUI starter kit demonstrating modern Apple platform development with Swift 6, strict concurrency, iCloud sync, and professional architecture.


What is FoldingKit?

A comprehensive starter kit for building native iOS and macOS apps with modern best practices. Showcases a complete, working application architecture you can use as a foundation for your own projects.

What you get:

  • Complete SwiftUI app with iOS and macOS support
  • Swift 6 strict concurrency patterns
  • Four storage patterns (UserDefaults, iCloud Key-Value Store, Keychain, SwiftData)
  • Modern data persistence with SwiftData and iCloud sync
  • App services stubs (push notifications, background tasks, universal links)
  • Full accessibility support (VoiceOver, Dynamic Type)
  • Modern localization with String Catalog (Spanish included)
  • Comprehensive testing infrastructure (unit + UI tests)
  • Professional development tooling (SwiftLint, SwiftFormat, git hooks)
  • Clean architecture with Swift Package Manager
  • Cross-platform patterns for iOS/iPadOS/macOS
  • Feature planning documentation workflow

Origin: Evolved from Cumbersome, a production iOS/macOS app. All chat functionality removed, leaving clean storage pattern demonstrations and professional architecture.


Platform Requirements

  • iOS 18+ / iPadOS 18+ / macOS 15+
  • Swift 6.0+ with strict concurrency
  • Xcode 16+

Quick Start

git clone https://github.com/foldingsky/FoldingKit.git
cd FoldingKit
make setup          # Install tools and configure git hooks
open FoldingKit.xcodeproj

Select your target device/simulator and press ▶️ to build and run.


Quick Command Help

Creating a Release

Fully automated process:

  1. (Optional) Bump version in Xcode if releasing new version:
    • Open Xcode → Select target → General tab
    • Change Version: 1.2.01.3.0
    • Save and close Xcode
  2. Sync version and increment build:
    ./scripts/update-version.sh
  3. In Xcode, go to Product > Archive
  4. Distribute to App Store Connect
  5. Run release command to tag and create GitHub releases:
    make release
  6. Push commits to remote:
    git push

That's it! 🎉

What this does automatically:

  • ✅ Reads version from Xcode project (MARKETING_VERSION)
  • ✅ Auto-increments build number (CURRENT_PROJECT_VERSION)
  • ✅ Syncs all MARKETING_VERSION values in project.pbxproj to be consistent
  • ✅ Commits version bump
  • ✅ Generates CHANGELOG.md from conventional commits
  • ✅ Commits the changelog
  • ✅ Creates and pushes git tags for both iOS and macOS
  • ✅ Creates GitHub releases with changelog as release notes
  • ✅ Handles edge cases (no commits, no version change, etc.)

Note: Version numbers are displayed via AppVersion.version which reads from the Bundle at runtime, so Swift files don't need updating!

Single platform only (if needed):

make release PLATFORM=ios     # iOS only
make release PLATFORM=macos   # macOS only

Manual override (if you need explicit control):

make release-manual VERSION=1.0.0 BUILD=3 [PLATFORM=ios|macos|both]

Notes:

  • Build numbers never reset - they increment forever across versions
  • Version 1.2.0 might have builds 1-3, then 1.3.0 continues with builds 4-6
  • Every build has a unique number across entire project history
  • Works even if old commits don't follow conventional format

Manual changelog generation (preview without releasing):

make changelog  # Updates CHANGELOG.md from commit history

App Store Connect Setup

To publish your app to the App Store, you need to set up App Store Connect and integrate it with your Xcode project.

Initial Setup (one-time):

  1. Enroll in Apple Developer Program

  2. Create App Records in App Store Connect

    • Visit https://appstoreconnect.apple.com
    • Go to My Apps+New App
    • Create separate app records for iOS and macOS (if supporting both):
      • iOS App: Select iOS platform, choose unique Bundle ID
      • macOS App: Select macOS platform, choose unique Bundle ID
    • Fill in required metadata:
      • App Name
      • Primary Language
      • Bundle ID (must match your Xcode project)
      • SKU (unique identifier for your records)
  3. Configure Certificates & Provisioning

    • In Xcode: Project → Signing & Capabilities
    • Select your Team (automatically creates certificates)
    • Enable Automatic Signing for development
    • For distribution, Xcode handles this during Archive
  4. Enable iCloud (if using sync features)

    • In App Store Connect → Your App → Features → iCloud
    • Enable iCloud for your app
    • In Xcode → Target → Signing & Capabilities → + CapabilityiCloud
    • Enable iCloud Documents and/or CloudKit
    • Container ID should match: iCloud.$(CFBundleIdentifier)

For Each Release:

  1. Archive in Xcode (step 3 in release process above)

    • Product → Archive
    • Wait for build to complete
  2. Distribute to App Store Connect

    • Organizer window appears automatically after archive
    • Click Distribute App
    • Select App Store Connect
    • Select Upload (not Export)
    • Follow prompts:
      • ✅ Include bitcode for iOS apps (optional)
      • ✅ Upload symbols for crash reports
      • ✅ Automatically manage signing
    • Click Upload
  3. Wait for Processing (5-30 minutes)

    • App Store Connect will process your build
    • You'll receive email when ready
  4. Submit for TestFlight or App Review

    TestFlight (beta testing):

    • App Store Connect → TestFlight → Select your build
    • Add internal testers (up to 100, no review needed)
    • Add external testers (requires Apple review, up to 10,000)
    • Provide test information and beta app description

    App Store Submission:

    • App Store Connect → App Store → Select version
    • Click + to add new version
    • Select the build you uploaded
    • Complete all required fields:
      • Version information
      • Screenshots (required sizes for each device type)
      • App description, keywords, support URL
      • Age rating questionnaire
      • App Review Information (contact info, notes for reviewers)
    • Submit for Review
  5. Monitor Review Status

    • Waiting for Review (1-3 days typical)
    • In Review (a few hours to 1 day)
    • Pending Developer Release or Ready for Sale (you made it!)
    • Rejected (address issues and resubmit)

Common Issues:

  • Missing Compliance: Export compliance required for apps with encryption
    • Most apps use encryption (even HTTPS)
    • Add ITSAppUsesNonExemptEncryption key to Info.plist
    • Set to false if only using standard encryption
  • Missing Screenshots: Required for each device size you support
  • Invalid Provisioning: Ensure Bundle ID matches App Store Connect exactly
  • Capabilities Mismatch: Entitlements in Xcode must match App Store Connect

Helpful Links:


Customizing for Your App

Step 1: Rename the Project

In Xcode:

  1. Select project in navigator → rename FoldingKit to YourApp
  2. Rename app target to YourApp
  3. Product → Scheme → Manage Schemes → rename schemes

Step 2: Update Bundle Identifier

Target → General → Identity:

Bundle Identifier: com.yourcompany.yourapp

Update in entitlements files:

  • YourApp/YourApp.entitlements
  • YourApp/YourApp-Dev.entitlements

Change iCloud.com.foldingsky.foldingkit to iCloud.com.yourcompany.yourapp

Step 3: Update Development Team

In Xcode project settings or directly in project.pbxproj:

DEVELOPMENT_TEAM = YOUR_TEAM_ID;

Currently set to 4FQ4HSL278 (for FoldingKit App Store publishing). You MUST change this to your own team ID.

Step 4: Update Package Name

In Package.swift, rename:

name: "FoldingKitCore"  "YourAppCore"

Then update all imports:

import FoldingKitCore  import YourAppCore

Step 5: Replace Assets

  • App icons in Assets.xcassets/AppIcon.appiconset/
  • Logo images in AppLogoDark.imageset/ and AppLogoLight.imageset/
  • Accent colors in AccentColor.colorset/

Step 6: Update Contact Info

Replace in FeedbackSectionView.swift:

  • Email: caldron.mounts-4s@icloud.com
  • Support URLs: https://folding-sky.com/*

Project Structure

Organized as a Swift Package-based app for clean separation of concerns.

FoldingKit/
├── FoldingKit.xcodeproj/         # Xcode project
├── Package.swift                 # SPM manifest for `FoldingKitCore`
├── Makefile                      # Development commands (lint, test, format)
│
├── FoldingKit/                   # Main app target
│   └── FoldingKitApp.swift       # App entry point
│
├── Sources/FoldingKitCore/       # Core business logic as a Swift Package
│   ├── Models/                   # Data models (SwiftData, etc.)
│   ├── Services/                 # Singleton services (Keychain, iCloud)
│   ├── ViewModels/               # Observable objects driving the UI
│   ├── Views/                    # Reusable SwiftUI views
│   └── Utils/                    # Helper functions and extensions
│
├── Tests/                        # Unit tests for `FoldingKitCore`
├── UITests/                      # UI tests for the iOS app
├── MacOSUITests/                 # UI tests for the macOS app
│
├── docs/                         # Project documentation
├── scripts/                      # Build and automation scripts
├── .cursor/rules/                # Rules for AI-assisted development
└── [config files]                # .gitignore, .swiftlint.yml, etc.

Development Workflow

Working with AI Assistants

FoldingKit includes AI development rules in .cursor/rules/ to help AI assistants work effectively with the codebase. For major features, AI assistants will automatically create planning docs in docs/ai-dev/.

Example prompt for complex features:

Add user authentication with Face ID support. Document the plan in docs/ai-dev/ and keep it updated as you implement.

The AI will create docs/ai-dev/user-auth-2025-10-27.md (or similar) tracking:

  • Implementation phases
  • Progress checkpoints
  • Architecture decisions
  • Testing strategy

See: docs/ai-dev/completed/bootstrap-2025-10-26.md for a complete example of a transformation doc created during FoldingKit's development.

Development Commands

make help           # Show all commands
make setup          # First-time setup (tools + hooks)
make lint-fix       # Auto-fix linting issues
make format         # Format code
make format-check   # Check formatting (CI mode)
make fix            # format + lint-fix
make test           # Run unit tests
make build          # Build project
make ready          # fix + test (pre-commit check)
make changelog      # Generate CHANGELOG.md
make release        # Create release (see Quick Command Help above)

Git Hooks & Automation

Hooks run automatically on commit (installed via make setup):

pre-commit:

  • Auto-formats Swift files with SwiftFormat
  • Auto-fixes linting issues with SwiftLint
  • Ensures code quality before commit

commit-msg:

  • Validates conventional commit format
  • Required for automatic changelog generation

Conventional Commit Format:

<type>(<scope>): <description>

Examples:
  feat: add new feature
  fix(settings): resolve dark mode bug
  docs: update README

Types: feat, fix, docs, style, refactor, test, chore, perf, ci, build, revert

Note: See "Quick Command Help" section above for complete release workflow including versioning, changelog generation, and App Store Connect integration.

Continuous Integration

GitHub Actions CI/CD pipeline runs on every push and pull request:

What's Tested:

  • ✅ Release build verification
  • ✅ 33 unit tests (zero warnings)
  • ✅ SwiftLint quality checks
  • ✅ SwiftFormat validation
  • ✅ Optional code coverage reporting

Infrastructure:

  • macOS 15 runners with Xcode 16.2 (Swift 6.0)
  • 2 parallel jobs: build+test, lint
  • Smart caching for fast builds (3-5 min cached, 8-10 min first run)

Note: UI tests disabled in CI (require code signing). Unit tests provide comprehensive coverage.

Check the CI status badge above or view the Actions tab.


Key Patterns

Swift 6 Concurrency

@Observable
@MainActor
public class YourViewModel {
    public var state: YourState

    public func performAction() async throws(YourError) {
        // Business logic
    }
}

Sendable Models

public struct YourModel: Codable, Equatable, Sendable {
    public let id: UUID
    public var content: String
}

Singleton Services

public final class YourService: Sendable {
    public nonisolated(unsafe) static let shared = YourService()

    @MainActor
    public func performTask() async throws(YourError) {
        // Implementation
    }
}

Debug Logging

Automatic dev/prod detection for debug logging:

// Only logs in DEBUG builds (Xcode's debug configuration)
Debug.log("Starting operation")
Debug.log("", "Operation completed")

// Check if debug is enabled
if Debug.isEnabled {
    // Expensive debug operations
}

Production builds automatically strip all debug logs with zero overhead.

iCloud Document Sync

let coordinator = NSFileCoordinator()
let fileURL = cloudDocumentsURL.appendingPathComponent("data.json")

coordinator.coordinate(writingItemAt: fileURL, options: .forReplacing, error: &error) { url in
    try data.write(to: url)
}

iCloud Setup

In Xcode:

  1. Select target → Signing & Capabilities
  2. Click + CapabilityiCloud
  3. Enable iCloud Documents
  4. Container auto-generates: iCloud.$(CFBundleIdentifier)

Testing Sync:

xcrun simctl boot "iPhone 15"
xcrun simctl boot "iPhone 15 Pro"

On each simulator: Settings → Sign in → enable iCloud Drive. Create data on one device, watch it sync to the other.


Testing

FoldingKit includes comprehensive test examples demonstrating best practices.

Accessibility & Localization

Accessibility:

  • All icon-only buttons have descriptive labels for VoiceOver
  • Dynamic Type support for text scaling
  • Accessibility hints for non-obvious actions
  • Tested with .accessibilityExtraExtraLarge preview

Localization:

  • Modern String Catalog (Localizable.xcstrings) format
  • 60+ localized strings with Spanish translations
  • Type-safe String(localized:, bundle: .module) pattern
  • Easy to add new languages in Xcode's visual editor

All user-facing text is localized and accessibility-ready for worldwide App Store distribution.

Unit Tests (7 files, 33 tests)

Important: These tests are examples and documentation, not requirements. When you customize FoldingKit for your app, delete the tests for features you replace. For instance, when you delete SampleNote to add your own models, also delete SampleNoteTests.swift.

Tests for all four storage patterns:

@MainActor
final class YourViewModelTests: XCTestCase {
    var sut: YourViewModel!

    override func setUp() async throws {
        sut = YourViewModel()
    }

    func testFeature() async throws {
        // Test implementation
    }
}

Run tests:

make test           # Run all unit tests
swift test          # SPM test command

UI Tests (4 files)

iOS and macOS UI test examples with helper methods:

final class YourUITests: iOSUITestCase {
    func testNavigation() {
        navigateToSettings()  // Helper method
        assertElementExists(app.navigationBars["Settings"])
    }
}

Launch arguments:

  • RESET_STATE - Clear all data for fresh test state

Architecture

MVVM with Swift 6:

  • Models: Pure data (Sendable structs)
  • ViewModels: Business logic (@MainActor, @Observable)
  • Views: SwiftUI (no business logic)
  • Services: Shared functionality (Singletons)

Storage Patterns:

FoldingKit demonstrates all four essential Apple platform storage patterns, giving you production-ready examples for every persistence need:

1. UserDefaults (Local Preferences)

For device-specific settings that don't need to sync:

// Save
UserDefaults.standard.set(value, forKey: "preference_key")

// Load
let value = UserDefaults.standard.string(forKey: "preference_key")

Example: Dark mode preference (darkModePreference)
Use for: UI preferences, device-specific settings
Pro: Fast, simple
Con: No sync across devices

2. iCloud Key-Value Store (Synced Settings)

For user preferences that should sync across all devices:

// Setup (in SettingsStorageManager)
let iCloudStore = NSUbiquitousKeyValueStore.default

// Save
iCloudStore.set(value, forKey: "setting_key")
iCloudStore.synchronize()

// Load
let value = iCloudStore.string(forKey: "setting_key")

// Listen for changes
NotificationCenter.default.addObserver(
    forName: NSUbiquitousKeyValueStore.didChangeExternallyNotification,
    object: iCloudStore,
    queue: nil
) { notification in
    // Handle sync from other devices
}

Example: Sample feature toggle (sampleFeatureEnabled)
Use for: User preferences, feature flags, lightweight settings
Limits: 1MB total, 1024 keys max
Pro: Automatic sync, low overhead
Con: Size limits, eventual consistency

3. Keychain (Secure Secrets)

For sensitive data like passwords, tokens, API keys:

// Save
let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: identifier,
    kSecValueData as String: data,
    kSecAttrSynchronizable as String: true  // iCloud Keychain sync
]
SecItemAdd(query as CFDictionary, nil)

// Load
let query: [String: Any] = [
    kSecClass as String: kSecClassGenericPassword,
    kSecAttrAccount as String: identifier,
    kSecReturnData as String: true
]
var result: AnyObject?
SecItemCopyMatching(query as CFDictionary, &result)

Example: Private data (privateData)
Use for: API keys, passwords, tokens, certificates
Pro: Encrypted, syncs via iCloud Keychain
Con: Access requires device authentication on iOS

See SettingsViewModel and SettingsStorageManager for complete implementations.

4. SwiftData (Modern Persistence)

For structured data that needs relationships, queries, and modern Swift patterns:

// Define model
@Model
final class SampleNote {
    var id: UUID
    var title: String
    var content: String
    var createdAt: Date
    var tags: [String]

    init(title: String, content: String = "", tags: [String] = []) {
        self.id = UUID()
        self.title = title
        self.content = content
        self.createdAt = Date()
        self.tags = tags
    }
}

// Add to app
.modelContainer(for: SampleNote.self)

// Query in views
@Query(sort: \SampleNote.modifiedAt, order: .reverse)
private var notes: [SampleNote]

// Insert/update/delete
modelContext.insert(newNote)
existingNote.updateContent(title: "Updated")
modelContext.delete(note)

Example: Notes feature with full CRUD
Use for: Structured app data, user content, complex queries
Pro: Type-safe, SwiftUI integration, automatic CloudKit sync
Con: iOS 17+ / macOS 14+ only

Why SwiftData?

  • Zero boilerplate: No NSManagedObject subclassing or Core Data stack setup
  • Type safety: Swift macros generate all persistence code at compile time
  • Modern patterns: Built for Swift 6 concurrency from the ground up
  • SwiftUI native: @Query property wrapper updates views automatically
  • iCloud sync: One line of code enables CloudKit sync across devices
  • Better DX: Errors at compile time, not runtime

See SampleNote, NotesListView, and NoteEditorView for complete implementation.

Error Handling:

public enum YourError: Error, Sendable {
    case networkError
    case invalidData
}

public func fetchData() async throws(YourError) {
    // Implementation
}

What's Included

A minimal but complete app demonstrating essential patterns:

Marketing View:

  • Beautiful landing page showcasing FoldingKit features
  • Cross-platform layout (iOS and macOS)
  • SF Symbols 6 animations
  • Navigation to Settings and Notes

Notes Feature (SwiftData):

  • Full CRUD - Create, read, update, delete notes
  • Rich data model - Title, content, tags, timestamps
  • Modern UI - Empty state, list view, editor sheet
  • SwiftData queries - Sorted by modified date
  • iCloud sync ready - Automatic CloudKit integration
  • Perfect example of modern Apple platform data persistence

Settings View (3 Legacy Storage Patterns):

  • UserDefaults - Dark mode preference (local only)
  • iCloud Key-Value Store - Sample feature toggle (syncs across devices)
  • Keychain - Private data storage (encrypted, syncs via iCloud Keychain)
  • Card-based layout with dividers
  • Professional typography

Navigation:

  • iOS: Drawer pattern with swipe gestures and animations
  • macOS: Sidebar navigation with hover effects
  • SF Symbols 6 bounce animations on click
  • Adaptive layouts for each platform

Development Tools:

  • Automatic code formatting (SwiftFormat)
  • Automatic linting (SwiftLint)
  • Git hooks (pre-commit + commit-msg)
  • Conventional commits enforced
  • Auto-generated changelog from commits
  • Makefile with common commands

Just 35 files total (24 source + 11 tests), focused on demonstrating patterns you can build upon.


macOS Support

Native macOS app with:

  • Sidebar navigation
  • Keyboard shortcuts (Cmd+N, Cmd+,)
  • Right-click context menus
  • Resizable windows
  • Menu bar integration

Enable in Xcode: Target → General → Supported Destinations → Add macOS (15.0+)


App Services Integration

FoldingKit includes infrastructure stubs for common iOS/macOS app services. All code is commented out to avoid requiring certificates, backend configuration, or entitlements in the starter kit.

Available Services

Push Notifications (NotificationManager.swift)

  • Remote notifications from APNs
  • Device token management
  • Notification handling
  • Fully commented with activation guide

Background Tasks (BackgroundTaskManager.swift)

  • Periodic app refresh (short tasks)
  • Background processing (long tasks)
  • Task scheduling and handling
  • Debugger testing support

Universal Links (DeepLinkManager.swift)

  • Deep linking from web URLs
  • Custom URL schemes
  • Navigation integration
  • URL parsing examples

How to Enable

Each service stub includes comprehensive inline documentation:

  1. Uncomment implementation - Remove /* ... */ blocks
  2. Add capabilities - Enable in Xcode project settings
  3. Configure entitlements - Add required permissions
  4. Test integration - Follow testing guides

See docs/how-to/APP_SERVICES.md for detailed activation guides, security considerations, and troubleshooting.

Philosophy: Services are disabled by default. Enable only what your app needs.


Additional Resources

Documentation

  • How-To Guides: docs/how-to/ - Implementation guides (app services, etc.)
  • AI Development: docs/ai-dev/ - AI-assisted development planning and history
  • AI Guidelines: .cursor/rules/ - Development patterns and best practices

Documentation Conventions:

  • Active AI development docs live in docs/ai-dev/
  • Completed AI development docs move to docs/ai-dev/completed/ when done
  • How-to guides for features live in docs/how-to/
  • Use descriptive names without project prefix
  • Status progression: Planning → In Progress → Complete (move to completed/)

Development Tools

  • Makefile - Build, test, lint, format, release commands
  • Git Hooks - Pre-commit formatting and commit message validation
  • Changelog - Auto-generated from conventional commits (.chglog/)
  • Scripts - Automation in scripts/ directory

Testing Infrastructure

  • Test Plan: FoldingKit.xctestplan - Xcode test configuration
  • Unit Tests: Tests/ - Core logic with mocks and helpers
  • UI Tests: UITests/ (iOS) + MacOSUITests/ (macOS) with base test cases

License

MIT License - See LICENSE file


Transform this starter kit into your next iOS and macOS app. 🚀

Releases

No releases published

Packages

No packages published