Skip to content

iOS App ​

The iOS companion app is a thin SwiftUI wrapper that hosts the PWA in a WKWebView and adds native push notifications with interactive actions, home screen widgets, and an Apple Watch app. A separate native Mac app hosts the same PWA on macOS. Source code is included; the apps are not on the App Store, so you build them yourself.

What It Adds ​

  • Real push notifications: APNs notifications that wake your phone, not web push
  • Interactive snooze grid: long-press a task notification for a 3x4 grid of snooze options
  • Lock screen actions: Done, +1hr, All +1hr, All → Next period, and All → <period> (one per reminder period) directly on the notification
  • Reminder period checklist: a reminder period's notification expands into a checklist of that period's reminders and quota reminders. Check the ones you did, then tap Complete N checked, or Complete all
  • Home screen widgets: Reminders, Today's Tasks, and Quotas, with check-off, snooze, and +1 buttons
  • Apple Watch app: Reminders, Tasks, and Quotas pages, a Smart Stack widget, and notification actions on your wrist
  • Home Screen quick actions: touch and hold the app icon for Snooze All +1hr, Snooze All to Next Period, Snooze All +2hr, and Snooze All to Tomorrow (your morning time). They snooze over-target tasks by the same rules as the snooze-all button, then the app opens with a "Snoozed N tasks" message and Undo
  • Cross-device sync: snoozed or completed tasks automatically dismiss notifications on other devices

See Widgets, Watch & Mac for what the widgets, the Watch app, and the Mac app do.

Prerequisites ​

  • Apple Developer Program membership ($99/year)
  • APNs configured on the server (see Notifications)
  • Xcode 26 or later (the widgets use the iOS 26 / watchOS 26 / macOS 26 SDKs for widget push), with the watchOS SDK installed
  • xcodegen
  • iPhone on iOS 17 or later; Apple Watch on watchOS 10 or later
  • iOS 26 / watchOS 26 / macOS 26 for instant widget sync (see Widget sync). Older versions still work but refresh on a schedule.

Building ​

The iOS project uses xcodegen to generate the Xcode project from project.yml:

bash
cd ios
xcodegen generate
open OpenTask.xcodeproj

Use your own team and identifiers ​

The project files contain the original author's Apple Developer team ID, bundle ID prefix, App Group, and associated domain. Replace them with your own before you build:

  1. In ios/project.yml (and macos/project.yml if you build the Mac app), set DEVELOPMENT_TEAM to your team ID, and change bundleIdPrefix and every PRODUCT_BUNDLE_IDENTIFIER to your own reverse-DNS prefix (for example com.example.opentask).
  2. Change the App Group (group.<your prefix>) and the webcredentials: associated domain in the entitlements sections to match your prefix and your server's domain.
  3. Some Swift files also contain the App Group, Keychain access group, and bundle IDs as string constants. Search ios/ and macos/ for the old prefix and team ID and replace every match.
  4. Set the server's APNS_BUNDLE_ID to your main app's bundle ID (see Notifications).

Each embedded extension's bundle ID must start with its parent app's bundle ID (for example com.example.opentask.widgets under com.example.opentask).

Simulator builds without the watchOS SDK

Generating and building project.yml requires the watchOS SDK. To build only the iPhone targets for the simulator, generate the watch-free variant with xcodegen generate --spec project-sim.yml. It produces OpenTaskSim.xcodeproj.

Targets ​

