From 88e16fb2d8c45d036d56245f9495a024e9bc2b9c Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Thu, 14 Aug 2025 16:11:07 -0400 Subject: [PATCH 1/2] [Firebase AI] Add `Sendable` conformance to `PartsRepresentable` --- FirebaseAI/Sources/PartsRepresentable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FirebaseAI/Sources/PartsRepresentable.swift b/FirebaseAI/Sources/PartsRepresentable.swift index 82d0688df38..2de636e73c2 100644 --- a/FirebaseAI/Sources/PartsRepresentable.swift +++ b/FirebaseAI/Sources/PartsRepresentable.swift @@ -17,7 +17,7 @@ import Foundation /// A protocol describing any data that could be serialized to model-interpretable input data, /// where the serialization process cannot fail with an error. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) -public protocol PartsRepresentable { +public protocol PartsRepresentable: Sendable { var partsValue: [any Part] { get } } From ffdf538a6bd793a2b44700f0393f7ea5596b512f Mon Sep 17 00:00:00 2001 From: Andrew Heard Date: Thu, 14 Aug 2025 18:51:46 -0400 Subject: [PATCH 2/2] Try workaround for `Sendable` on Xcode 16.2 --- .../Sources/PartsRepresentable+Image.swift | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/FirebaseAI/Sources/PartsRepresentable+Image.swift b/FirebaseAI/Sources/PartsRepresentable+Image.swift index 5bedeb8f3d1..cfab1a69083 100644 --- a/FirebaseAI/Sources/PartsRepresentable+Image.swift +++ b/FirebaseAI/Sources/PartsRepresentable+Image.swift @@ -50,8 +50,8 @@ enum ImageConversionError: Error { #elseif canImport(AppKit) /// Enables images to be representable as ``PartsRepresentable``. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) - extension NSImage: PartsRepresentable { - public var partsValue: [any Part] { + public extension NSImage { + var partsValue: [any Part] { guard let cgImage = cgImage(forProposedRect: nil, context: nil, hints: nil) else { return [ErrorPart(ImageConversionError.invalidUnderlyingImage)] } @@ -63,13 +63,22 @@ enum ImageConversionError: Error { return [InlineDataPart(data: data, mimeType: "image/jpeg")] } } -#endif + + #if swift(>=6.2) + @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) + extension NSImage: PartsRepresentable {} + #else // swift(>=6.2) + @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *) + extension NSImage: @unchecked @retroactive Sendable, PartsRepresentable {} + #endif + +#endif // canImport(UIKit) #if !os(watchOS) // This code does not build on watchOS. /// Enables `CGImages` to be representable as model content. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, *) - extension CGImage: PartsRepresentable { - public var partsValue: [any Part] { + public extension CGImage { + var partsValue: [any Part] { let output = NSMutableData() guard let imageDestination = CGImageDestinationCreateWithData( output, UTType.jpeg.identifier as CFString, 1, nil @@ -86,13 +95,22 @@ enum ImageConversionError: Error { return [ErrorPart(ImageConversionError.couldNotConvertToJPEG)] } } + + #if swift(>=6.1) + @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, *) + extension CGImage: PartsRepresentable {} + #else // swift(>=6.1) + @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, *) + extension CGImage: @unchecked @retroactive Sendable, PartsRepresentable {} + #endif // swift(>=6.1) + #endif // !os(watchOS) #if canImport(CoreImage) /// Enables `CIImages` to be representable as model content. @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, *) - extension CIImage: PartsRepresentable { - public var partsValue: [any Part] { + public extension CIImage { + var partsValue: [any Part] { let context = CIContext() let jpegData = (colorSpace ?? CGColorSpace(name: CGColorSpace.sRGB)) .flatMap { @@ -107,4 +125,13 @@ enum ImageConversionError: Error { return [ErrorPart(ImageConversionError.couldNotConvertToJPEG)] } } + + #if swift(>=6.2) + @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, *) + extension CIImage: PartsRepresentable {} + #else // swift(>=6.2) + @available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, *) + extension CIImage: @unchecked @retroactive Sendable, PartsRepresentable {} + #endif // swift(>=6.2) + #endif // canImport(CoreImage)