Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2025 App Services

WWDC25 · 18 min · App Services

Wake up to the AlarmKit API

Rrrr-rrrrr-innng! From countdown timers in your recipe app to wake-up alarms in your travel planning app, the AlarmKit framework in iOS and iPadOS 26 brings timers and alarms to the Lock Screen, Dynamic Island, and more. Learn how to create and manage your app’s alarms, customize their Live Activities, and offer custom alert actions using the App Intents framework. To get the most from this video, we recommend first watching “Meet ActivityKit” from WWDC23.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

Code shown on screen · 14 snippets

Check authorization status swift · at 2:41 ↗
// Check authorization status

import AlarmKit

func checkAuthorization() {

  switch AlarmManager.shared.authorizationState {
    case .notDetermined:
      // Manually request authorization
    case .authorized:
      // Proceed with scheduling
    case .denied:
      // Inform status is not authorized
  }
  
}
Set up the countdown duration swift · at 4:08 ↗
// Set up the countdown duration

import AlarmKit

func scheduleAlarm() {

  /* ... */

  let countdownDuration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))

  /* ... */
}
Set a fixed schedule swift · at 4:40 ↗
// Set a fixed schedule

import AlarmKit

func scheduleAlarm() {

  /* ... */

  let keynoteDateComponents = DateComponents(
    calendar: .current,
    year: 2025,
    month: 6,
    day: 9,
    hour: 9,
    minute: 41)
  let keynoteDate = Calendar.current.date(from: keynoteDateComponents)!
  let scheduleFixed = Alarm.Schedule.fixed(keynoteDate)

  /* ... */

}
Set a relative schedule swift · at 5:13 ↗
// Set a relative schedule

import AlarmKit

func scheduleAlarm() {

  /* ... */

  let time = Alarm.Schedule.Relative.Time(hour: 7, minute: 0)
  let recurrence = Alarm.Schedule.Relative.Recurrence.weekly([
    .monday,
    .wednesday,
    .friday
  ])
  
  let schedule = Alarm.Schedule.Relative(time: time, repeats: recurrence)

  /* ... */

}
Set up alert appearance with dismiss button swift · at 5:43 ↗
// Set up alert appearance with dismiss button

import AlarmKit

func scheduleAlarm() async throws {
    typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
    
    let id = UUID()
    let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
    
    let stopButton = AlarmButton(
        text: "Dismiss",
        textColor: .white,
        systemImageName: "stop.circle")
    
    let alertPresentation = AlarmPresentation.Alert(
        title: "Food Ready!",
        stopButton: stopButton)
    
    let attributes = AlarmAttributes<CookingData>(
        presentation: AlarmPresentation(
            alert: alertPresentation),
        tintColor: Color.green)
    
    let alarmConfiguration = AlarmConfiguration(
        countdownDuration: duration,
        attributes: attributes)
    
    try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
}
Set up alert appearance with repeat button swift · at 7:17 ↗
// Set up alert appearance with repeat button

import AlarmKit

func scheduleAlarm() async throws {
    typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
    
    let id = UUID()
    let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
    
    let stopButton = AlarmButton(
        text: "Dismiss",
        textColor: .white,
        systemImageName: "stop.circle")
    
    let repeatButton = AlarmButton(
        text: "Repeat",
        textColor: .white,
        systemImageName: "repeat.circle")
    
    let alertPresentation = AlarmPresentation.Alert(
        title: "Food Ready!",
        stopButton: stopButton,
        secondaryButton: repeatButton,
        secondaryButtonBehavior: .countdown)
    
    let attributes = AlarmAttributes<CookingData>(
        presentation: AlarmPresentation(alert: alertPresentation),
        tintColor: Color.green)
    
    let alarmConfiguration = AlarmConfiguration(
        countdownDuration: duration,
        attributes: attributes)
    
    try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
}
Create a Live Activity for a countdown swift · at 9:15 ↗
// Create a Live Activity for a countdown

import AlarmKit
import ActivityKit
import WidgetKit

struct AlarmLiveActivity: Widget {

  var body: some WidgetConfiguration {
    ActivityConfiguration(for: AlarmAttributes<CookingData>.self) { context in

      switch context.state.mode {
      case .countdown:
        countdownView(context)
      case .paused:
        pausedView(context)
      case .alert:
        alertView(context)
      }

    } dynamicIsland: { context in 

      DynamicIsland {
        DynamicIslandExpandedRegion(.leading) {
          leadingView(context)
        }
        DynamicIslandExpandedRegion(.trailing) {
          trailingView(context)
        }
      } compactLeading: {
        compactLeadingView(context)
      } compactTrailing: {
        compactTrailingView(context)
      } minimal: {
        minimalView(context)
      }

    }
  }
}
Create custom metadata for the Live Activity swift · at 10:26 ↗
// Create custom metadata for the Live Activity

