@capawesome/capacitor-alarm 0.0.1

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.
Files changed (45) hide show
  1. package/CapawesomeCapacitorAlarm.podspec +17 -0
  2. package/LICENSE +21 -0
  3. package/Package.swift +28 -0
  4. package/README.md +449 -0
  5. package/android/build.gradle +58 -0
  6. package/android/src/main/AndroidManifest.xml +9 -0
  7. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/Alarm.java +85 -0
  8. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/AlarmPlugin.java +154 -0
  9. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/classes/CustomException.java +20 -0
  10. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/classes/CustomExceptions.java +16 -0
  11. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/classes/options/CreateAlarmOptions.java +124 -0
  12. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/classes/options/CreateTimerOptions.java +48 -0
  13. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/classes/results/CreateAlarmResult.java +25 -0
  14. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/classes/results/IsAvailableResult.java +22 -0
  15. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/interfaces/Callback.java +5 -0
  16. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/interfaces/EmptyCallback.java +5 -0
  17. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/interfaces/NonEmptyResultCallback.java +7 -0
  18. package/android/src/main/java/io/capawesome/capacitorjs/plugins/alarm/interfaces/Result.java +7 -0
  19. package/android/src/main/res/.gitkeep +0 -0
  20. package/dist/docs.json +743 -0
  21. package/dist/esm/definitions.d.ts +349 -0
  22. package/dist/esm/definitions.js +55 -0
  23. package/dist/esm/definitions.js.map +1 -0
  24. package/dist/esm/index.d.ts +4 -0
  25. package/dist/esm/index.js +7 -0
  26. package/dist/esm/index.js.map +1 -0
  27. package/dist/esm/web.d.ts +12 -0
  28. package/dist/esm/web.js +28 -0
  29. package/dist/esm/web.js.map +1 -0
  30. package/dist/plugin.cjs.js +97 -0
  31. package/dist/plugin.cjs.js.map +1 -0
  32. package/dist/plugin.js +100 -0
  33. package/dist/plugin.js.map +1 -0
  34. package/ios/Plugin/Alarm.swift +148 -0
  35. package/ios/Plugin/AlarmPlugin.swift +147 -0
  36. package/ios/Plugin/Classes/Options/CancelAlarmOptions.swift +20 -0
  37. package/ios/Plugin/Classes/Options/CreateAlarmOptions.swift +70 -0
  38. package/ios/Plugin/Classes/Results/CreateAlarmResult.swift +16 -0
  39. package/ios/Plugin/Classes/Results/GetAlarmsResult.swift +49 -0
  40. package/ios/Plugin/Classes/Results/IsAvailableResult.swift +16 -0
  41. package/ios/Plugin/Classes/Results/PermissionStatusResult.swift +16 -0
  42. package/ios/Plugin/Enums/CustomError.swift +47 -0
  43. package/ios/Plugin/Info.plist +24 -0
  44. package/ios/Plugin/Protocols/Result.swift +6 -0
  45. package/package.json +95 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/definitions.js","esm/index.js","esm/web.js"],"sourcesContent":["/**\n * @since 0.1.0\n */\nexport var ErrorCode;\n(function (ErrorCode) {\n /**\n * No app that can handle alarms was found on the device.\n *\n * @since 0.1.0\n */\n ErrorCode[\"NoClockApp\"] = \"NO_CLOCK_APP\";\n /**\n * The permission to schedule alarms was denied.\n *\n * @since 0.1.0\n */\n ErrorCode[\"PermissionDenied\"] = \"PERMISSION_DENIED\";\n})(ErrorCode || (ErrorCode = {}));\n/**\n * The days of the week.\n *\n * @since 0.1.0\n */\nexport var Weekday;\n(function (Weekday) {\n /**\n * @since 0.1.0\n */\n Weekday[\"Friday\"] = \"FRIDAY\";\n /**\n * @since 0.1.0\n */\n Weekday[\"Monday\"] = \"MONDAY\";\n /**\n * @since 0.1.0\n */\n Weekday[\"Saturday\"] = \"SATURDAY\";\n /**\n * @since 0.1.0\n */\n Weekday[\"Sunday\"] = \"SUNDAY\";\n /**\n * @since 0.1.0\n */\n Weekday[\"Thursday\"] = \"THURSDAY\";\n /**\n * @since 0.1.0\n */\n Weekday[\"Tuesday\"] = \"TUESDAY\";\n /**\n * @since 0.1.0\n */\n Weekday[\"Wednesday\"] = \"WEDNESDAY\";\n})(Weekday || (Weekday = {}));\n//# sourceMappingURL=definitions.js.map","import { registerPlugin } from '@capacitor/core';\nconst Alarm = registerPlugin('Alarm', {\n web: () => import('./web').then(m => new m.AlarmWeb()),\n});\nexport * from './definitions';\nexport { Alarm };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class AlarmWeb extends WebPlugin {\n async cancelAlarm(_options) {\n throw this.unimplemented('Not implemented on web.');\n }\n async checkPermissions() {\n throw this.unimplemented('Not implemented on web.');\n }\n async createAlarm(_options) {\n throw this.unimplemented('Not implemented on web.');\n }\n async createTimer(_options) {\n throw this.unimplemented('Not implemented on web.');\n }\n async getAlarms() {\n throw this.unimplemented('Not implemented on web.');\n }\n async isAvailable() {\n throw this.unimplemented('Not implemented on web.');\n }\n async openAlarms() {\n throw this.unimplemented('Not implemented on web.');\n }\n async requestPermissions() {\n throw this.unimplemented('Not implemented on web.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["ErrorCode","Weekday","registerPlugin","WebPlugin"],"mappings":";;;IAAA;IACA;IACA;AACWA;IACX,CAAC,UAAU,SAAS,EAAE;IACtB;IACA;IACA;IACA;IACA;IACA,IAAI,SAAS,CAAC,YAAY,CAAC,GAAG,cAAc;IAC5C;IACA;IACA;IACA;IACA;IACA,IAAI,SAAS,CAAC,kBAAkB,CAAC,GAAG,mBAAmB;IACvD,CAAC,EAAEA,iBAAS,KAAKA,iBAAS,GAAG,EAAE,CAAC,CAAC;IACjC;IACA;IACA;IACA;IACA;AACWC;IACX,CAAC,UAAU,OAAO,EAAE;IACpB;IACA;IACA;IACA,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ;IAChC;IACA;IACA;IACA,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ;IAChC;IACA;IACA;IACA,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU;IACpC;IACA;IACA;IACA,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,QAAQ;IAChC;IACA;IACA;IACA,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,UAAU;IACpC;IACA;IACA;IACA,IAAI,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS;IAClC;IACA;IACA;IACA,IAAI,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW;IACtC,CAAC,EAAEA,eAAO,KAAKA,eAAO,GAAG,EAAE,CAAC,CAAC;;ACpDxB,UAAC,KAAK,GAAGC,mBAAc,CAAC,OAAO,EAAE;IACtC,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC1D,CAAC;;ICFM,MAAM,QAAQ,SAASC,cAAS,CAAC;IACxC,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE;IAChC,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE;IAChC,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,WAAW,CAAC,QAAQ,EAAE;IAChC,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,SAAS,GAAG;IACtB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,WAAW,GAAG;IACxB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,UAAU,GAAG;IACvB,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ,IAAI,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,MAAM,IAAI,CAAC,aAAa,CAAC,yBAAyB,CAAC;IAC3D,IAAI;IACJ;;;;;;;;;;;;;;;"}
@@ -0,0 +1,148 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import AlarmKit
4
+ import SwiftUI
5
+
6
+ @available(iOS 26.0, *)
7
+ @objc public class Alarm: NSObject {
8
+ private struct EmptyMetadata: AlarmMetadata {}
9
+
10
+ private let labelsUserDefaultsKey = "capawesome_capacitor_alarm_labels"
11
+ private let manager = AlarmKit.AlarmManager.shared
12
+ private let plugin: AlarmPlugin
13
+
14
+ init(plugin: AlarmPlugin) {
15
+ self.plugin = plugin
16
+ }
17
+
18
+ @objc public func cancelAlarm(_ options: CancelAlarmOptions, completion: @escaping (Error?) -> Void) {
19
+ do {
20
+ try manager.cancel(id: options.id)
21
+ removeLabel(for: options.id)
22
+ completion(nil)
23
+ } catch {
24
+ completion(error)
25
+ }
26
+ }
27
+
28
+ @objc public func checkPermissions(completion: @escaping (PermissionStatusResult) -> Void) {
29
+ completion(PermissionStatusResult(alarms: getPermissionState(from: manager.authorizationState)))
30
+ }
31
+
32
+ @objc public func createAlarm(_ options: CreateAlarmOptions, completion: @escaping (CreateAlarmResult?, Error?) -> Void) {
33
+ Task {
34
+ do {
35
+ try await requestAuthorizationIfNeeded()
36
+ let id = UUID()
37
+ let attributes = AlarmAttributes<EmptyMetadata>(presentation: makePresentation(label: options.label), tintColor: .accentColor)
38
+ let schedule = makeSchedule(hour: options.hour, minute: options.minute, days: options.days)
39
+ let configuration = AlarmKit.AlarmManager.AlarmConfiguration.alarm(schedule: schedule, attributes: attributes)
40
+ _ = try await manager.schedule(id: id, configuration: configuration)
41
+ setLabel(options.label, for: id)
42
+ completion(CreateAlarmResult(id: id.uuidString), nil)
43
+ } catch {
44
+ completion(nil, error)
45
+ }
46
+ }
47
+ }
48
+
49
+ @objc public func getAlarms(completion: @escaping (GetAlarmsResult?, Error?) -> Void) {
50
+ do {
51
+ let alarms = try manager.alarms
52
+ completion(GetAlarmsResult(alarms: alarms, labels: getLabels()), nil)
53
+ } catch {
54
+ completion(nil, error)
55
+ }
56
+ }
57
+
58
+ @objc public func isAvailable(completion: @escaping (IsAvailableResult) -> Void) {
59
+ completion(IsAvailableResult(available: true))
60
+ }
61
+
62
+ @objc public func requestPermissions(completion: @escaping (PermissionStatusResult?, Error?) -> Void) {
63
+ Task {
64
+ do {
65
+ try ensureUsageDescription()
66
+ let state = try await manager.requestAuthorization()
67
+ completion(PermissionStatusResult(alarms: getPermissionState(from: state)), nil)
68
+ } catch {
69
+ completion(nil, error)
70
+ }
71
+ }
72
+ }
73
+
74
+ private func ensureUsageDescription() throws {
75
+ guard Bundle.main.object(forInfoDictionaryKey: "NSAlarmKitUsageDescription") is String else {
76
+ throw CustomError.privacyDescriptionsMissing
77
+ }
78
+ }
79
+
80
+ private func getLabels() -> [String: String] {
81
+ return UserDefaults.standard.dictionary(forKey: labelsUserDefaultsKey) as? [String: String] ?? [:]
82
+ }
83
+
84
+ private func getPermissionState(from state: AlarmKit.AlarmManager.AuthorizationState) -> String {
85
+ switch state {
86
+ case .authorized:
87
+ return "granted"
88
+ case .denied:
89
+ return "denied"
90
+ case .notDetermined:
91
+ return "prompt"
92
+ @unknown default:
93
+ return "prompt"
94
+ }
95
+ }
96
+
97
+ private func makePresentation(label: String?) -> AlarmPresentation {
98
+ let title = LocalizedStringResource("\(label ?? "Alarm")")
99
+ let alert: AlarmPresentation.Alert
100
+ if #available(iOS 26.1, *) {
101
+ alert = AlarmPresentation.Alert(title: title)
102
+ } else {
103
+ alert = AlarmPresentation.Alert(title: title, stopButton: AlarmButton(text: "Stop", textColor: .white, systemImageName: "stop.circle"))
104
+ }
105
+ return AlarmPresentation(alert: alert)
106
+ }
107
+
108
+ private func makeSchedule(hour: Int, minute: Int, days: [Locale.Weekday]?) -> AlarmKit.Alarm.Schedule {
109
+ let time = AlarmKit.Alarm.Schedule.Relative.Time(hour: hour, minute: minute)
110
+ var recurrence = AlarmKit.Alarm.Schedule.Relative.Recurrence.never
111
+ if let days = days, !days.isEmpty {
112
+ recurrence = .weekly(days)
113
+ }
114
+ return .relative(AlarmKit.Alarm.Schedule.Relative(time: time, repeats: recurrence))
115
+ }
116
+
117
+ private func removeLabel(for id: UUID) {
118
+ var labels = getLabels()
119
+ labels.removeValue(forKey: id.uuidString)
120
+ UserDefaults.standard.set(labels, forKey: labelsUserDefaultsKey)
121
+ }
122
+
123
+ private func requestAuthorizationIfNeeded() async throws {
124
+ switch manager.authorizationState {
125
+ case .authorized:
126
+ return
127
+ case .denied:
128
+ throw CustomError.permissionDenied
129
+ case .notDetermined:
130
+ try ensureUsageDescription()
131
+ let state = try await manager.requestAuthorization()
132
+ guard state == .authorized else {
133
+ throw CustomError.permissionDenied
134
+ }
135
+ @unknown default:
136
+ throw CustomError.permissionDenied
137
+ }
138
+ }
139
+
140
+ private func setLabel(_ label: String?, for id: UUID) {
141
+ guard let label = label else {
142
+ return
143
+ }
144
+ var labels = getLabels()
145
+ labels[id.uuidString] = label
146
+ UserDefaults.standard.set(labels, forKey: labelsUserDefaultsKey)
147
+ }
148
+ }
@@ -0,0 +1,147 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc(AlarmPlugin)
5
+ public class AlarmPlugin: CAPPlugin, CAPBridgedPlugin {
6
+ public let identifier = "AlarmPlugin"
7
+ public let jsName = "Alarm"
8
+ public let pluginMethods: [CAPPluginMethod] = [
9
+ CAPPluginMethod(name: "cancelAlarm", returnType: CAPPluginReturnPromise),
10
+ CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
11
+ CAPPluginMethod(name: "createAlarm", returnType: CAPPluginReturnPromise),
12
+ CAPPluginMethod(name: "createTimer", returnType: CAPPluginReturnPromise),
13
+ CAPPluginMethod(name: "getAlarms", returnType: CAPPluginReturnPromise),
14
+ CAPPluginMethod(name: "isAvailable", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "openAlarms", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise)
17
+ ]
18
+ public static let tag = "AlarmPlugin"
19
+
20
+ private var implementation: Any?
21
+
22
+ override public func load() {
23
+ if #available(iOS 26.0, *) {
24
+ self.implementation = Alarm(plugin: self)
25
+ }
26
+ }
27
+
28
+ @objc func cancelAlarm(_ call: CAPPluginCall) {
29
+ guard #available(iOS 26.0, *), let implementation = implementation as? Alarm else {
30
+ rejectCallAsUnavailable(call)
31
+ return
32
+ }
33
+ do {
34
+ let options = try CancelAlarmOptions(call)
35
+ implementation.cancelAlarm(options, completion: { error in
36
+ if let error = error {
37
+ self.rejectCall(call, error)
38
+ return
39
+ }
40
+ self.resolveCall(call)
41
+ })
42
+ } catch {
43
+ self.rejectCall(call, error)
44
+ }
45
+ }
46
+
47
+ @objc override public func checkPermissions(_ call: CAPPluginCall) {
48
+ guard #available(iOS 26.0, *), let implementation = implementation as? Alarm else {
49
+ rejectCallAsUnavailable(call)
50
+ return
51
+ }
52
+ implementation.checkPermissions(completion: { result in
53
+ self.resolveCall(call, result)
54
+ })
55
+ }
56
+
57
+ @objc func createAlarm(_ call: CAPPluginCall) {
58
+ guard #available(iOS 26.0, *), let implementation = implementation as? Alarm else {
59
+ rejectCallAsUnavailable(call)
60
+ return
61
+ }
62
+ do {
63
+ let options = try CreateAlarmOptions(call)
64
+ implementation.createAlarm(options, completion: { result, error in
65
+ if let error = error {
66
+ self.rejectCall(call, error)
67
+ return
68
+ }
69
+ self.resolveCall(call, result)
70
+ })
71
+ } catch {
72
+ self.rejectCall(call, error)
73
+ }
74
+ }
75
+
76
+ @objc func createTimer(_ call: CAPPluginCall) {
77
+ rejectCallAsUnimplemented(call)
78
+ }
79
+
80
+ @objc func getAlarms(_ call: CAPPluginCall) {
81
+ guard #available(iOS 26.0, *), let implementation = implementation as? Alarm else {
82
+ rejectCallAsUnavailable(call)
83
+ return
84
+ }
85
+ implementation.getAlarms(completion: { result, error in
86
+ if let error = error {
87
+ self.rejectCall(call, error)
88
+ return
89
+ }
90
+ self.resolveCall(call, result)
91
+ })
92
+ }
93
+
94
+ @objc func isAvailable(_ call: CAPPluginCall) {
95
+ if #available(iOS 26.0, *), let implementation = implementation as? Alarm {
96
+ implementation.isAvailable(completion: { result in
97
+ self.resolveCall(call, result)
98
+ })
99
+ } else {
100
+ resolveCall(call, IsAvailableResult(available: false))
101
+ }
102
+ }
103
+
104
+ @objc func openAlarms(_ call: CAPPluginCall) {
105
+ rejectCallAsUnimplemented(call)
106
+ }
107
+
108
+ @objc override public func requestPermissions(_ call: CAPPluginCall) {
109
+ guard #available(iOS 26.0, *), let implementation = implementation as? Alarm else {
110
+ rejectCallAsUnavailable(call)
111
+ return
112
+ }
113
+ implementation.requestPermissions(completion: { result, error in
114
+ if let error = error {
115
+ self.rejectCall(call, error)
116
+ return
117
+ }
118
+ self.resolveCall(call, result)
119
+ })
120
+ }
121
+
122
+ private func rejectCall(_ call: CAPPluginCall, _ error: Error) {
123
+ CAPLog.print("[", AlarmPlugin.tag, "] ", error)
124
+ let code = (error as? CustomError)?.code
125
+ call.reject(error.localizedDescription, code)
126
+ }
127
+
128
+ private func rejectCallAsUnavailable(_ call: CAPPluginCall) {
129
+ call.unavailable("This method is not available on this platform.")
130
+ }
131
+
132
+ private func rejectCallAsUnimplemented(_ call: CAPPluginCall) {
133
+ call.unimplemented("This method is not available on this platform.")
134
+ }
135
+
136
+ private func resolveCall(_ call: CAPPluginCall) {
137
+ call.resolve()
138
+ }
139
+
140
+ private func resolveCall(_ call: CAPPluginCall, _ result: Result?) {
141
+ if let result = result?.toJSObject() as? JSObject {
142
+ call.resolve(result)
143
+ } else {
144
+ call.resolve()
145
+ }
146
+ }
147
+ }
@@ -0,0 +1,20 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public class CancelAlarmOptions: NSObject {
5
+ let id: UUID
6
+
7
+ init(_ call: CAPPluginCall) throws {
8
+ self.id = try CancelAlarmOptions.getIdFromCall(call)
9
+ }
10
+
11
+ private static func getIdFromCall(_ call: CAPPluginCall) throws -> UUID {
12
+ guard let id = call.getString("id") else {
13
+ throw CustomError.idMissing
14
+ }
15
+ guard let uuid = UUID(uuidString: id) else {
16
+ throw CustomError.idInvalid
17
+ }
18
+ return uuid
19
+ }
20
+ }
@@ -0,0 +1,70 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @available(iOS 26.0, *)
5
+ @objc public class CreateAlarmOptions: NSObject {
6
+ let days: [Locale.Weekday]?
7
+ let hour: Int
8
+ let label: String?
9
+ let minute: Int
10
+
11
+ init(_ call: CAPPluginCall) throws {
12
+ self.days = try CreateAlarmOptions.getDaysFromCall(call)
13
+ self.hour = try CreateAlarmOptions.getHourFromCall(call)
14
+ self.label = call.getString("label")
15
+ self.minute = try CreateAlarmOptions.getMinuteFromCall(call)
16
+ }
17
+
18
+ private static func getDaysFromCall(_ call: CAPPluginCall) throws -> [Locale.Weekday]? {
19
+ guard let days = call.getArray("days") else {
20
+ return nil
21
+ }
22
+ return try days.map { day in
23
+ guard let weekday = day as? String else {
24
+ throw CustomError.daysInvalid
25
+ }
26
+ return try CreateAlarmOptions.getWeekdayFromString(weekday)
27
+ }
28
+ }
29
+
30
+ private static func getHourFromCall(_ call: CAPPluginCall) throws -> Int {
31
+ guard let hour = call.getInt("hour") else {
32
+ throw CustomError.hourMissing
33
+ }
34
+ guard (0...23).contains(hour) else {
35
+ throw CustomError.hourInvalid
36
+ }
37
+ return hour
38
+ }
39
+
40
+ private static func getMinuteFromCall(_ call: CAPPluginCall) throws -> Int {
41
+ guard let minute = call.getInt("minute") else {
42
+ throw CustomError.minuteMissing
43
+ }
44
+ guard (0...59).contains(minute) else {
45
+ throw CustomError.minuteInvalid
46
+ }
47
+ return minute
48
+ }
49
+
50
+ private static func getWeekdayFromString(_ weekday: String) throws -> Locale.Weekday {
51
+ switch weekday {
52
+ case "FRIDAY":
53
+ return .friday
54
+ case "MONDAY":
55
+ return .monday
56
+ case "SATURDAY":
57
+ return .saturday
58
+ case "SUNDAY":
59
+ return .sunday
60
+ case "THURSDAY":
61
+ return .thursday
62
+ case "TUESDAY":
63
+ return .tuesday
64
+ case "WEDNESDAY":
65
+ return .wednesday
66
+ default:
67
+ throw CustomError.daysInvalid
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,16 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public class CreateAlarmResult: NSObject, Result {
5
+ let id: String?
6
+
7
+ init(id: String?) {
8
+ self.id = id
9
+ }
10
+
11
+ @objc public func toJSObject() -> AnyObject {
12
+ var result = JSObject()
13
+ result["id"] = id == nil ? NSNull() : id
14
+ return result as AnyObject
15
+ }
16
+ }
@@ -0,0 +1,49 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import AlarmKit
4
+
5
+ @available(iOS 26.0, *)
6
+ @objc public class GetAlarmsResult: NSObject, Result {
7
+ let alarms: [AlarmKit.Alarm]
8
+ let labels: [String: String]
9
+
10
+ init(alarms: [AlarmKit.Alarm], labels: [String: String]) {
11
+ self.alarms = alarms
12
+ self.labels = labels
13
+ }
14
+
15
+ @objc public func toJSObject() -> AnyObject {
16
+ var alarmsResult = JSArray()
17
+ for alarm in alarms {
18
+ let label = labels[alarm.id.uuidString]
19
+ let time = GetAlarmsResult.getTimeFromAlarm(alarm)
20
+ var alarmResult = JSObject()
21
+ alarmResult["enabled"] = alarm.state == .scheduled || alarm.state == .alerting
22
+ alarmResult["hour"] = time == nil ? NSNull() : time?.hour
23
+ alarmResult["id"] = alarm.id.uuidString
24
+ alarmResult["label"] = label == nil ? NSNull() : label
25
+ alarmResult["minute"] = time == nil ? NSNull() : time?.minute
26
+ alarmsResult.append(alarmResult)
27
+ }
28
+ var result = JSObject()
29
+ result["alarms"] = alarmsResult
30
+ return result as AnyObject
31
+ }
32
+
33
+ private static func getTimeFromAlarm(_ alarm: AlarmKit.Alarm) -> (hour: Int, minute: Int)? {
34
+ switch alarm.schedule {
35
+ case .fixed(let date):
36
+ let components = Calendar.current.dateComponents([.hour, .minute], from: date)
37
+ guard let hour = components.hour, let minute = components.minute else {
38
+ return nil
39
+ }
40
+ return (hour, minute)
41
+ case .relative(let relative):
42
+ return (relative.time.hour, relative.time.minute)
43
+ case .none:
44
+ return nil
45
+ @unknown default:
46
+ return nil
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,16 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public class IsAvailableResult: NSObject, Result {
5
+ let available: Bool
6
+
7
+ init(available: Bool) {
8
+ self.available = available
9
+ }
10
+
11
+ @objc public func toJSObject() -> AnyObject {
12
+ var result = JSObject()
13
+ result["available"] = available
14
+ return result as AnyObject
15
+ }
16
+ }
@@ -0,0 +1,16 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public class PermissionStatusResult: NSObject, Result {
5
+ let alarms: String
6
+
7
+ init(alarms: String) {
8
+ self.alarms = alarms
9
+ }
10
+
11
+ @objc public func toJSObject() -> AnyObject {
12
+ var result = JSObject()
13
+ result["alarms"] = alarms
14
+ return result as AnyObject
15
+ }
16
+ }
@@ -0,0 +1,47 @@
1
+ import Foundation
2
+
3
+ enum CustomError: Error {
4
+ case daysInvalid
5
+ case hourInvalid
6
+ case hourMissing
7
+ case idInvalid
8
+ case idMissing
9
+ case minuteInvalid
10
+ case minuteMissing
11
+ case permissionDenied
12
+ case privacyDescriptionsMissing
13
+
14
+ var code: String? {
15
+ switch self {
16
+ case .permissionDenied:
17
+ return "PERMISSION_DENIED"
18
+ case .daysInvalid, .hourInvalid, .hourMissing, .idInvalid, .idMissing, .minuteInvalid, .minuteMissing, .privacyDescriptionsMissing:
19
+ return nil
20
+ }
21
+ }
22
+ }
23
+
24
+ extension CustomError: LocalizedError {
25
+ public var errorDescription: String? {
26
+ switch self {
27
+ case .daysInvalid:
28
+ return NSLocalizedString("days must contain only valid weekdays.", comment: "daysInvalid")
29
+ case .hourInvalid:
30
+ return NSLocalizedString("hour must be between 0 and 23.", comment: "hourInvalid")
31
+ case .hourMissing:
32
+ return NSLocalizedString("hour must be provided.", comment: "hourMissing")
33
+ case .idInvalid:
34
+ return NSLocalizedString("id must be a valid UUID.", comment: "idInvalid")
35
+ case .idMissing:
36
+ return NSLocalizedString("id must be provided.", comment: "idMissing")
37
+ case .minuteInvalid:
38
+ return NSLocalizedString("minute must be between 0 and 59.", comment: "minuteInvalid")
39
+ case .minuteMissing:
40
+ return NSLocalizedString("minute must be provided.", comment: "minuteMissing")
41
+ case .permissionDenied:
42
+ return NSLocalizedString("The permission to schedule alarms was denied.", comment: "permissionDenied")
43
+ case .privacyDescriptionsMissing:
44
+ return NSLocalizedString("One or more privacy descriptions are missing.", comment: "privacyDescriptionsMissing")
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>CFBundleDevelopmentRegion</key>
6
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
7
+ <key>CFBundleExecutable</key>
8
+ <string>$(EXECUTABLE_NAME)</string>
9
+ <key>CFBundleIdentifier</key>
10
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+ <key>CFBundleInfoDictionaryVersion</key>
12
+ <string>6.0</string>
13
+ <key>CFBundleName</key>
14
+ <string>$(PRODUCT_NAME)</string>
15
+ <key>CFBundlePackageType</key>
16
+ <string>FMWK</string>
17
+ <key>CFBundleShortVersionString</key>
18
+ <string>1.0</string>
19
+ <key>CFBundleVersion</key>
20
+ <string>$(CURRENT_PROJECT_VERSION)</string>
21
+ <key>NSPrincipalClass</key>
22
+ <string></string>
23
+ </dict>
24
+ </plist>
@@ -0,0 +1,6 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc public protocol Result {
5
+ @objc func toJSObject() -> AnyObject
6
+ }
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "@capawesome/capacitor-alarm",
3
+ "version": "0.0.1",
4
+ "description": "Capacitor plugin to create system alarms and timers.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Plugin/",
14
+ "CapawesomeCapacitorAlarm.podspec",
15
+ "Package.swift"
16
+ ],
17
+ "author": "Robin Genz <mail@robingenz.dev>",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/capawesome-team/capacitor-plugins.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/capawesome-team/capacitor-plugins/issues"
25
+ },
26
+ "funding": [
27
+ {
28
+ "type": "github",
29
+ "url": "https://github.com/sponsors/capawesome-team/"
30
+ },
31
+ {
32
+ "type": "opencollective",
33
+ "url": "https://opencollective.com/capawesome"
34
+ }
35
+ ],
36
+ "homepage": "https://capawesome.io/docs/sdks/capacitor/alarm/",
37
+ "keywords": [
38
+ "capacitor",
39
+ "plugin",
40
+ "native",
41
+ "alarm",
42
+ "alarmkit",
43
+ "timer",
44
+ "clock"
45
+ ],
46
+ "scripts": {
47
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
48
+ "verify:ios": "cd ios && pod install && xcodebuild -workspace Plugin.xcworkspace -scheme Plugin -destination generic/platform=iOS && cd ..",
49
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
50
+ "verify:web": "npm run build",
51
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
52
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
53
+ "eslint": "eslint . --ext ts",
54
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\"",
55
+ "swiftlint": "node-swiftlint",
56
+ "docgen": "docgen --api AlarmPlugin --output-readme README.md --output-json dist/docs.json",
57
+ "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
58
+ "clean": "rimraf ./dist",
59
+ "watch": "tsc --watch",
60
+ "ios:pod:install": "cd ios && pod install --repo-update && cd ..",
61
+ "ios:spm:install": "cd ios && swift package resolve && cd ..",
62
+ "prepublishOnly": "npm run build"
63
+ },
64
+ "devDependencies": {
65
+ "@capacitor/android": "8.0.0",
66
+ "@capacitor/cli": "8.0.0",
67
+ "@capacitor/core": "8.0.0",
68
+ "@capacitor/docgen": "0.3.1",
69
+ "@capacitor/ios": "8.0.0",
70
+ "@ionic/eslint-config": "0.4.0",
71
+ "eslint": "8.57.0",
72
+ "prettier-plugin-java": "2.6.7",
73
+ "rimraf": "6.1.2",
74
+ "rollup": "4.53.3",
75
+ "swiftlint": "2.0.0",
76
+ "typescript": "5.9.3"
77
+ },
78
+ "peerDependencies": {
79
+ "@capacitor/core": ">=8.0.0"
80
+ },
81
+ "eslintConfig": {
82
+ "extends": "@ionic/eslint-config/recommended"
83
+ },
84
+ "capacitor": {
85
+ "ios": {
86
+ "src": "ios"
87
+ },
88
+ "android": {
89
+ "src": "android"
90
+ }
91
+ },
92
+ "publishConfig": {
93
+ "access": "public"
94
+ }
95
+ }