Setup and context
Apple Watch is the world's most popular smartwatch, and it's used daily for health tracking, fitness monitoring, notifications, and more. However, traditional watchOS development has historically been challenging — setting up Xcode, configuring WatchKit Extensions, and designing iPhone-Watch communication required significant expertise.
With Rork Max, you can leverage AI to build SwiftUI-based Apple Watch companion apps efficiently. This guide walks you through everything from watchOS project setup to iPhone data synchronization, HealthKit integration, and complication (watch face widget) implementation.
Who this guide is for:
- Developers who've learned the basics of Rork app development
- Anyone looking to extend their iPhone app to Apple Watch
- Indie developers seeking differentiation through wearable support
For a foundational understanding of Rork Max, check out the Rork Max Native Swift Complete Guide.
Understanding watchOS App Architecture
Apple Watch apps are built around two primary architectures.
Independent Apps: These run entirely on Apple Watch without requiring an iPhone. Supported since watchOS 6, users can download them directly from the App Store on their watch.
Companion Apps: These work alongside an iPhone app, enabling bidirectional data sync and remote control features. Most commercial apps use this approach for richer functionality.
Rork Max supports both architectures, but this guide focuses on the most practical setup: an iPhone app with a Watch companion app.
watchOS Project Directory Structure
When you add a watchOS target in Rork Max, the following structure is automatically generated:
MyApp/
├── MyApp/ # iPhone app
│ ├── ContentView.swift
│ └── MyAppApp.swift
├── MyAppWatch/ # Watch app
│ ├── MyAppWatchApp.swift # Watch app entry point
│ ├── ContentView.swift # Watch main screen
│ ├── ComplicationController.swift
│ └── Assets.xcassets
└── Shared/ # Code shared between iPhone and Watch
├── Models/
└── Services/
Understanding this structure helps you write more precise prompts for Rork Max, resulting in higher quality generated code.
Creating a watchOS Project in Rork Max
Step 1: Initialize the Project
Create a new project in Rork Max and specify that you want a watchOS target included.
// Example Rork Max prompt:
// "Create a project with an iPhone app and Apple Watch companion app.
// The Watch app should display a task list with completion toggles."
// Generated Watch app entry point
import SwiftUI
@main
struct MyAppWatchApp: App {
var body: some Scene {
WindowGroup {
TaskListView()
}
}
}Step 2: Build the Watch UI with SwiftUI
watchOS interfaces are built with SwiftUI. Since screen real estate is limited, prioritizing information is crucial.
// TaskListView.swift — Watch task list screen
import SwiftUI
struct TaskListView: View {
@StateObject private var viewModel = TaskViewModel()
var body: some View {
NavigationStack {
List {
ForEach(viewModel.tasks) { task in
TaskRow(task: task) {
viewModel.toggleComplete(task)
}
}
}
.navigationTitle("Tasks")
}
.onAppear {
viewModel.fetchTasks()
}
}
}
struct TaskRow: View {
let task: TaskItem
let onToggle: () -> Void
var body: some View {
Button(action: onToggle) {
HStack {
Image(systemName: task.isCompleted
? "checkmark.circle.fill"
: "circle")
.foregroundColor(task.isCompleted ? .green : .gray)
Text(task.title)
.strikethrough(task.isCompleted)
.lineLimit(2)
}
}
}
}
// Expected output:
// A task list displayed on Apple Watch where tapping
// toggles the completion state of each taskKey design considerations for Watch UI: keep tap targets large enough for comfortable interaction, and limit the amount of information displayed on each screen.
Implementing iPhone–Apple Watch Data Sync
The defining feature of a companion app is bidirectional data synchronization with the iPhone. Apple provides the WCSession (Watch Connectivity) framework for real-time messaging and background data transfers.
Real-Time Communication with WCSession
// Shared/Services/WatchConnectivityManager.swift
import WatchConnectivity
class WatchConnectivityManager: NSObject, ObservableObject, WCSessionDelegate {
static let shared = WatchConnectivityManager()
@Published var receivedTasks: [TaskItem] = []
private override init() {
super.init()
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
}
// Send tasks from iPhone to Watch
func sendTasks(_ tasks: [TaskItem]) {
guard WCSession.default.isReachable else { return }
let data = tasks.map { [
"id": $0.id.uuidString,
"title": $0.title,
"isCompleted": $0.isCompleted
] as [String: Any] }
WCSession.default.sendMessage(
["tasks": data],
replyHandler: nil
) { error in
print("Send error: \(error.localizedDescription)")
}
}
// Receive handler
func session(
_ session: WCSession,
didReceiveMessage message: [String: Any]
) {
if let tasksData = message["tasks"] as? [[String: Any]] {
DispatchQueue.main.async {
self.receivedTasks = tasksData.compactMap {
TaskItem(from: $0)
}
}
}
}
// Required delegate methods
func session(
_ session: WCSession,
activationDidCompleteWith state: WCSessionActivationState,
error: Error?
) {}
#if os(iOS)
func sessionDidBecomeInactive(_ session: WCSession) {}
func sessionDidDeactivate(_ session: WCSession) {
WCSession.default.activate()
}
#endif
}
// Expected output:
// Tasks added or updated on iPhone sync to Watch in real time
// Tasks completed on Watch sync back to iPhonePersistent Sync with Application Context
While sendMessage works great for real-time communication, messages won't be delivered if the Watch app is inactive. updateApplicationContext persists the latest data so the Watch can retrieve the current state whenever it wakes up.
// Background-compatible data sync
func syncTasksInBackground(_ tasks: [TaskItem]) {
let context: [String: Any] = [
"tasks": tasks.map { $0.toDictionary() },
"lastUpdated": Date().timeIntervalSince1970
]
do {
try WCSession.default.updateApplicationContext(context)
} catch {
print("Context update error: \(error.localizedDescription)")
}
}
// Receiving on the Watch side
func session(
_ session: WCSession,
didReceiveApplicationContext context: [String: Any]
) {
if let tasksData = context["tasks"] as? [[String: Any]] {
DispatchQueue.main.async {
self.receivedTasks = tasksData.compactMap {
TaskItem(from: $0)
}
}
}
}Integrating HealthKit Data into Your Watch App
One of Apple Watch's greatest strengths is real-time access to health data. By integrating HealthKit, you can build apps that leverage heart rate, step count, workout data, and more.
// Shared/Services/HealthKitManager.swift
import HealthKit
class HealthKitManager: ObservableObject {
private let healthStore = HKHealthStore()
@Published var todaySteps: Int = 0
@Published var currentHeartRate: Double = 0
// Request HealthKit authorization
func requestAuthorization() async throws {
let typesToRead: Set<HKObjectType> = [
HKQuantityType(.stepCount),
HKQuantityType(.heartRate),
HKQuantityType(.activeEnergyBurned)
]
try await healthStore.requestAuthorization(
toShare: [],
read: typesToRead
)
}
// Fetch today's step count
func fetchTodaySteps() async {
let stepsType = HKQuantityType(.stepCount)
let startOfDay = Calendar.current.startOfDay(for: Date())
let predicate = HKQuery.predicateForSamples(
withStart: startOfDay,
end: Date(),
options: .strictStartDate
)
let descriptor = HKStatisticsQueryDescriptor(
predicate: .init(
quantityType: stepsType,
predicate: predicate
),
options: .cumulativeSum
)
do {
let result = try await descriptor.result(for: healthStore)
let steps = result?.sumQuantity()?
.doubleValue(for: .count()) ?? 0
await MainActor.run {
self.todaySteps = Int(steps)
}
} catch {
print("Step count fetch error: \(error.localizedDescription)")
}
}
}
// Expected output:
// The Watch app displays "Today's Steps: 8,432" on screenApps using HealthKit require a clear privacy policy during App Store review. For detailed review preparation, the App Store Review Complete Checklist for Rork Apps is an invaluable resource.
Building Complications (Watch Face Widgets)
Complications place small widgets directly on the watch face, letting users see key information at a glance without opening the app. This significantly boosts user engagement and daily active usage.
// ComplicationProvider.swift
import WidgetKit
import SwiftUI
struct TaskComplicationEntry: TimelineEntry {
let date: Date
let remainingTasks: Int
let nextTask: String?
}
struct TaskComplicationProvider: TimelineProvider {
func placeholder(in context: Context) -> TaskComplicationEntry {
TaskComplicationEntry(
date: Date(),
remainingTasks: 3,
nextTask: "Sample task"
)
}
func getSnapshot(
in context: Context,
completion: @escaping (TaskComplicationEntry) -> Void
) {
let entry = TaskComplicationEntry(
date: Date(),
remainingTasks: 5,
nextTask: "Submit proposal"
)
completion(entry)
}
func getTimeline(
in context: Context,
completion: @escaping (Timeline<TaskComplicationEntry>) -> Void
) {
// Fetch actual task data and build timeline
let tasks = TaskStorage.shared.incompleteTasks
let entry = TaskComplicationEntry(
date: Date(),
remainingTasks: tasks.count,
nextTask: tasks.first?.title
)
let nextUpdate = Calendar.current.date(
byAdding: .minute, value: 30, to: Date()
)!
let timeline = Timeline(
entries: [entry],
policy: .after(nextUpdate)
)
completion(timeline)
}
}
// Complication display view
struct TaskComplicationView: View {
let entry: TaskComplicationEntry
var body: some View {
VStack(alignment: .leading) {
Text("\(entry.remainingTasks)")
.font(.title2)
.fontWeight(.bold)
Text("Tasks left")
.font(.caption2)
.foregroundColor(.secondary)
}
.containerBackground(.fill.tertiary, for: .widget)
}
}
// Expected output:
// A complication on the watch face showing "5 Tasks left"Summary
Here's a recap of the key points for building Apple Watch companion apps with Rork Max:
- watchOS app structure: Design around three layers — iPhone app, Watch target, and Shared code
- SwiftUI-based Watch UI: Keep interfaces simple and optimized for the small screen
- WCSession data sync: Use
sendMessagefor real-time updates andupdateApplicationContextfor persistent state - HealthKit integration: Leverage step counts, heart rate, and workout data to add genuine value
- Complications: Embed your app into the watch face for maximum daily engagement
Adding Apple Watch support is a powerful differentiator on the App Store. While many indie developers haven't yet ventured into watchOS, now is the perfect time to use Rork Max's AI code generation to move quickly and capture this opportunity.
For step-by-step instructions on publishing your Watch app, see Rork Max 2-Click App Store Publishing Guide.