import AlarmKit

struct CookingData: AlarmMetadata {
  let method: Method
    
  init(method: Method) {
    self.method = method
  }
    
  enum Method: String, Codable {
    case frying = "frying.pan"
    case grilling = "flame"
  }
}
Provide custom metadata to the Live Activity swift · at 10:43 ↗
// Provide custom metadata to the Live Activity

import AlarmKit

func scheduleAlarm() async throws {
    typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
    
    let id = UUID()
    let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
    let customMetadata = CookingData(method: .frying)
    
    let stopButton = AlarmButton(
        text: "Dismiss",
        textColor: .white,
        systemImageName: "stop.circle")
    
    let repeatButton = AlarmButton(
        text: "Repeat",
        textColor: .white,
        systemImageName: "repeat.circle")
    
    let alertPresentation = AlarmPresentation.Alert(
        title: "Food Ready!",
        stopButton: stopButton,
        secondaryButton: repeatButton,
        secondaryButtonBehavior: .countdown)
    
    let attributes = AlarmAttributes<CookingData>(
        presentation: AlarmPresentation(alert: alertPresentation),
        metadata: customMetadata,
        tintColor: Color.green)
    
    let alarmConfiguration = AlarmConfiguration(
        countdownDuration: duration,
        attributes: attributes)
    
    try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
}
Use custom metadata in the Live Activity swift · at 11:01 ↗
// Use custom metadata in the Live Activity

import AlarmKit
import ActivityKit
import WidgetKit

struct AlarmLiveActivity: Widget {

  var body: some WidgetConfiguration { /* ... */ }

  func alarmIcon(context: ActivityViewContext<AlarmAttributes<CookingData>>) -> some View {
    let method = context.attributes.metadata?.method ?? .grilling
    return Image(systemName: method.rawValue)
  }

}
Set up the system countdown appearance swift · at 12:03 ↗
// Set up the system countdown appearance

import AlarmKit

func scheduleAlarm() async throws {
  typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
    
  let id = UUID()
  let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
  let customMetadata = CookingData(method: .frying)

  let stopButton = AlarmButton(
    text: "Dismiss",
    textColor: .white,
    systemImageName: "stop.circle")

  let repeatButton = AlarmButton(
    text: "Repeat",
    textColor: .white,
    systemImageName: "repeat.circle")

  let alertPresentation = AlarmPresentation.Alert(
    title: "Food Ready!",
    stopButton: stopButton,
    secondaryButton: repeatButton,
    secondaryButtonBehavior: .countdown)

  let pauseButton = AlarmButton(
    text: "Pause",
    textColor: .green,
    systemImageName: "pause")

  let countdownPresentation = AlarmPresentation.Countdown(
    title: "Cooking",
    pauseButton: pauseButton)

  let attributes = AlarmAttributes<CookingData>(
    presentation: AlarmPresentation(
      alert: alertPresentation,
      countdown: countdownPresentation),
    metadata: customMetadata,
    tintColor: Color.green)

  let alarmConfiguration = AlarmConfiguration(
    countdownDuration: duration,
    attributes: attributes)

  try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
  
}
Set up the system paused appearance swift · at 12:43 ↗
// Set up the system paused appearance

import AlarmKit

func scheduleAlarm() async throws {
  typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
    
  let id = UUID()
  let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
  let customMetadata = CookingData(method: .frying)

  let stopButton = AlarmButton(
    text: "Dismiss",
    textColor: .white,
    systemImageName: "stop.circle")

  let repeatButton = AlarmButton(
    text: "Repeat",
    textColor: .white,
    systemImageName: "repeat.circle")

  let alertPresentation = AlarmPresentation.Alert(
    title: "Food Ready!",
    stopButton: stopButton,
    secondaryButton: repeatButton,
    secondaryButtonBehavior: .countdown)

  let pauseButton = AlarmButton(
    text: "Pause",
    textColor: .green,
    systemImageName: "pause")

  let countdownPresentation = AlarmPresentation.Countdown(
    title: "Cooking",
    pauseButton: pauseButton)

  let resumeButton = AlarmButton(
    text: "Resume",
    textColor: .green,
    systemImageName: "play")

  let pausedPresentation = AlarmPresentation.Paused(
    title: "Paused",
    resumeButton: resumeButton)

  let attributes = AlarmAttributes<CookingData>(
    presentation: AlarmPresentation(
      alert: alertPresentation,
      countdown: countdownPresentation,
      paused: pausedPresentation),
    metadata: customMetadata,
    tintColor: Color.green)

  let alarmConfiguration = AlarmConfiguration(
    countdownDuration: duration,
    attributes: attributes)

  try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
  
}
Add a custom button swift · at 14:09 ↗
// Add a custom button

