Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AlarmKitで実現する 新時代のシステム通知

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Ryo Tsuzukihashi Ryo Tsuzukihashi
September 19, 2025

AlarmKitで実現する 新時代のシステム通知

サードパーティアプリ開発者は長年、Appleの純正アラーム機能に制約を感じてきました。
Silent modeやFocus modeに阻まれ、重要な通知が届かないリスクは、開発者とユーザー双方にとって深刻な問題でした。

iOS 26のAlarmKitは、この技術的障壁をついに解放し、開発者にも革命的なアラーム機能の実装を可能にします。
従来のローカル通知の根本的問題は、デバイス設定への依存性でした。
AlarmKitを使用することで、重要な通知がどんな状況でもユーザーに確実に届くようになります。

AlarmKitの主な革新として以下の特徴が挙げられます。

1)あらゆるシステム設定を突破するアラーム実行により、Silent modeやFocus modeの影響を受けない確実な通知を実現
2)Live Activitiesと連携したDynamic Island統合により、ユーザーの目に留まりやすい視覚的なアラーム表示を提供
3)Apple Watchでの自動同期により、デバイスを問わない一貫したアラーム機能
4)App Intentsによるカスタムアクション実行により、アラーム後の独自ワークフローを組み込み可能

実装デモを行い、料理タイマーアプリを題材に段階的な構築過程をお見せします。
AlarmKitの登場により、開発者はユーザーにとってより信頼性の高いアラーム機能を提供できるようになります。
https://fortee.jp/iosdc-japan-2025/proposal/439dad47-8495-4f7e-8ee0-e244bb1e7667

Avatar for Ryo Tsuzukihashi

Ryo Tsuzukihashi

September 19, 2025
Tweet

More Decks by Ryo Tsuzukihashi

Other Decks in Technology

