RORK LABEN
BUILD — Rork Maxがネイティブ Swift アプリを生成し、React Nativeでは届きにくい領域に踏み込めますPLATFORM — Rork MaxはiPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageに対応しますNATIVE — HealthKit・Core ML・NFC・Dynamic Island・Live Activitiesなどのネイティブ機能が使えますTEST — ブラウザ内ストリーミングiOSシミュレータで、XcodeやMacなしでテストできますDEPLOY — ビルド・証明書・App Store申請までの自動化で公開までを簡素化しますPRICE — 無料で開始でき、有料プランは$25/月〜、Rork Maxは$200/月ですBUILD — Rork Maxがネイティブ Swift アプリを生成し、React Nativeでは届きにくい領域に踏み込めますPLATFORM — Rork MaxはiPhone・iPad・Apple Watch・Apple TV・Vision Pro・iMessageに対応しますNATIVE — HealthKit・Core ML・NFC・Dynamic Island・Live Activitiesなどのネイティブ機能が使えますTEST — ブラウザ内ストリーミングiOSシミュレータで、XcodeやMacなしでテストできますDEPLOY — ビルド・証明書・App Store申請までの自動化で公開までを簡素化しますPRICE — 無料で開始でき、有料プランは$25/月〜、Rork Maxは$200/月です
記事一覧/アプリ開発
アプリ開発/2026-07-01上級

Rork Max のネイティブ Swift で AlarmKit のタイマーを実装する — 消音・集中モードを越えて鳴らす

ローカル通知が消音モードや集中モードで鳴らない問題を、iOS 26 の AlarmKit で解決する実装メモです。認可・カウントダウンの schedule・ロック画面と Dynamic Island の Live Activity・実行中タイマーの一覧までの Swift コードと、Rork Max のネイティブ生成や Expo からの橋渡しの境界設計を、実際にハマった点を添えてまとめます。

AlarmKitRork Max202Live ActivityiOS95タイマー2

プレミアム記事

個人開発で小さなインターバルタイマーを作っていたとき、テスターの一人から「マナーモードにしていると鳴らないんだけど」と言われました。手元の検証では問題なく動いていたので、最初は再現できずに戸惑いました。原因は単純で、私が UserNotifications でローカル通知を組んでいたからです。消音スイッチを入れた iPhone では、通知の音は鳴りません。タイマーやアラームのように「鳴らないと意味がない」種類の通知にとって、これは致命的でした。

長らく、この壁を越える正規の手段は Critical Alerts entitlement だけでした。けれどこれは医療・安全・公共安全といった用途を想定した特別な権限で、Apple への申請が必要で、個人開発の小さなタイマーアプリではまず通りません。iOS 26 で登場した AlarmKit は、この状況を変えました。タイマーとアラームに限ってですが、申請なしで消音モードと集中モードを越えてアラートを鳴らせるようになったのです。Rork Max がネイティブ Swift を生成するようになったことで、この AlarmKit を個人開発でも現実的に組み込めるようになりました。実際に組み込んで分かったことを、手順としてまとめます。

私自身、個人開発で運営している小さなユーティリティ系のアプリにタイマー機能を足したとき、「鳴らない」という一点だけで使われなくなってしまう経験をしました。タイマーは、約束どおり鳴ることがそのままリテンションに直結する機能です。AdMob で広告を載せている無料アプリでも、「ちゃんと鳴るから使い続ける」という信頼が、結局は継続利用と収益の土台になります。その意味で AlarmKit は、単なる新 API ではなく、個人開発の小さなアプリの体験を一段引き上げる道具だと感じています。

なぜローカル通知では鳴らないのか

まず、ここを正確に押さえておくと後の設計判断が楽になります。UserNotifications で送るローカル通知やプッシュ通知は、端末の消音スイッチと集中モードの設定に従います。つまり利用者が消音にしていれば音は鳴らず、集中モードで遮断していればバナーも抑制されます。これはユーザーを邪魔しないための正しい挙動で、ニュースやマーケティング通知ではむしろ歓迎されるものです。

問題は、タイマー・アラーム・リマインダーのように「鳴ること自体が機能」の通知です。料理の3分タイマー、ワークアウトのインターバル、昼寝の30分アラーム。これらは消音中だからこそ鳴ってほしい場面が多いのに、ローカル通知では沈黙してしまいます。次の表が、3つの選択肢の差を端的に表しています。

手段消音・集中モードを越えるか申請の要否個人開発での現実性
UserNotifications(ローカル通知)越えない(沈黙する)不要高いが、鳴らない問題が残る
Critical Alerts entitlement越えるApple への個別申請が必要低い(用途が限定され、まず通らない)
AlarmKit(iOS 26+)越える不要(利用者の認可のみ)高い(タイマー・アラーム用途に限る)

