import SwiftRs
import Tauri
import Foundation
import UIKit
import os.log
private let logger = OSLog(subsystem: "com.danielerolli.beaver-notes", category: "AppIcon")
class ChangeArgs: Decodable {
let name: String
let disable: [String]?
let suppressNotification: Bool?
}
class ResetArgs: Decodable {
let disable: [String]?
let suppressNotification: Bool?
}
class AppIconPlugin: Plugin {
@objc public func isSupported(_ invoke: Invoke) throws {
DispatchQueue.main.sync {
let supported = UIApplication.shared.supportsAlternateIcons
os_log("isSupported: %@", log: logger, type: .info, supported ? "YES" : "NO")
invoke.resolve(["value": supported])
}
}
@objc public func getName(_ invoke: Invoke) throws {
DispatchQueue.main.sync {
let name = UIApplication.shared.alternateIconName
os_log("getName: %@", log: logger, type: .info, name ?? "nil (default)")
invoke.resolve(["value": name as Any? ?? NSNull()])
}
}
@objc public func change(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(ChangeArgs.self)
os_log("change requested: name=%@ suppress=%@", log: logger, type: .info, args.name, (args.suppressNotification ?? false) ? "YES" : "NO")
setIcon(iconName: args.name, suppressNotification: args.suppressNotification ?? false, invoke)
}
@objc public func reset(_ invoke: Invoke) throws {
let args = try invoke.parseArgs(ResetArgs.self)
os_log("reset requested", log: logger, type: .info)
setIcon(iconName: nil, suppressNotification: args.suppressNotification ?? false, invoke)
}
private func setIcon(iconName: String?, suppressNotification: Bool, _ invoke: Invoke) {
DispatchQueue.main.async {
guard UIApplication.shared.supportsAlternateIcons else {
os_log("setIcon rejected: alternate icons not supported on this device", log: logger, type: .error)
invoke.reject("Alternate icons not supported. This feature requires a real device (not simulator) and alternate icons configured in Xcode Assets.")
return
}
if suppressNotification {
if UIApplication.shared.responds(to: #selector(getter: UIApplication.supportsAlternateIcons)) {
typealias SetAlternateIconName = @convention(c) (NSObject, Selector, NSString?, @escaping (NSError) -> Void) -> Void
let selectorString = "_setAlternateIconName:completionHandler:"
let selector = NSSelectorFromString(selectorString)
let imp = UIApplication.shared.method(for: selector)
let method = unsafeBitCast(imp, to: SetAlternateIconName.self)
method(UIApplication.shared, selector, iconName as NSString?, { _ in
os_log("setIcon succeeded (suppressed): %@", log: logger, type: .info, iconName ?? "default")
})
invoke.resolve()
}
} else {
UIApplication.shared.setAlternateIconName(iconName) { error in
if let error = error {
os_log("setIcon failed: %@", log: logger, type: .error, error.localizedDescription)
invoke.reject(error.localizedDescription)
} else {
os_log("setIcon succeeded: %@", log: logger, type: .info, iconName ?? "default")
invoke.resolve()
}
}
}
}
}
}
@_cdecl("init_plugin_app_icon")
func initPlugin() -> Plugin {
os_log("AppIconPlugin initialized", log: logger, type: .info)
return AppIconPlugin()
}