Transcript

  1. ࣗݾ঺հ ᠃ڮྋ !UTV[VLJ ʮࢥ͍ग़ͱςΫϊϩδʔͰ৽͍͠ମݧΛ૑଄͢Δʯ ݸਓ։ൃؤுͬͯΔ ୅ද࡞඼ ཱྀߦࢥ͍ग़Ϛοϓ ݅ ߴධ ສ%-ཱྀͷࢥ͍ग़Λ஍ਤʹه࿥͢ΔΞϓϦ

    ྲྀΕΔϝϞா ສ%- ϝσΟΞ঺հଟ਺৽ײ֮ͷϝϞΞϓϦ ͍ͭͰ΋λΠϚʔ ਆΞϓϦड৆ -JWF"DUJWJUZରԠͷλΠϚʔΞϓϦ גࣜձࣾ;0;0 ;0;0508/J04։ൃ ྺ࢙͋Δେن໛ΞϓϦΛҰॹʹΑ͍ͯ͘͘͠஥ؒืूத ͜Ε·ͰݸਓͰϦϦʔεͨ͠ΞϓϦ͸ݸ
  2. AlarmKitొ৔ J04ͷֵ໋ ৽࣌୅ͷγεςϜ௨஌ϑϨʔϜϫʔΫ iOS 26 / iPadOS 26ରԠ Silent Mode

    / Focus Modeಥഁ ॏཁͳ௨஌Λ࣮֬ʹϢʔβʔʹಧ͚Δ ΧελϜΞϥʔϜͱλΠϚʔ ΧελϚΠζՄೳͳεέδϡʔϧͱ6* ճݶΓɺ܁Γฦ͠ɺΧ΢ϯτμ΢ϯɺεψʔζػೳ ֵ໋తͳΞϥʔϜػೳ λΠϚʔ ఆظΞϥʔϜ εψʔζ
  3. / / UIଐੑͷఆٛ let stopButton = AlarmButton( text: "͓͸Α͏", textColor:

    Color.blue, systemImageName: "checkmark.circle" ) let alertPresentation = AlarmPresentation.Alert( title: "ࠓ೔͸iOSDCʂى͖ͳ͍ͱ஗ࠁ͢Δͧʂʂʂ", stopButton: stopButton ) / / ͦΕҎ֎ͷݟͨ໨ͷ࡞੒ let attributes = AlarmAttributes( presentation: AlarmPresentation(alert: alertPresentation), metadata: SampleAlarmMetadata(), tintColor: Color.orange ) / / εέδϡʔϧͷఆٛɹྫ: ݻఆ೔࣌ͷΞϥʔϜ let schedule = Alarm.Schedule.fixed(Date().addingTimeInterval(3)) let alarmConfiguration = AlarmManager.AlarmConfiguration<SampleAlarmMetadata> .alarm( schedule: schedule, attributes: attributes ) / / εέδϡʔϧ࣮ߦ _ = try? await AlarmManager.shared.schedule(id: UUID(), configuration: alarmConfiguration) γϯϓϧͳΞϥʔϜ࣮૷ ΞϥʔϜϑϩʔ ΞϥʔϜఆٛ γεςϜ΁ొ࿥ εέδϡʔϧ࣮ߦ Ξϥʔτදࣔ
  4. // UIଐੑͷఆٛ let stopButton = AlarmButton( text: "͓͸Α͏", textColor: Color.blue,

    systemImageName: "checkmark.circle" ) let alertPresentation = AlarmPresentation.Alert( title: "ࠓ೔͸iOSDCʂى͖ͳ͍ͱ஗ࠁ͢Δͧʂʂʂ", stopButton: stopButton ) γϯϓϧͳΞϥʔϜ࣮૷
  5. // εέδϡʔϧͷఆٛɹྫ: ݻఆ೔࣌ͷΞϥʔϜ let schedule = Alarm.Schedule.fixed(Date().addingTimeInterval(10)) let alarmConfiguration =

    AlarmManager.AlarmConfiguration<SampleAlarmMetadata> .alarm( schedule: schedule, attributes: attributes ) γϯϓϧͳΞϥʔϜ࣮૷
  6. Χ΢ϯτμ΢ϯ࣮૷ ͱεψʔζ λΠϚʔػೳ ఆٛ Χ΢ϯτμ΢ϯظؒΛఆٛ Alarm.CountdownDuration preAlert: ॳճΞϥʔτ·Ͱͷ࣌ؒ postAlert: εψʔζ࣌ؒ

    UI Χ΢ϯτμ΢ϯҰ࣌ఀࢭ6*Λఆٛ AlarmPresentation.Countdown Χ΢ϯτμ΢ϯதͷλΠτϧɺҰ࣌ఀࢭϘλϯ ಈ࡞ ηΧϯμϦϘλϯͷಈ࡞ઃఆ secondaryButtonBehavior: .countdown -> εψʔζ΍λΠϚʔ܁Γฦ͠ΛτϦΨʔ -> postAlert
  7. Χ΢ϯτμ΢ϯ࣮૷ λΠϚʔػೳͱεψʔζ let alertContent = AlarmPresentation.Alert( title: "AlarmKitΛ఻͑Δొஃ࣌ؒ", stopButton: .stopButton,

    secondaryButton: .repeatButton, secondaryButtonBehavior: .countdown ) let countdownContent = AlarmPresentation.Countdown(title: "ۓுͷొஃ", pauseButton: .init(text: "ఀࢭ", textColor: .orange, systemImageName: "pause")) let pausedContent = AlarmPresentation.Paused(title: "ۓு࠶։", resumeButton: .init(text: "࠶։", textColor: .blue, systemImageName: "play.fill")) let attributes = AlarmAttributes( presentation: AlarmPresentation( alert: alertContent, countdown: countdownContent, paused: pausedContent ), metadata: CookingData(method: .fry), tintColor: .orange ) let id = UUID() let alarmConfiguration = AlarmConfiguration( countdownDuration: .init(preAlert: 15 * 60, postAlert: 5 * 60), attributes: attributes, secondaryIntent: RepeatIntent(alarmID: id.uuidString) )
  8. Χ΢ϯτμ΢ϯ࣮૷ ·ͣΞϥʔτը໘ let alertContent = AlarmPresentation.Alert( title: “AlarmKitΛ࢖ͬͯΈΔ", stopButton: .stopButton,

    secondaryButton: .repeatButton, secondaryButtonBehavior: .countdown ) SecondaryButtonBehavior .countdown .custom→ࣗ༝ͳΞΫγϣϯ͕ઃఆͰ͖Δ
  9. Χ΢ϯτμ΢ϯ࣮૷ ·ͣΞϥʔτը໘ extension AlarmButton { static var repeatButton: Self {

    AlarmButton( text: "Repeat", textColor: .black, systemImageName: "repeat.circle" ) } }
  10. Χ΢ϯτμ΢ϯ࣮૷ λΠϚʔػೳͱεψʔζ let attributes = AlarmAttributes( presentation: AlarmPresentation( alert: alertContent,

    countdown: countdownContent, paused: pausedContent ), metadata: CookingData(method: .fry), tintColor: .orange )
  11. Χ΢ϯτμ΢ϯ࣮૷ λΠϚʔػೳͱεψʔζ let id = UUID() let alarmConfiguration = AlarmConfiguration(

    countdownDuration: .init(preAlert: 15 * 60, postAlert: 5 * 60), attributes: attributes, secondaryIntent: RepeatIntent(alarmID: id.uuidString) ) try await AlarmManager.shared.schedule( id: id, configuration: alarmConfiguration )
  12. Χ΢ϯτμ΢ϯ࣮૷ λΠϚʔػೳͱεψʔζ struct RepeatIntent: LiveActivityIntent { func perform() throws ->

    some IntentResult { try AlarmManager.shared.countdown(id: UUID(uuidString: alarmID)!) return .result() } static var title: LocalizedStringResource = "Repeat" static var description = IntentDescription("Repeat a countdown") @Parameter(title: "alarmID") var alarmID: String
  13. Live Activities - Dynamic IslandରԠ Dynamic Island / ϩοΫը໘Ͱͷࢹ֮తମݧ Dynamic

    Island දࣔ ϩοΫը໘ ελϯόΠϞʔυ • ΞϥʔϜͷঢ়ଶ (.countdown, .paused) ʹԠͨ͡ϏϡʔΛఏڙ "DUJWJUZ,JUΛར༻ͯ͠ΧελϜ6*Λ࣮૷
  14. ΧελϜϝλσʔλ struct CookingData: AlarmMetadata { let createdAt: Date let method:

    Method? enum Method: String, Codable { case stove case oven var icon: String { switch self { case .stove: "stove" case .oven: "oven" } } } }
  15. Live Activities struct AlarmLiveActivity: Widget { var body: some WidgetConfiguration

    { ActivityConfiguration(for: AlarmAttributes<CookingData>.self) { context in // The Lock Screen presentation. lockScreenView(attributes: context.attributes, state: context.state) } dynamicIsland: { context in // The presentations that appear in the Dynamic Island. DynamicIsland { // The expanded Dynamic Island presentation. DynamicIslandExpandedRegion(.leading) { alarmTitle(attributes: context.attributes, state: context.state) } DynamicIslandExpandedRegion(.trailing) { cookingMethod(metadata: context.attributes.metadata) } DynamicIslandExpandedRegion(.bottom) { bottomView(attributes: context.attributes, state: context.state) } } compactLeading: { // The compact leading presentation. countdown(state: context.state, maxWidth: 44) .foregroundStyle(context.attributes.tintColor) } compactTrailing: { // The compact trailing presentation. AlarmProgressView(cookingMethod: context.attributes.metadata ? . method, mode: context.state.mode, tint: context.attributes.tintColor) } minimal: { // The minimal presentation. AlarmProgressView(cookingMethod: context.attributes.metadata ? . method, mode: context.state.mode, tint: context.attributes.tintColor) } .keylineTint(context.attributes.tintColor) } }
  16. Live Activities struct AlarmLiveActivity: Widget { var body: some WidgetConfiguration

    { ActivityConfiguration(for: AlarmAttributes<CookingData>.self) { context in // The Lock Screen presentation. lockScreenView(attributes: context.attributes, state: context.state) } dynamicIsland: { context in // The presentations that appear in the Dynamic Island. DynamicIsland { // The expanded Dynamic Island presentation. DynamicIslandExpandedRegion(.leading) { alarmTitle(attributes: context.attributes, state: context.state) }
  17. App Intents ΧελϜΞΫγϣϯ Λ૊ΈࠐΉ ΞϥʔϜޙͷಠࣗϫʔΫϑϩʔ App Intent ఀࢭϘλϯɺηΧϯμϦϘλϯͷ྆ํʹରԠ AlarmManager.AlarmConfiguration stopIntent

    secondaryIntent secondaryButtonBehavior: .custom LiveActivityIntent PQFO"QQ8IFO3VO Πϯςϯτ࣮ߦ࣌ʹΞϓϦΛىಈ ىಈޙͷৄࡉϏϡʔදࣔͳͲɺෳࡶͳϫʔΫϑϩʔΛ࣮ݱ ΞϓϦىಈΧελϜॲཧ ϨγϐදࣔͳͲಛఆͷը໘ʹભҠ ΞϥʔϜൃՐ ϢʔβʔʹΞϥʔτ͕දࣔ͞ΕΔ ηΧϯμϦϘλϯλοϓ ΧελϜΞΫγϣϯΛτϦΨʔ "QQ*OUFOU࣮ߦ ϝιου͕ݺͼग़͞ΕΔ
  18. Thank You! ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ AlarmKitͷՄೳੑΛօ͞ΜͷΞϓϦͰͥͻ׆͔͍ͯͩ͘͠͞ ᠃ڮྋ iOS Developer & Creator Twitter(X)

    @tsuzuki817 GitHub tsuzukihashi ࣭͝໰͸DM·ͨ͸ϋογϡλάͰʂ iOSDC Japan 2025 | 2025/09/19 | Track D