AlarmKit は万能ではありません。任意の通知を鳴らし放題にする仕組みではなく、あくまで「ユーザーが自分で仕掛けたタイマー・アラーム」という文脈に限定されています。逆に言えば、その文脈にぴたりとはまるアプリにとっては、これまでなかった正規ルートが開いたということです。

最小構成でも、組み立てる部品はいくつかに分かれます。全体像を先に番号で押さえておくと、後のコードが追いやすくなります。

  1. Info.plist に利用目的を書き、AlarmManager で利用者の認可を取る
  2. アラートと属性を組み立て、schedule(.timer(...)) でカウントダウンを開始する
  3. Widget Extension に Live Activity を実装し、ロック画面と Dynamic Island に残り時間を出す
  4. alarmUpdates を購読してアプリ内に実行中タイマーを一覧し、必要なら cancel(id:) で止める

認可を取る

AlarmKit のアラートは集中モード中でも音を立てて画面に出るため、利用者の明示的な許可が前提になります。手順は2つです。まず Info.plistNSAlarmKitUsageDescription を追加し、なぜアラームを仕掛けるのかを一文で説明します。この文言は、初回の認可ダイアログに Apple 標準の説明と並べて表示されます。

次に、認可状態を確認し、未決定なら requestAuthorization() でシステムプロンプトを出します。AlarmManager.shared がその窓口です。

import SwiftUI
import AlarmKit
 
struct TimerStartButton: View {
    private let manager = AlarmManager.shared
 
    var body: some View {
        Button("タイマーを開始", systemImage: "timer") {
            Task {
                if await ensureAuthorized() {
                    await scheduleTimer()
                } else {
                    // 拒否時: 機能を無効化し、設定アプリへの導線を出す
                }
            }
        }
    }
 
    private func ensureAuthorized() async -> Bool {
        switch manager.authorizationState {
        case .notDetermined:
            do {
                let state = try await manager.requestAuthorization()
                return state == .authorized
            } catch {
                print("認可エラー: \(error)")
                return false
            }
        case .authorized:
            return true
        case .denied:
            return false
        @unknown default:
            return false
        }
    }
}

.notDetermined の分岐でだけプロンプトを出している点が要点です。すでに .denied の利用者に毎回プロンプトを投げてもシステムは何も表示しませんし、.authorized なら即座に処理へ進めます。拒否されたときに何をするかは、アプリの性格で決めます。タイマーが主機能なら設定アプリへ誘導し、補助機能なら静かにローカル通知へフォールバックする、といった分岐が現実的です。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
UserNotifications では消音・集中モードで鳴らない理由と、AlarmKit が Critical Alerts 権限なしでそれを越える仕組み、置き換えの判断基準が分かります
認可リクエストからカウントダウンの schedule、ロック画面と Dynamic Island の Live Activity 最小構成までの、動く Swift コード一式が手に入ります
Rork Max が生成するネイティブ Swift で AlarmKit を使う手順と、Expo / React Native からネイティブモジュールで橋渡しする際の境界設計が身につきます
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

アプリ開発2026-07-01
Rork Max のネイティブ Swift で SharePlay を組む — 二人の画面を同じ状態で動かす設計
GroupActivities を使い、FaceTime 越しに二人の画面を同じ状態で動かす SharePlay を Rork Max のネイティブ Swift で組む実装メモです。GroupActivity の宣言、GroupSession への参加、GroupSessionMessenger での状態同期、遅延と競合の扱い、そして React Native 側から橋渡しするときの境界設計まで、実際に詰まった点を添えてまとめます。
アプリ開発2026-07-01
Rork Max のネイティブ Swift で ShazamKit を使い、流れている曲を認識するアプリを作る
SHManagedSession を使って、周囲で流れている曲を認識するアプリを Rork Max のネイティブ Swift で組む実装メモです。旧来の AVAudioEngine 手回しとの違い、idle・prerecording・matching の状態設計、認識精度を上げる prerecording の使いどころ、Expo からの橋渡しの境界設計まで、実際にハマった点を添えてまとめます。
アプリ開発2026-07-01
ブロック画面を自分のアプリらしく作り替える — Rork Max で ShieldConfiguration とボタン処理を実装する
スクリーンタイム系アプリで表示される灰色の遮蔽画面を、自分のアプリのトーンに作り替えます。ShieldConfiguration 拡張でUIを差し替え、ShieldAction でボタンの挙動まで制御する実装を、Rork Max のネイティブ構成に組み込む形でまとめました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →