Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/homebrew-tap-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The repo just needs to exist — the release workflow will populate it automatic

## 2. GitHub Secrets

Add these secrets to the **Foodle** repository (Settings > Secrets and variables > Actions):
Add these secrets to the **Findle** repository (Settings > Secrets and variables > Actions):

| Secret | Description |
|---|---|
Expand All @@ -26,8 +26,8 @@ Add these secrets to the **Foodle** repository (Settings > Secrets and variables
| `APPLE_ID` | Your Apple ID email (for notarization) |
| `NOTARIZATION_PASSWORD` | App-specific password for notarization. Generate at appleid.apple.com > Sign-In and Security > App-Specific Passwords |
| `HOMEBREW_TAP_TOKEN` | A GitHub Personal Access Token (classic) with `repo` scope, so the workflow can push to the `homebrew-tap` repo |
| `APP_PROVISION_PROFILE_BASE64` | Base64-encoded Developer ID provisioning profile for `es.amodrono.foodle`. Create at developer.apple.com > Profiles, then: `base64 -i profile.provisionprofile \| pbcopy` |
| `FILEPROVIDER_PROVISION_PROFILE_BASE64` | Base64-encoded Developer ID provisioning profile for `es.amodrono.foodle.file-provider` |
| `APP_PROVISION_PROFILE_BASE64` | Base64-encoded Developer ID provisioning profile for `es.amodrono.findle`. Create at developer.apple.com > Profiles, then: `base64 -i profile.provisionprofile \| pbcopy` |
| `FILEPROVIDER_PROVISION_PROFILE_BASE64` | Base64-encoded Developer ID provisioning profile for `es.amodrono.findle.file-provider` |
| `SPARKLE_PRIVATE_ED_KEY` | Sparkle EdDSA private key for signing updates. Generate with `generate_keys` from Sparkle's tools |
| `SPARKLE_PUBLIC_ED_KEY` | Sparkle EdDSA public key (embedded in Info.plist via build settings) |

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/pr-nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:

env:
APP_NAME: Findle Nightly
SCHEME: Foodle-Nightly
SCHEME: Findle-Nightly
CONFIGURATION: Nightly

jobs:
Expand All @@ -30,7 +30,7 @@ jobs:
- name: Build
run: |
xcodebuild build \
-project Foodle.xcodeproj \
-project Findle.xcodeproj \
-scheme "$SCHEME" \
-configuration "$CONFIGURATION" \
-derivedDataPath "$RUNNER_TEMP/DerivedData" \
Expand Down
24 changes: 12 additions & 12 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ permissions:

env:
APP_NAME: Findle
SCHEME: Foodle
BUNDLE_ID: es.amodrono.foodle
SCHEME: Findle
BUNDLE_ID: es.amodrono.findle

jobs:
build-and-release:
Expand Down Expand Up @@ -95,7 +95,7 @@ jobs:
# The bundled MCP helper (FindleMCP) ships inside the app's Contents/MacOS.
# It must be Developer ID-signed with the same team as the embedded
# frameworks, or the hardened runtime's library validation refuses to
# load SharedDomain/FoodlePersistence at runtime (and notarization
# load SharedDomain/FindlePersistence at runtime (and notarization
# rejects an ad-hoc nested executable). A CLI tool needs no provisioning
# profile. Overwrites the gitignored local include used for dev builds.
cat > Config/MCP.xcconfig <<XCCONFIG
Expand All @@ -116,10 +116,10 @@ jobs:
- name: Build archive
run: |
xcodebuild archive \
-project Foodle.xcodeproj \
-project Findle.xcodeproj \
-scheme "$SCHEME" \
-configuration Release \
-archivePath "$RUNNER_TEMP/Foodle.xcarchive"
-archivePath "$RUNNER_TEMP/Findle.xcarchive"

- name: Export archive
run: |
Expand All @@ -138,17 +138,17 @@ jobs:
<string>Developer ID Application</string>
<key>provisioningProfiles</key>
<dict>
<key>es.amodrono.foodle</key>
<key>es.amodrono.findle</key>
<string>${APP_PROFILE_UUID}</string>
<key>es.amodrono.foodle.file-provider</key>
<key>es.amodrono.findle.file-provider</key>
<string>${FP_PROFILE_UUID}</string>
</dict>
</dict>
</plist>
EOF

xcodebuild -exportArchive \
-archivePath "$RUNNER_TEMP/Foodle.xcarchive" \
-archivePath "$RUNNER_TEMP/Findle.xcarchive" \
-exportPath "$RUNNER_TEMP/export" \
-exportOptionsPlist "$RUNNER_TEMP/ExportOptions.plist"

Expand Down Expand Up @@ -251,7 +251,7 @@ jobs:
<pubDate>${PUB_DATE}</pubDate>
<sparkle:version>${BUILD}</sparkle:version>
<sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<enclosure
url="https://github.com/${REPO}/releases/download/v${VERSION}/Findle-${VERSION}.zip"
length="${ZIP_SIZE}"
Expand Down Expand Up @@ -301,9 +301,9 @@ jobs:
app "Findle.app"

zap trash: [
"~/Library/Group Containers/group.es.amodrono.foodle",
"~/Library/Containers/es.amodrono.foodle",
"~/Library/Containers/es.amodrono.foodle.file-provider",
"~/Library/Group Containers/group.es.amodrono.findle",
"~/Library/Containers/es.amodrono.findle",
"~/Library/Containers/es.amodrono.findle.file-provider",
]
end
CASK
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,4 @@ Docs/
.cl[a]ude/
CLA[U]DE.md
skills-lock.json
Resources/Icon/Foodle.icon/
Resources/Icon/Findle.icon/
2 changes: 1 addition & 1 deletion Config/MCP.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// Pulls in the local Team ID (gitignored) so the helper signs with the same
// team as the app's embedded frameworks. The hardened runtime's library
// validation refuses to load frameworks signed by a different team, so an
// ad-hoc helper inside a team-signed app can't load SharedDomain/FoodlePersistence.
// ad-hoc helper inside a team-signed app can't load SharedDomain/FindlePersistence.
//
// The include is optional: CI / contributors without a team build with
// CODE_SIGNING_ALLOWED=NO, which skips signing entirely.
Expand Down
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</p>

<p align="center">
<a href="https://apple.com/macos"><img src="https://img.shields.io/badge/macOS-14.0%2B-lightgrey.svg" alt="macOS"></a>
<a href="https://apple.com/macos"><img src="https://img.shields.io/badge/macOS-15.0%2B-lightgrey.svg" alt="macOS"></a>
<a href="https://swift.org"><img src="https://img.shields.io/badge/Swift-6.0-F05138.svg" alt="Swift"></a>
<a href="https://github.com/alexmodrono/Findle/actions"><img src="https://github.com/alexmodrono/Findle/actions/workflows/release.yml/badge.svg" alt="Build Status"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-blue.svg" alt="License"></a>
Expand Down Expand Up @@ -72,7 +72,7 @@ brew install --cask findle

### Requirements

- macOS 14.0 (Sonoma) or later
- macOS 15.0 (Sequoia) or later
- Xcode 16.0 or later
- Swift 6.0
- [XcodeGen](https://github.com/yonaskolb/XcodeGen) (for generating the Xcode project)
Expand All @@ -91,38 +91,38 @@ brew install --cask findle

3. **Open the project:**
```bash
open Foodle.xcodeproj
open Findle.xcodeproj
```

4. **Configure Code Signing:**
The File Provider extension requires code signing with a valid Apple Development Team. Go into the Xcode project settings and select your own Development Team for all targets before building.

5. Select the `Foodle` scheme, build, and run.
5. Select the `Findle` scheme, build, and run.

> **Note:** The Xcode project and scheme are named `Foodle` for historical reasons, but the built app is called **Findle**.
> **Note:** The Xcode project and scheme are named `Findle` for historical reasons, but the built app is called **Findle**.

### Nightly Validation

Before shipping, use the Release-like `Foodle-Nightly` scheme. It keeps optimization and hardened runtime behavior close to Release while still allowing tests to run, and produces a separate "Findle Nightly" app that can be installed side-by-side with the production build.
Before shipping, use the Release-like `Findle-Nightly` scheme. It keeps optimization and hardened runtime behavior close to Release while still allowing tests to run, and produces a separate "Findle Nightly" app that can be installed side-by-side with the production build.

```bash
xcodegen generate
xcodebuild build \
-project Foodle.xcodeproj \
-scheme Foodle-Nightly \
-project Findle.xcodeproj \
-scheme Findle-Nightly \
-configuration Nightly \
CODE_SIGN_IDENTITY="-" \
CODE_SIGNING_ALLOWED=NO
```

This builds an unsigned optimized app suitable for fast validation of optimized code paths. It is not suitable for File Provider registration or end-to-end SSO/Finder checks because the extension is not code signed.

For real pre-ship runtime validation, build a signed Nightly app from Xcode by selecting the `Foodle-Nightly` scheme with your Development Team configured, or pass it from the command line:
For real pre-ship runtime validation, build a signed Nightly app from Xcode by selecting the `Findle-Nightly` scheme with your Development Team configured, or pass it from the command line:

```bash
xcodebuild build \
-project Foodle.xcodeproj \
-scheme Foodle-Nightly \
-project Findle.xcodeproj \
-scheme Findle-Nightly \
-configuration Nightly \
DEVELOPMENT_TEAM=ABCDE12345
```
Expand All @@ -138,10 +138,10 @@ Findle is split into a modular set of frameworks and targets. This keeps the sep
| Module | Purpose |
|--------|---------|
| `SharedDomain` | Core types, models, state machines, and shared error handling. |
| `FoodleNetworking` | The Moodle API client, authentication logic, and Keychain integration. |
| `FoodlePersistence` | The SQLite database layer, metadata cache, and sync cursors. |
| `FoodleSyncEngine` | Orchestrates the sync process, computes diffs, and manages downloads. |
| `FoodleFileProvider`| The Apple File Provider extension that hooks directly into Finder. |
| `FindleNetworking` | The Moodle API client, authentication logic, and Keychain integration. |
| `FindlePersistence` | The SQLite database layer, metadata cache, and sync cursors. |
| `FindleSyncEngine` | Orchestrates the sync process, computes diffs, and manages downloads. |
| `FindleFileProvider`| The Apple File Provider extension that hooks directly into Finder. |
| `Findle` (App) | The main SwiftUI app shell, handling onboarding, diagnostics, and settings. |

### Data Flow Overview
Expand All @@ -159,8 +159,8 @@ graph LR
Reliability is maintained with unit and integration tests. You can run the test suites directly in Xcode using `Cmd+U`, or via the command line:

```bash
xcodebuild test -project Foodle.xcodeproj -scheme SharedDomainTests
xcodebuild test -project Foodle.xcodeproj -scheme PersistenceTests
xcodebuild test -project Findle.xcodeproj -scheme SharedDomainTests
xcodebuild test -project Findle.xcodeproj -scheme PersistenceTests
```

Mock API responses and test fixtures can be found in the `Fixtures/` directory.
Expand Down
2 changes: 1 addition & 1 deletion Resources/Entitlements/FileProvider.entitlements
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(FOODLE_BUNDLE_PREFIX)</string>
<string>$(AppIdentifierPrefix)$(FINDLE_BUNDLE_PREFIX)</string>
</array>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(FOODLE_BUNDLE_PREFIX)</string>
<string>$(AppIdentifierPrefix)$(FINDLE_BUNDLE_PREFIX)</string>
</array>
<key>com.apple.security.temporary-exception.mach-lookup.global-name</key>
<array>
Expand Down
86 changes: 43 additions & 43 deletions Resources/Info/App-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,48 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.education</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSUIElement</key>
<false/>
<key>SUFeedURL</key>
<string>https://github.com/alexmodrono/Findle/releases/latest/download/appcast.xml</string>
<key>SUEnableInstallerLauncherService</key>
<true/>
<key>SUPublicEDKey</key>
<string>0J4Dy2ctl0ueMVmU3Cf+/n1Dz2EO6MmwtyfqiwUnQa0=</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>es.amodrono.findle.sso-callback</string>
<key>CFBundleURLSchemes</key>
<array>
<string>findle</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
</dict>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>es.amodrono.findle.sso-callback</string>
<key>CFBundleURLSchemes</key>
<array>
<string>findle</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.education</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>
<false/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>SUEnableInstallerLauncherService</key>
<true/>
<key>SUFeedURL</key>
<string>$(FINDLE_APPCAST_URL)</string>
<key>SUPublicEDKey</key>
<string>0J4Dy2ctl0ueMVmU3Cf+/n1Dz2EO6MmwtyfqiwUnQa0=</string>
</dict>
</plist>
18 changes: 13 additions & 5 deletions Resources/Info/FileProvider-Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -34,39 +34,47 @@
<key>NSExtensionFileProviderActionActivationRule</key>
<string>TRUEPREDICATE</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>$(FOODLE_BUNDLE_PREFIX).action.open-in-moodle</string>
<string>$(FINDLE_BUNDLE_PREFIX).action.sync-now</string>
<key>NSExtensionFileProviderActionName</key>
<string>Sync Now</string>
</dict>
<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>TRUEPREDICATE</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>$(FINDLE_BUNDLE_PREFIX).action.open-in-moodle</string>
<key>NSExtensionFileProviderActionName</key>
<string>Open in Moodle</string>
</dict>
<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>TRUEPREDICATE</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>$(FOODLE_BUNDLE_PREFIX).action.copy-moodle-link</string>
<string>$(FINDLE_BUNDLE_PREFIX).action.copy-moodle-link</string>
<key>NSExtensionFileProviderActionName</key>
<string>Copy Moodle Link</string>
</dict>
<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>TRUEPREDICATE</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>$(FOODLE_BUNDLE_PREFIX).action.open-course-page</string>
<string>$(FINDLE_BUNDLE_PREFIX).action.open-course-page</string>
<key>NSExtensionFileProviderActionName</key>
<string>Open Course Page</string>
</dict>
<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>TRUEPREDICATE</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>$(FOODLE_BUNDLE_PREFIX).action.keep-downloaded</string>
<string>$(FINDLE_BUNDLE_PREFIX).action.keep-downloaded</string>
<key>NSExtensionFileProviderActionName</key>
<string>Keep Downloaded</string>
</dict>
<dict>
<key>NSExtensionFileProviderActionActivationRule</key>
<string>TRUEPREDICATE</string>
<key>NSExtensionFileProviderActionIdentifier</key>
<string>$(FOODLE_BUNDLE_PREFIX).action.remove-download</string>
<string>$(FINDLE_BUNDLE_PREFIX).action.remove-download</string>
<key>NSExtensionFileProviderActionName</key>
<string>Remove Download</string>
</dict>
Expand Down
2 changes: 1 addition & 1 deletion Sources/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import FileProvider
import OSLog

final class AppDelegate: NSObject, NSApplicationDelegate, @unchecked Sendable {
private let logger = Logger(subsystem: "es.amodrono.foodle", category: "AppDelegate")
private let logger = Logger(subsystem: "es.amodrono.findle", category: "AppDelegate")
private var windowObservers: [NSObjectProtocol] = []

func applicationDidFinishLaunching(_ notification: Notification) {
Expand Down
Loading
Loading