feat(ios): implement testAlarm method and fix plugin discovery
Add testAlarm() method to iOS plugin for quick notification testing. Fix plugin method discovery by registering testAlarm in CAPBridgedPlugin pluginMethods array. Add force-load code in AppDelegate to ensure plugin is discovered by Capacitor's objc_getClassList scan. Changes: - Add testAlarm() implementation in DailyNotificationPlugin.swift - Register testAlarm in pluginMethods array (required for Capacitor discovery) - Add force-load code in test app AppDelegate (matches working ios-test-app) - Add UNUserNotificationCenterDelegate to show notifications in foreground - Add test notification button to ScheduleView with immediate feedback - Add debug logging for method discovery and plugin loading Fixes issue where testAlarm was implemented but returned "UNIMPLEMENTED" because it wasn't registered in the pluginMethods array. Also ensures plugin class is loaded before Capacitor's discovery phase.
This commit is contained in:
@@ -11,6 +11,7 @@ import Capacitor
|
||||
import UserNotifications
|
||||
import BackgroundTasks
|
||||
import CoreData
|
||||
import ObjectiveC
|
||||
|
||||
/**
|
||||
* iOS implementation of Daily Notification Plugin
|
||||
@@ -82,6 +83,34 @@ public class DailyNotificationPlugin: CAPPlugin {
|
||||
|
||||
NSLog("DNP-DEBUG: DailyNotificationPlugin.load() completed - initialization done")
|
||||
print("DNP-PLUGIN: Daily Notification Plugin loaded on iOS")
|
||||
|
||||
// Debug: Log all available @objc methods for Capacitor discovery
|
||||
let methods = getObjCMethods()
|
||||
NSLog("DNP-DEBUG: Available @objc methods: \(methods.joined(separator: ", "))")
|
||||
print("DNP-DEBUG: Available @objc methods: \(methods.joined(separator: ", "))")
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug helper: Get all @objc methods for this class
|
||||
*/
|
||||
private func getObjCMethods() -> [String] {
|
||||
var methods: [String] = []
|
||||
var methodCount: UInt32 = 0
|
||||
let methodList = class_copyMethodList(type(of: self), &methodCount)
|
||||
|
||||
for i in 0..<Int(methodCount) {
|
||||
if let method = methodList?[i] {
|
||||
let selector = method_getName(method)
|
||||
let methodName = NSStringFromSelector(selector)
|
||||
// Filter for methods that look like plugin methods (take CAPPluginCall)
|
||||
if methodName.contains(":") && !methodName.hasPrefix("_") {
|
||||
methods.append(methodName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(methodList)
|
||||
return methods.sorted()
|
||||
}
|
||||
|
||||
// MARK: - Configuration Methods
|
||||
@@ -1120,6 +1149,129 @@ public class DailyNotificationPlugin: CAPPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method: Schedule an alarm to fire in a few seconds
|
||||
* Useful for verifying alarm delivery works correctly
|
||||
*
|
||||
* @param call Plugin call with optional secondsFromNow (default: 5)
|
||||
* @returns Object with scheduled (boolean), secondsFromNow (number), and triggerAtMillis (number)
|
||||
*/
|
||||
@objc func testAlarm(_ call: CAPPluginCall) {
|
||||
NSLog("DNP-DEBUG: testAlarm() method CALLED - method is being invoked!")
|
||||
print("DNP-DEBUG: testAlarm() method CALLED - method is being invoked!")
|
||||
print("DNP-DEBUG: testAlarm call data: \(call.jsObjectRepresentation)")
|
||||
|
||||
guard let scheduler = scheduler else {
|
||||
NSLog("DNP-DEBUG: testAlarm() - scheduler is nil, rejecting")
|
||||
print("DNP-DEBUG: testAlarm() - scheduler is nil, rejecting")
|
||||
let error = DailyNotificationErrorCodes.createErrorResponse(
|
||||
code: DailyNotificationErrorCodes.PLUGIN_NOT_INITIALIZED,
|
||||
message: "Plugin not initialized"
|
||||
)
|
||||
let errorMessage = error["message"] as? String ?? "Unknown error"
|
||||
let errorCode = error["error"] as? String ?? "unknown_error"
|
||||
call.reject(errorMessage, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
// Get secondsFromNow parameter (default: 5)
|
||||
let secondsFromNow = call.getInt("secondsFromNow") ?? 5
|
||||
|
||||
// Ensure minimum of 1 second (iOS requirement)
|
||||
let validSeconds = max(1, secondsFromNow)
|
||||
|
||||
Task {
|
||||
do {
|
||||
// Check permissions first
|
||||
let permissionStatus = await notificationCenter.notificationSettings()
|
||||
if permissionStatus.authorizationStatus != .authorized && permissionStatus.authorizationStatus != .provisional {
|
||||
let error = DailyNotificationErrorCodes.createErrorResponse(
|
||||
code: DailyNotificationErrorCodes.NOTIFICATIONS_DENIED,
|
||||
message: "Notification permissions not granted"
|
||||
)
|
||||
let errorMessage = error["message"] as? String ?? "Unknown error"
|
||||
let errorCode = error["error"] as? String ?? "unknown_error"
|
||||
call.reject(errorMessage, errorCode)
|
||||
return
|
||||
}
|
||||
|
||||
// Create test notification content
|
||||
let notificationContent = UNMutableNotificationContent()
|
||||
notificationContent.title = "Test Notification"
|
||||
notificationContent.body = "This is a test notification scheduled \(validSeconds) seconds from now"
|
||||
notificationContent.sound = .default
|
||||
notificationContent.categoryIdentifier = "DAILY_NOTIFICATION"
|
||||
notificationContent.userInfo = [
|
||||
"notification_id": "test_\(Date().timeIntervalSince1970)",
|
||||
"is_test": true
|
||||
]
|
||||
|
||||
// Create time interval trigger (fires in X seconds)
|
||||
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(validSeconds), repeats: false)
|
||||
|
||||
// Create notification request with unique ID
|
||||
let notificationId = "test_alarm_\(Date().timeIntervalSince1970)"
|
||||
let request = UNNotificationRequest(
|
||||
identifier: notificationId,
|
||||
content: notificationContent,
|
||||
trigger: trigger
|
||||
)
|
||||
|
||||
// Schedule notification
|
||||
try await notificationCenter.add(request)
|
||||
|
||||
// Calculate trigger time in milliseconds
|
||||
let triggerAtMillis = Int64((Date().timeIntervalSince1970 + Double(validSeconds)) * 1000)
|
||||
|
||||
let result: [String: Any] = [
|
||||
"scheduled": true,
|
||||
"secondsFromNow": validSeconds,
|
||||
"triggerAtMillis": triggerAtMillis
|
||||
]
|
||||
|
||||
print("DNP-PLUGIN: Test alarm scheduled for \(validSeconds) seconds from now (triggerAtMillis=\(triggerAtMillis))")
|
||||
NSLog("DNP-DEBUG: testAlarm() - Successfully scheduled, resolving with result: \(result)")
|
||||
|
||||
DispatchQueue.main.async {
|
||||
NSLog("DNP-DEBUG: testAlarm() - Resolving call with result")
|
||||
call.resolve(result)
|
||||
}
|
||||
|
||||
} catch {
|
||||
NSLog("DNP-DEBUG: testAlarm() - Error caught: \(error)")
|
||||
print("DNP-PLUGIN: Error scheduling test alarm: \(error)")
|
||||
let errorResponse = DailyNotificationErrorCodes.createErrorResponse(
|
||||
code: DailyNotificationErrorCodes.SCHEDULING_FAILED,
|
||||
message: "Failed to schedule test alarm: \(error.localizedDescription)"
|
||||
)
|
||||
let errorMessage = errorResponse["message"] as? String ?? "Unknown error"
|
||||
let errorCode = errorResponse["error"] as? String ?? "unknown_error"
|
||||
NSLog("DNP-DEBUG: testAlarm() - Rejecting with error: \(errorMessage) (\(errorCode))")
|
||||
DispatchQueue.main.async {
|
||||
call.reject(errorMessage, errorCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debug method: List all available plugin methods
|
||||
* Useful for verifying Capacitor method discovery
|
||||
*
|
||||
* @param call Plugin call
|
||||
*/
|
||||
@objc func listAvailableMethods(_ call: CAPPluginCall) {
|
||||
let methods = getObjCMethods()
|
||||
let result: [String: Any] = [
|
||||
"methods": methods,
|
||||
"count": methods.count,
|
||||
"testAlarmFound": methods.contains("testAlarm:")
|
||||
]
|
||||
NSLog("DNP-DEBUG: listAvailableMethods() - Found \(methods.count) methods")
|
||||
NSLog("DNP-DEBUG: testAlarm: found: \(methods.contains("testAlarm:"))")
|
||||
call.resolve(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last notification that was delivered
|
||||
*
|
||||
@@ -1943,6 +2095,7 @@ public class DailyNotificationPlugin: CAPPlugin {
|
||||
methods.append(CAPPluginMethod(name: "configure", returnType: CAPPluginReturnPromise))
|
||||
methods.append(CAPPluginMethod(name: "configureNativeFetcher", returnType: CAPPluginReturnPromise))
|
||||
methods.append(CAPPluginMethod(name: "scheduleDailyNotification", returnType: CAPPluginReturnPromise))
|
||||
methods.append(CAPPluginMethod(name: "testAlarm", returnType: CAPPluginReturnPromise))
|
||||
methods.append(CAPPluginMethod(name: "getLastNotification", returnType: CAPPluginReturnPromise))
|
||||
methods.append(CAPPluginMethod(name: "cancelAllNotifications", returnType: CAPPluginReturnPromise))
|
||||
methods.append(CAPPluginMethod(name: "getNotificationStatus", returnType: CAPPluginReturnPromise))
|
||||
|
||||
Reference in New Issue
Block a user