import AlarmKit
import AppIntents

func scheduleAlarm() async throws {
  typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
    
  let id = UUID()
  let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
  let customMetadata = CookingData(method: .frying)
  let secondaryIntent = OpenInApp(alarmID: id.uuidString)

  let stopButton = AlarmButton(
    text: "Dismiss",
    textColor: .white,
    systemImageName: "stop.circle")

  let openButton = AlarmButton(
    text: "Open",
    textColor: .white,
    systemImageName: "arrow.right.circle.fill")

  let alertPresentation = AlarmPresentation.Alert(
    title: "Food Ready!",
    stopButton: stopButton,
    secondaryButton: openButton,
    secondaryButtonBehavior: .custom)

  let pauseButton = AlarmButton(
    text: "Pause",
    textColor: .green,
    systemImageName: "pause")

  let countdownPresentation = AlarmPresentation.Countdown(
    title: "Cooking",
    pauseButton: pauseButton)

  let resumeButton = AlarmButton(
    text: "Resume",
    textColor: .green,
    systemImageName: "play")

  let pausedPresentation = AlarmPresentation.Paused(
    title: "Paused",
    resumeButton: resumeButton)

  let attributes = AlarmAttributes<CookingData>(
    presentation: AlarmPresentation(
      alert: alertPresentation,
      countdown: countdownPresentation,
      paused: pausedPresentation),
    metadata: customMetadata,
    tintColor: Color.green)

  let alarmConfiguration = AlarmConfiguration(
    countdownDuration: duration,
    attributes: attributes,
    secondaryIntent: secondaryIntent)

  try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
  
}

public struct OpenInApp: LiveActivityIntent {
    public func perform() async throws -> some IntentResult { .result() }
    
    public static var title: LocalizedStringResource = "Open App"
    public static var description = IntentDescription("Opens the Sample app")
    public static var openAppWhenRun = true
    
    @Parameter(title: "alarmID")
    public var alarmID: String
    
    public init(alarmID: String) {
        self.alarmID = alarmID
    }
    
    public init() {
        self.alarmID = ""
    }
}
Add a custom sound swift · at 16:10 ↗
// Add a custom sound

import AlarmKit
import AppIntents

func scheduleAlarm() async throws {
  typealias AlarmConfiguration = AlarmManager.AlarmConfiguration<CookingData>
  
  let id = UUID()
  let duration = Alarm.CountdownDuration(preAlert: (10 * 60), postAlert: (5 * 60))
  let customMetadata = CookingData(method: .frying)
  let secondaryIntent = OpenInApp(alarmID: id.uuidString)

  let stopButton = AlarmButton(
    text: "Dismiss",
    textColor: .white,
    systemImageName: "stop.circle")

  let openButton = AlarmButton(
    text: "Open",
    textColor: .white,
    systemImageName: "arrow.right.circle.fill")

  let alertPresentation = AlarmPresentation.Alert(
    title: "Food Ready!",
    stopButton: stopButton,
    secondaryButton: openButton,
    secondaryButtonBehavior: .custom)

  let pauseButton = AlarmButton(
    text: "Pause",
    textColor: .green,
    systemImageName: "pause")

  let countdownPresentation = AlarmPresentation.Countdown(
    title: "Cooking",
    pauseButton: pauseButton)

  let resumeButton = AlarmButton(
    text: "Resume",
    textColor: .green,
    systemImageName: "play")

  let pausedPresentation = AlarmPresentation.Paused(
    title: "Paused",
    resumeButton: resumeButton)

  let attributes = AlarmAttributes<CookingData>(
    presentation: AlarmPresentation(
      alert: alertPresentation,
      countdown: countdownPresentation,
      paused: pausedPresentation),
    metadata: customMetadata,
    tintColor: Color.green)

  let sound = AlertConfiguration.AlertSound.named("Chime")

  let alarmConfiguration = AlarmConfiguration(
    countdownDuration: duration,
    attributes: attributes,
    secondaryIntent: secondaryIntent,
    sound: sound)

  try await AlarmManager.shared.schedule(id: id, configuration: alarmConfiguration)
  
}

public struct OpenInApp: LiveActivityIntent {
    public func perform() async throws -> some IntentResult { .result() }
    
    public static var title: LocalizedStringResource = "Open App"
    public static var description = IntentDescription("Opens the Sample app")
    public static var openAppWhenRun = true
    
    @Parameter(title: "alarmID")
    public var alarmID: String
    
    public init(alarmID: String) {
        self.alarmID = alarmID
    }
    
    public init() {
        self.alarmID = ""
    }
}

Resources