@capgo/capacitor-alarm 7.1.15 → 7.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,18 @@
1
1
  import Foundation
2
+ #if canImport(UIKit)
3
+ import UIKit
4
+ #endif
2
5
 
3
6
  #if canImport(AlarmKit)
4
7
  import AlarmKit
8
+ #if canImport(SwiftUI)
9
+ import SwiftUI
10
+ #endif
11
+
12
+ @available(iOS 26.0, *)
13
+ private struct AlarmBridgeMetadata: AlarmMetadata {
14
+ let label: String
15
+ }
5
16
  #endif
6
17
 
7
18
  class AlarmKitBridge {
@@ -18,7 +29,6 @@ class AlarmKitBridge {
18
29
  if #available(iOS 26.0, *) {
19
30
  Task {
20
31
  do {
21
- // Acquire the AlarmKit manager; many Apple frameworks use a shared singleton.
22
32
  let alarmManager = AlarmManager.shared
23
33
  let state = try await alarmManager.requestAuthorization()
24
34
  completion(state == .authorized, nil)
@@ -35,9 +45,34 @@ class AlarmKitBridge {
35
45
  static func createAlarm(hour: Int, minute: Int, label: String?, completion: @escaping (Bool, String?) -> Void) {
36
46
  #if canImport(AlarmKit)
37
47
  if #available(iOS 26.0, *) {
38
- // TODO: Replace with real AlarmKit calls.
39
- // We intentionally avoid guessing API surface to prevent compilation/runtime issues.
40
- completion(false, "AlarmKit integrated but no concrete API wired. Provide exact AlarmKit API usage.")
48
+ guard (0..<24).contains(hour), (0..<60).contains(minute) else {
49
+ completion(false, "Invalid time components for alarm.")
50
+ return
51
+ }
52
+
53
+ Task {
54
+ do {
55
+ guard let triggerDate = nextTriggerDate(hour: hour, minute: minute) else {
56
+ completion(false, "Unable to compute next trigger date.")
57
+ return
58
+ }
59
+ let displayLabel = sanitizedLabel(label)
60
+ let configuration = try alarmConfiguration(triggerDate: triggerDate, label: displayLabel)
61
+
62
+ _ = try await AlarmManager.shared.schedule(id: UUID(), configuration: configuration)
63
+
64
+ let formatter = DateFormatter()
65
+ formatter.dateStyle = .none
66
+ formatter.timeStyle = .short
67
+ formatter.locale = Locale.current
68
+ formatter.timeZone = Calendar.current.timeZone
69
+
70
+ let message = "Alarm scheduled for \(formatter.string(from: triggerDate))."
71
+ completion(true, message)
72
+ } catch {
73
+ completion(false, "Failed to schedule alarm: \(error.localizedDescription)")
74
+ }
75
+ }
41
76
  return
42
77
  }
43
78
  #endif
@@ -45,13 +80,84 @@ class AlarmKitBridge {
45
80
  }
46
81
 
47
82
  static func openAlarms(completion: @escaping (Bool, String?) -> Void) {
48
- #if canImport(AlarmKit)
49
- if #available(iOS 26.0, *) {
50
- // TODO: Replace with real AlarmKit calls.
51
- completion(false, "AlarmKit integrated but no concrete API wired. Provide exact AlarmKit API usage.")
52
- return
83
+ #if canImport(UIKit)
84
+ DispatchQueue.main.async {
85
+ let candidates = AlarmKitBridge.clockURLCandidates()
86
+ attemptOpenClock(urls: candidates, index: 0, completion: completion)
53
87
  }
88
+ #else
89
+ completion(false, "Clock UI cannot be opened on this platform.")
54
90
  #endif
55
- completion(false, "AlarmKit not available on this device/SDK")
56
91
  }
57
92
  }
93
+
94
+ #if canImport(AlarmKit)
95
+ @available(iOS 26.0, *)
96
+ private extension AlarmKitBridge {
97
+ static func sanitizedLabel(_ label: String?) -> String {
98
+ let trimmed = label?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
99
+ return trimmed.isEmpty ? "Alarm" : trimmed
100
+ }
101
+
102
+ static func nextTriggerDate(hour: Int, minute: Int) -> Date? {
103
+ let now = Date()
104
+ var components = Calendar.current.dateComponents([.year, .month, .day], from: now)
105
+ components.hour = hour
106
+ components.minute = minute
107
+ components.second = 0
108
+
109
+ guard let candidate = Calendar.current.date(from: components) else { return nil }
110
+ if candidate.timeIntervalSince(now) > 1 {
111
+ return candidate
112
+ }
113
+ return Calendar.current.date(byAdding: .day, value: 1, to: candidate)
114
+ }
115
+
116
+ static func alarmConfiguration(triggerDate: Date, label: String) throws -> AlarmManager.AlarmConfiguration<AlarmBridgeMetadata> {
117
+ #if canImport(SwiftUI)
118
+ let stopTitle: LocalizedStringResource = LocalizedStringResource("Stop")
119
+ let stopButton = AlarmButton(text: stopTitle, textColor: Color.white, systemImageName: "stop.fill")
120
+
121
+ let titleResource = LocalizedStringResource(String.LocalizationValue(label))
122
+ let alert = AlarmPresentation.Alert(title: titleResource, stopButton: stopButton)
123
+ let presentation = AlarmPresentation(alert: alert)
124
+
125
+ let tintColor = Color.orange
126
+ let metadata = AlarmBridgeMetadata(label: label)
127
+
128
+ let attributes = AlarmAttributes<AlarmBridgeMetadata>(presentation: presentation, metadata: metadata, tintColor: tintColor)
129
+ return AlarmManager.AlarmConfiguration<AlarmBridgeMetadata>.alarm(
130
+ schedule: .fixed(triggerDate),
131
+ attributes: attributes
132
+ )
133
+ #else
134
+ struct MissingSwiftUI: Error {}
135
+ throw MissingSwiftUI()
136
+ #endif
137
+ }
138
+
139
+ }
140
+ #endif
141
+
142
+ #if canImport(UIKit)
143
+ private extension AlarmKitBridge {
144
+ static func clockURLCandidates() -> [URL] {
145
+ ["clock-alarm://", "clock://", "clock-app://"].compactMap { URL(string: $0) }
146
+ }
147
+
148
+ static func attemptOpenClock(urls: [URL], index: Int, completion: @escaping (Bool, String?) -> Void) {
149
+ guard index < urls.count else {
150
+ completion(false, "Clock app not reachable on this device.")
151
+ return
152
+ }
153
+
154
+ UIApplication.shared.open(urls[index], options: [:]) { success in
155
+ if success {
156
+ completion(true, nil)
157
+ } else {
158
+ attemptOpenClock(urls: urls, index: index + 1, completion: completion)
159
+ }
160
+ }
161
+ }
162
+ }
163
+ #endif
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-alarm",
3
- "version": "7.1.15",
3
+ "version": "7.1.16",
4
4
  "description": "Manage native alarm Capacitor plugin",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",