Next steps: widgets, watchOS, visionOS and the Swift ecosystem

Add extension targets correctly, share code across Apple platforms, depend on Swift packages safely, and choose where to take the project next.

Extension targets and shared containers

  • A widget, watch app or share extension is a separate process with its own binary. It cannot reach the app's in-memory state.
  • Share data through an App Group: give both targets the same group identifier and use FileManager.containerURL(forSecurityApplicationGroupIdentifier:).
  • Widgets get a strict memory budget and are killed if they exceed it. Return a small, pre-computed snapshot.
  • Extension background refreshes are scheduled by the system; treat every refresh as best-effort, never as a timer.
import WidgetKit
import SwiftUI

struct Snapshot: TimelineEntry {
    let date: Date
    let openCount: Int
}

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> Snapshot {
        Snapshot(date: .now, openCount: 3)
    }

    func getSnapshot(in context: Context, completion: @escaping (Snapshot) -> Void) {
        completion(Snapshot(date: .now, openCount: SharedStore.openCount))
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<Snapshot>) -> Void) {
        let entry = Snapshot(date: .now, openCount: SharedStore.openCount)
        let next = Calendar.current.date(byAdding: .hour, value: 1, to: .now)!
        completion(Timeline(entries: [entry], policy: .after(next)))
    }
}

Dependencies and cross-platform Swift

// Package.swift for a shared module used by the app and the watch app
// swift-tools-version: 6.0
import PackageDescription

let package = Package(
    name: "Core",
    platforms: [.iOS(.v17), .watchOS(.v10), .macOS(.v14)],
    products: [
        .library(name: "Core", targets: ["Core"])
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-log.git", from: "1.6.0")
    ],
    targets: [
        .target(name: "Core", dependencies: [.product(name: "Logging", package: "swift-log")]),
        .testTarget(name: "CoreTests", dependencies: ["Core"])
    ]
)
TargetReusesCannot
iOS appCore, design system, networkingwatchOS-only APIs
watch appCore, simplified viewsHeavy background work
visionOSCore, most SwiftUIAssumptions about a flat window
Server (Vapor)Core, Codable modelsUIKit or SwiftUI

Pin dependencies with a resolved file committed to the repository and update deliberately. A floating branch dependency turns someone else's force-push into your broken build.

Choosing what to learn next

// A design system is the highest-leverage shared package
public struct PrimaryButton: View {
    public let title: String
    public let action: () -> Void

    public init(title: String, action: @escaping () -> Void) {
        self.title = title
        self.action = action
    }

    public var body: some View {
        Button(title, action: action)
            .buttonStyle(.borderedProminent)
            .controlSize(.large)
    }
}
💡
The fastest way to grow is to ship something small end to end — a widget, a watch complication, a server endpoint in Vapor — rather than reading about each platform. Each one exercises the same core Swift skills you already have.

FAQ

Should I make an app universal or add a separate watch app?
Add the watch app only when the interaction genuinely benefits from the wrist — glanceable data or a quick action. Porting the whole app usually produces a slow, cramped experience.
How do I know a Swift package is safe to depend on?
Check that it is actively maintained, has a version tag rather than only a branch, ships tests, and has a clear licence. Then pin it by version range and review the resolved file when it changes.

Localization, accessibility and system integration Release engineering: TestFlight, CI and App Store Connect

Last refreshed 2026-09-18.