From 1ffa5df3818195987c3c6392383c64279bb009d1 Mon Sep 17 00:00:00 2001 From: vborko Date: Fri, 17 Oct 2025 03:28:01 +0200 Subject: [PATCH 01/11] Fix HTTPSCallableResult.data crash - add return statement fallback --- Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index 266a5d2..212a4b6 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -90,6 +90,7 @@ public class HTTPSCallableResult: KotlinConverting Date: Fri, 17 Oct 2025 12:07:23 +0200 Subject: [PATCH 02/11] Simplify HTTPSCallableResult.data to use nil-coalescing --- .../SkipFirebaseFunctions/SkipFirebaseFunctions.swift | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index 212a4b6..df72d0d 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -86,13 +86,9 @@ public class HTTPSCallableResult: KotlinConverting Date: Fri, 17 Oct 2025 12:30:08 +0200 Subject: [PATCH 03/11] Use intermediate variable for getData() to help Skip transpilation --- Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index df72d0d..e4a5168 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -86,9 +86,11 @@ public class HTTPSCallableResult: KotlinConverting Date: Fri, 17 Oct 2025 12:35:11 +0200 Subject: [PATCH 04/11] Add getDataSafe() method as alternative to data property --- .../SkipFirebaseFunctions.swift | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index e4a5168..38bbb08 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -91,6 +91,21 @@ public class HTTPSCallableResult: KotlinConverting [String: Any] { + guard let rawData = platformValue.getData() else { + return [String: Any]() + } + + // Try to cast to dictionary + if let dict = rawData as? [String: Any] { + return dict + } + + // Fallback: return empty dict + return [String: Any]() + } } #endif From 9bba7b336bff31fd7c39a322388d25a33410b2cd Mon Sep 17 00:00:00 2001 From: vborko Date: Fri, 17 Oct 2025 12:39:56 +0200 Subject: [PATCH 05/11] Change getDataSafe() to return Any for better Kotlin bridging --- .../SkipFirebaseFunctions.swift | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index 38bbb08..124c435 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -93,18 +93,11 @@ public class HTTPSCallableResult: KotlinConverting [String: Any] { - guard let rawData = platformValue.getData() else { - return [String: Any]() - } - - // Try to cast to dictionary - if let dict = rawData as? [String: Any] { - return dict - } - - // Fallback: return empty dict - return [String: Any]() + // Returns Any to avoid generic casting issues in Kotlin + public func getDataSafe() -> Any { + // Return raw data from Android Firebase SDK + // Let Skip bridge the Kotlin HashMap to Swift types + return platformValue.getData() ?? [String: Any]() } } From 51b78aba93c4d8b8acde03715837d1aef3c1d94a Mon Sep 17 00:00:00 2001 From: vborko Date: Fri, 17 Oct 2025 12:41:44 +0200 Subject: [PATCH 06/11] Add deepSwift() helpers to convert Kotlin collections to Swift types (from Firestore) --- .../SkipFirebaseFunctions.swift | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index 124c435..27ccde0 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -92,13 +92,44 @@ public class HTTPSCallableResult: KotlinConverting Any { - // Return raw data from Android Firebase SDK - // Let Skip bridge the Kotlin HashMap to Swift types - return platformValue.getData() ?? [String: Any]() + guard let rawData = platformValue.getData() else { + return [String: Any]() + } + // Convert Kotlin collections to Swift types (same approach as Firestore) + return deepSwift(value: rawData) + } +} + +// MARK: - Kotlin to Swift type conversion helpers (from SkipFirebaseFirestore) + +fileprivate func deepSwift(value: Any) -> Any { + if let str = value as? String { + return str // needed to not be treated as a Collection + } else if let map = value as? kotlin.collections.Map { + return deepSwift(map: map) + } else if let collection = value as? kotlin.collections.Collection { + return deepSwift(collection: collection) + } else { + return value + } +} + +fileprivate func deepSwift(map: kotlin.collections.Map) -> Dictionary { + var dict = Dictionary() + for (key, value) in map { + dict[key] = deepSwift(value: value) + } + return dict +} + +fileprivate func deepSwift(collection: kotlin.collections.Collection) -> Array { + var array = Array() + for value in collection { + array.append(deepSwift(value: value)) } + return array } #endif From f3f3b207a5ea470ac1727945d722c2f6116bae79 Mon Sep 17 00:00:00 2001 From: vborko Date: Mon, 29 Dec 2025 18:19:42 +0100 Subject: [PATCH 07/11] Update SkipFirebaseCrashlytics.swift --- Sources/SkipFirebaseCrashlytics/SkipFirebaseCrashlytics.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/SkipFirebaseCrashlytics/SkipFirebaseCrashlytics.swift b/Sources/SkipFirebaseCrashlytics/SkipFirebaseCrashlytics.swift index fc90b14..d3feac3 100644 --- a/Sources/SkipFirebaseCrashlytics/SkipFirebaseCrashlytics.swift +++ b/Sources/SkipFirebaseCrashlytics/SkipFirebaseCrashlytics.swift @@ -21,6 +21,10 @@ public final class Crashlytics { // public static func crashlytics(app: FirebaseApp) -> Crashlytics { // Crashlytics(crashlytics: com.google.firebase.crashlytics.FirebaseCrashlytics.getInstance(app.app)) // } + + public func log(_ message: String) { + _crashlytics.log(message) + } } #endif #endif From c3967f7ca4eaf46f42454aaed646800640db864a Mon Sep 17 00:00:00 2001 From: vborko Date: Mon, 12 Jan 2026 00:47:48 +0100 Subject: [PATCH 08/11] merge firebase and functions deepswift() into shared core --- .../SkipFirebaseCore/SkipFirebaseCore.swift | 84 +++++++++++++++++++ .../SkipFirebaseFirestore.swift | 81 ------------------ .../SkipFirebaseFunctions.swift | 30 ------- 3 files changed, 84 insertions(+), 111 deletions(-) diff --git a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift index bc3f310..ab29429 100644 --- a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift +++ b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift @@ -140,5 +140,89 @@ public final class FirebaseOptions { } } +// https://firebase.google.com/docs/reference/swift/firebasefirestore/api/reference/Classes/Timestamp +// https://firebase.google.com/docs/reference/android/com/google/firebase/Timestamp + +public class Timestamp: Hashable, KotlinConverting { + public let timestamp: com.google.firebase.Timestamp + + public init(timestamp: com.google.firebase.Timestamp) { + self.timestamp = timestamp + } + + public init(date: Date) { + self.timestamp = com.google.firebase.Timestamp(date.kotlin()) + } + + public init(seconds: Int64, nanoseconds: Int32) { + self.timestamp = com.google.firebase.Timestamp(seconds, nanoseconds) + } + + // SKIP @nooverride + public override func kotlin(nocopy: Bool = false) -> com.google.firebase.Timestamp { + timestamp + } + + public var description: String { + timestamp.toString() + } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.timestamp == rhs.timestamp + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(timestamp.hashCode()) + } + + public func dateValue() -> Date { + Date(platformValue: timestamp.toDate()) + } + + public func toDate() -> Date { + dateValue() + } + + public var seconds: Int64 { + timestamp.seconds + } + + public var nanoseconds: Int32 { + timestamp.nanoseconds + } +} + +// MARK: - Kotlin to Swift type conversion helpers + +public func deepSwift(value: Any) -> Any { + if let str = value as? String { + return str // needed to not be treated as a Collection + } else if let ts = value as? com.google.firebase.Timestamp { + return Timestamp(timestamp: ts) + } else if let map = value as? kotlin.collections.Map { + return deepSwift(map: map) + } else if let collection = value as? kotlin.collections.Collection { + return deepSwift(collection: collection) + } else { + return value + } +} + +public func deepSwift(map: kotlin.collections.Map) -> Dictionary { + var dict = Dictionary() + for (key, value) in map { + dict[key] = deepSwift(value: value) + } + return dict +} + +public func deepSwift(collection: kotlin.collections.Collection) -> Array { + var array = Array() + for value in collection { + array.append(deepSwift(value: value)) + } + return array +} + #endif #endif diff --git a/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift b/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift index 34ac908..b12fdfc 100644 --- a/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift +++ b/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift @@ -957,55 +957,6 @@ public class DocumentReference: KotlinConverting { - public let timestamp: com.google.firebase.Timestamp - - public init(timestamp: com.google.firebase.Timestamp) { - self.timestamp = timestamp - } - - public init(date: Date) { - self.timestamp = com.google.firebase.Timestamp(date.kotlin()) - } - - public init(seconds: Int64, nanoseconds: Int32) { - self.timestamp = com.google.firebase.Timestamp(seconds, nanoseconds) - } - - // SKIP @nooverride - public override func kotlin(nocopy: Bool = false) -> com.google.firebase.Timestamp { - timestamp - } - - public var description: String { - timestamp.toString() - } - - public static func == (lhs: Self, rhs: Self) -> Bool { - lhs.timestamp == rhs.timestamp - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(timestamp.hashCode()) - } - - public func dateValue() -> Date { - Date(platformValue: timestamp.toDate()) - } - - public func toDate() -> Date { - dateValue() - } - - public var seconds: Int64 { - timestamp.seconds - } - - public var nanoseconds: Int32 { - timestamp.nanoseconds - } -} - public class WriteBatch { public let batch: com.google.firebase.firestore.WriteBatch @@ -1102,37 +1053,5 @@ fileprivate func asNSError(firestoreException: com.google.firebase.firestore.Fir return NSError(domain: FirestoreErrorDomain, code: firestoreException.code.value(), userInfo: userInfo) } -// MARK: Utilies for converting between Swift and Kotlin types - -fileprivate func deepSwift(value: Any) -> Any { - if let str = value as? String { - return str // needed to not be treated as a Collection - } else if let ts = value as? com.google.firebase.Timestamp { - return Timestamp(timestamp: ts) - } else if let map = value as? kotlin.collections.Map { - return deepSwift(map: map) - } else if let collection = value as? kotlin.collections.Collection { - return deepSwift(collection: collection) - } else { - return value - } -} - -fileprivate func deepSwift(map: kotlin.collections.Map) -> Dictionary { - var dict = Dictionary() - for (key, value) in map { - dict[key] = deepSwift(value: value) - } - return dict -} - -fileprivate func deepSwift(collection: kotlin.collections.Collection) -> Array { - var array = Array() - for value in collection { - array.append(deepSwift(value: value)) - } - return array -} - #endif #endif diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index 27ccde0..64e3cec 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -102,35 +102,5 @@ public class HTTPSCallableResult: KotlinConverting Any { - if let str = value as? String { - return str // needed to not be treated as a Collection - } else if let map = value as? kotlin.collections.Map { - return deepSwift(map: map) - } else if let collection = value as? kotlin.collections.Collection { - return deepSwift(collection: collection) - } else { - return value - } -} - -fileprivate func deepSwift(map: kotlin.collections.Map) -> Dictionary { - var dict = Dictionary() - for (key, value) in map { - dict[key] = deepSwift(value: value) - } - return dict -} - -fileprivate func deepSwift(collection: kotlin.collections.Collection) -> Array { - var array = Array() - for value in collection { - array.append(deepSwift(value: value)) - } - return array -} - #endif #endif From 4ff8e9eefab41f3086fef15738555b0e5ebc6162 Mon Sep 17 00:00:00 2001 From: vborko Date: Fri, 16 Jan 2026 00:18:32 +0100 Subject: [PATCH 09/11] only one public func for deepswift, rest private --- Sources/SkipFirebaseCore/SkipFirebaseCore.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift index ab29429..7b816ad 100644 --- a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift +++ b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift @@ -208,7 +208,7 @@ public func deepSwift(value: Any) -> Any { } } -public func deepSwift(map: kotlin.collections.Map) -> Dictionary { +private func deepSwift(map: kotlin.collections.Map) -> Dictionary { var dict = Dictionary() for (key, value) in map { dict[key] = deepSwift(value: value) @@ -216,7 +216,7 @@ public func deepSwift(map: kotlin.collections.Map) -> Dictionary) -> Array { +private func deepSwift(collection: kotlin.collections.Collection) -> Array { var array = Array() for value in collection { array.append(deepSwift(value: value)) From 63b43035034f09bc0c8d7bd14c41f52e5f8a49a0 Mon Sep 17 00:00:00 2001 From: vborko Date: Fri, 16 Jan 2026 01:29:43 +0100 Subject: [PATCH 10/11] make deepswift helpers internal, add typealias for Firebase Timestamp in firestore, functions --- Sources/SkipFirebaseCore/SkipFirebaseCore.swift | 4 ++-- Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift | 2 ++ Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift index 7b816ad..801b869 100644 --- a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift +++ b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift @@ -208,7 +208,7 @@ public func deepSwift(value: Any) -> Any { } } -private func deepSwift(map: kotlin.collections.Map) -> Dictionary { +internal func deepSwift(map: kotlin.collections.Map) -> Dictionary { var dict = Dictionary() for (key, value) in map { dict[key] = deepSwift(value: value) @@ -216,7 +216,7 @@ private func deepSwift(map: kotlin.collections.Map) -> Dictionary) -> Array { +internal func deepSwift(collection: kotlin.collections.Collection) -> Array { var array = Array() for value in collection { array.append(deepSwift(value: value)) diff --git a/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift b/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift index b12fdfc..5f8c93a 100644 --- a/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift +++ b/Sources/SkipFirebaseFirestore/SkipFirebaseFirestore.swift @@ -5,6 +5,8 @@ import Foundation import SkipFirebaseCore import kotlinx.coroutines.tasks.await +public typealias Timestamp = SkipFirebaseCore.Timestamp + public final class Firestore: KotlinConverting { public let store: com.google.firebase.firestore.FirebaseFirestore diff --git a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift index 64e3cec..8925ab9 100644 --- a/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift +++ b/Sources/SkipFirebaseFunctions/SkipFirebaseFunctions.swift @@ -5,6 +5,8 @@ import Foundation import SkipFirebaseCore import kotlinx.coroutines.tasks.await +public typealias Timestamp = SkipFirebaseCore.Timestamp + // https://firebase.google.com/docs/reference/swift/firebasefunctions/api/reference/Classes/Functions // https://firebase.google.com/docs/reference/android/com/google/firebase/functions/FirebaseFunctions From 965a3af1bb6052b1a08889850a93e75a6ad0b30d Mon Sep 17 00:00:00 2001 From: vborko Date: Fri, 16 Jan 2026 01:39:55 +0100 Subject: [PATCH 11/11] revert to public --- Sources/SkipFirebaseCore/SkipFirebaseCore.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift index 801b869..ab29429 100644 --- a/Sources/SkipFirebaseCore/SkipFirebaseCore.swift +++ b/Sources/SkipFirebaseCore/SkipFirebaseCore.swift @@ -208,7 +208,7 @@ public func deepSwift(value: Any) -> Any { } } -internal func deepSwift(map: kotlin.collections.Map) -> Dictionary { +public func deepSwift(map: kotlin.collections.Map) -> Dictionary { var dict = Dictionary() for (key, value) in map { dict[key] = deepSwift(value: value) @@ -216,7 +216,7 @@ internal func deepSwift(map: kotlin.collections.Map) -> Dictionary) -> Array { +public func deepSwift(collection: kotlin.collections.Collection) -> Array { var array = Array() for value in collection { array.append(deepSwift(value: value))