TargetPlatformDescription
OpenTaskiOSMain app: WKWebView host, first-launch setup, APNs registration, Home Screen quick actions
OpenTaskNotificationiOSNotification content extension: the snooze grid and the reminder period checklist on long-press
OpenTaskWidgetsiOSHome screen and Lock Screen widgets (Reminders, Today's Tasks, Quotas)
OpenTaskWatchwatchOSApple Watch app: Reminders, Tasks, and Quotas pages, plus notification actions
OpenTaskWatchWidgetswatchOSSmart Stack / watch face widget, embedded in the Watch app

Building OpenTask embeds the notification extension, the widget extension, and the Watch app. Installing the iPhone app installs the Watch app on a paired watch.

Mac app ​

The Mac app is a separate, native macOS project in macos/. It is not a Mac Catalyst build of the iPhone app. It requires macOS 14 or later.

bash
cd macos
xcodegen generate
open OpenTaskMac.xcodeproj

It uses the same bundle IDs as the iPhone app and its widgets, so macOS treats them as one app. Update macos/project.yml with your own team ID and identifiers as described above. On macOS, the App Group and Keychain access group are prefixed with your team ID.

Push on the Mac

The default Mac build is signed without the push entitlement. To receive notifications, build with the push variant of the entitlements file (OpenTaskMac/OpenTaskMac-Push.entitlements), for example by passing CODE_SIGN_ENTITLEMENTS=OpenTaskMac/OpenTaskMac-Push.entitlements -allowProvisioningUpdates to xcodebuild. See macos/README.md in the repository for details.

First-Run Setup ​

When the app launches for the first time:

  1. Enter your server URL (for example https://tasks.example.com). Without a scheme, https:// is assumed; type http:// for a plain-HTTP server. The app checks that the server's login page is reachable.
  2. Sign in to OpenTask in the app with your username and password.
  3. Allow notifications when prompted.

Once the app has a push token and you are signed in, the web app automatically creates a Bearer token for the device and stores it in the Keychain. You don't create or paste a token by hand. Notification actions, widgets, and the Watch app all use this token.

Notifications are required for widgets and the Watch

The Bearer token is only provisioned after the app receives an APNs device token, and the app only registers for one after you allow notifications. If you decline notifications, or the app is signed without the push entitlement, notification actions, widgets, and the Watch app stay unconnected.

The main app, the notification extension, and the widget extension share credentials through the App Group Keychain. The iPhone app sends them to the Watch app over WatchConnectivity. If the Watch shows Not Connected, open OpenTask on your iPhone.

The Mac app follows the same flow: enter the server URL, sign in, and allow notifications.

Server Endpoints Used ​

The apps and their extensions call these endpoints with the device's Bearer token:

EndpointPurpose
POST /api/push/apns/registerDevice token registration
DELETE /api/push/apns/registerDevice token unregistration
POST /api/push/apns/widget-tokenWidget push token registration (iOS/macOS/watchOS 26)
POST /api/notifications/actionsDone/snooze/delete from notification actions
PATCH /api/tasks/{id}Snooze to a specific time (notification snooze grid)
POST /api/tasks/bulk/snooze-overdueBulk snooze from notifications, widgets, the Watch, and the Mac menu bar item (including slot, to snooze to a reminder period)
POST /api/tasks/bulk/snoozePer-task and selected-task snooze from the Tasks widget, the Watch, and the Mac menu bar item
POST /api/tasks/bulk/completeComplete items (reminder checklist, widget bulk select, the Mac menu bar item), including quota reminders through prompts
POST /api/quota-prompts/consider, POST /api/quota-prompts/didQuota reminders' dashed circle and square on the Reminders widget, the Watch, and the Smart Stack card
POST /api/quota-prompts/restorePut back a handled quota reminder from the Reminders widget's completed list
POST /api/tasks/{id}/progressQuota +1 / −1 from the Quotas widget and the Watch
POST /api/tasks/{id}/undoneRestore a completed item from a widget's completed list
GET /api/tasksOpen tasks and quotas for the widgets, the Watch, and the Mac menu bar item
POST /api/tasksQuick add from the Mac menu bar item
GET /api/remindersReminders and quota reminders grouped by period, for the widgets, the Watch, and the checklist
POST /api/task-segments/consider, /defer, /target-todayA task segment row's circle, Defer all, and Target today on the Reminders widget, the Watch, and the Smart Stack card
GET /api/tasks/countsThe app icon and Dock badge
GET /api/time-slotsReminder periods, cached for the period snooze actions
GET /api/completionsToday's completed items, for a widget's completed list
GET /api/projectsProject names and colors for the Tasks widget
GET /api/user/preferencesLabel colors for the Quotas widget and the Watch
POST /api/undo, POST /api/redo, GET /api/undo/statusWidget and Watch undo/redo
POST /api/auth/session-from-tokenRestore an expired web session in the app using the Bearer token