From 02ea7150c5ec484c63b1469c986298e0eb4e7dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Sun, 25 Aug 2024 10:58:12 +0900 Subject: [PATCH 1/3] feat(api): add method getArgsData --- .../mobile/android/src/main/java/app/tauri/plugin/Invoke.kt | 4 ++++ core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt index d951ca55b211..fd7521566777 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt @@ -17,6 +17,10 @@ class Invoke( private val argsJson: String, private val jsonMapper: ObjectMapper ) { + fun getArgsData(): JsonNode { + return jsonMapper.readTree(argsJson) + } + fun parseArgs(cls: Class): T { return jsonMapper.readValue(argsJson, cls) } diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift index b1b5dbfe5f7f..76854939f29a 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift @@ -26,6 +26,11 @@ import UIKit self.sendChannelData = sendChannelData } + public func getArgsData() throws -> Any { + let jsonData = self.data.data(using: .utf8)! + return try JSONSerialization.jsonObject(with: jsonData, options: []) + } + public func parseArgs(_ type: T.Type) throws -> T { let jsonData = self.data.data(using: .utf8)! let decoder = JSONDecoder() From d319ddc0f3f76c1b632a9564fe6a4893d91da062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A6=8A=E5=8E=9F=E6=98=8C=E5=BD=A6?= Date: Sun, 25 Aug 2024 11:01:41 +0900 Subject: [PATCH 2/3] chore --- core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift index 76854939f29a..60afce776be3 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift @@ -26,9 +26,9 @@ import UIKit self.sendChannelData = sendChannelData } - public func getArgsData() throws -> Any { + public func getArgsData() throws -> [String: Any] { let jsonData = self.data.data(using: .utf8)! - return try JSONSerialization.jsonObject(with: jsonData, options: []) + return try JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any] } public func parseArgs(_ type: T.Type) throws -> T { From 91e38c2d11e6df6be41d9ba14da836f43d21d4ce Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 26 Aug 2024 14:23:00 -0300 Subject: [PATCH 3/3] return JSObject instead --- .changes/mobile-plugin-get-args.md | 6 ++ .../src/main/java/app/tauri/plugin/Invoke.kt | 8 +- .../mobile/ios-api/Sources/Tauri/Invoke.swift | 10 +- .../ios-api/Sources/Tauri/JSTypes.swift | 101 ++++++++++++++++++ 4 files changed, 121 insertions(+), 4 deletions(-) create mode 100644 .changes/mobile-plugin-get-args.md diff --git a/.changes/mobile-plugin-get-args.md b/.changes/mobile-plugin-get-args.md new file mode 100644 index 000000000000..2fb6dea4f445 --- /dev/null +++ b/.changes/mobile-plugin-get-args.md @@ -0,0 +1,6 @@ +--- +"tauri": patch:enhance +--- + +Added `getArgs` and `getRawArgs` methods to the plugin `Invoke` class (Kotlin and Swift), +which lets you parse the arguments manually instead of through the `parseArgs` method. diff --git a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt index fd7521566777..1f9d080078d9 100644 --- a/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt +++ b/core/tauri/mobile/android/src/main/java/app/tauri/plugin/Invoke.kt @@ -17,8 +17,12 @@ class Invoke( private val argsJson: String, private val jsonMapper: ObjectMapper ) { - fun getArgsData(): JsonNode { - return jsonMapper.readTree(argsJson) + fun getRawArgs(): String { + return argsJson + } + + fun getArgs(): JSObject { + return JSObject(argsJson) } fun parseArgs(cls: Class): T { diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift index 60afce776be3..6f810868fdf5 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/Invoke.swift @@ -26,9 +26,15 @@ import UIKit self.sendChannelData = sendChannelData } - public func getArgsData() throws -> [String: Any] { + public func getRawArgs() -> String { + return self.data + } + + public func getArgs() throws -> JSObject { let jsonData = self.data.data(using: .utf8)! - return try JSONSerialization.jsonObject(with: jsonData, options: []) as! [String: Any] + let data = try JSONSerialization.jsonObject(with: jsonData, options: []) + return JSTypes.coerceDictionaryToJSObject( + (data as! NSDictionary), formattingDatesAsStrings: true)! } public func parseArgs(_ type: T.Type) throws -> T { diff --git a/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift b/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift index 8fd5d2f2979c..0ac9f2c79f32 100644 --- a/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift +++ b/core/tauri/mobile/ios-api/Sources/Tauri/JSTypes.swift @@ -20,3 +20,104 @@ extension Dictionary: JSValue where Key == String, Value == JSValue {} // convenience aliases public typealias JSObject = [String: JSValue] public typealias JSArray = [JSValue] + +extension Dictionary where Key == String, Value == JSValue { + public func getValue(_ key: String) -> JSValue? { + return self[key] + } + + public func getString(_ key: String) -> String? { + return self[key] as? String + } + + public func getBool(_ key: String) -> Bool? { + return self[key] as? Bool + } + + public func getInt(_ key: String) -> Int? { + return self[key] as? Int + } + + public func getFloat(_ key: String) -> Float? { + if let floatValue = self[key] as? Float { + return floatValue + } else if let doubleValue = self[key] as? Double { + return Float(doubleValue) + } + return nil + } + + public func getDouble(_ key: String) -> Double? { + return self[key] as? Double + } + + public func getArray(_ key: String) -> JSArray? { + return self[key] as? JSArray + } + + public func getObject(_ key: String) -> JSObject? { + return self[key] as? JSObject + } +} + +/* + Simply casting objects from foundation class clusters (such as __NSArrayM) + doesn't work with the JSValue protocol and will always fail. So we need to + recursively and explicitly convert each value in the dictionary. + */ +public enum JSTypes {} +extension JSTypes { + public static func coerceDictionaryToJSObject( + _ dictionary: NSDictionary?, formattingDatesAsStrings: Bool = false + ) -> JSObject? { + return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject + } + + public static func coerceDictionaryToJSObject( + _ dictionary: [AnyHashable: Any]?, formattingDatesAsStrings: Bool = false + ) -> JSObject? { + return coerceToJSValue(dictionary, formattingDates: formattingDatesAsStrings) as? JSObject + } +} + +private let dateStringFormatter = ISO8601DateFormatter() + +// We need a large switch statement because we have a lot of types. +// swiftlint:disable:next cyclomatic_complexity +private func coerceToJSValue(_ value: Any?, formattingDates: Bool) -> JSValue? { + guard let value = value else { + return nil + } + switch value { + case let stringValue as String: + return stringValue + case let numberValue as NSNumber: + return numberValue + case let boolValue as Bool: + return boolValue + case let intValue as Int: + return intValue + case let floatValue as Float: + return floatValue + case let doubleValue as Double: + return doubleValue + case let dateValue as Date: + if formattingDates { + return dateStringFormatter.string(from: dateValue) + } + return dateValue + case let nullValue as NSNull: + return nullValue + case let arrayValue as NSArray: + return arrayValue.compactMap { coerceToJSValue($0, formattingDates: formattingDates) } + case let dictionaryValue as NSDictionary: + let keys = dictionaryValue.allKeys.compactMap { $0 as? String } + var result: JSObject = [:] + for key in keys { + result[key] = coerceToJSValue(dictionaryValue[key], formattingDates: formattingDates) + } + return result + default: + return nil + } +}