@@ -27,6 +27,36 @@ import Foundation
2727// Avoids exposing internal FirebaseCore APIs to Swift users.
2828@_implementationOnly import FirebaseCoreExtension
2929
30+ final class AtomicBox < T> {
31+ private var _value : T
32+ private let lock = NSLock ( )
33+
34+ public init ( _ value: T ) {
35+ _value = value
36+ }
37+
38+ public func value( ) -> T {
39+ lock. withLock {
40+ _value
41+ }
42+ }
43+
44+ @discardableResult
45+ public func withLock( _ mutatingBody: ( _ value: inout T ) -> Void ) -> T {
46+ lock. withLock {
47+ mutatingBody ( & _value)
48+ return _value
49+ }
50+ }
51+
52+ @discardableResult
53+ public func withLock< R> ( _ mutatingBody: ( _ value: inout T ) throws -> R ) rethrows -> R {
54+ try lock. withLock {
55+ try mutatingBody ( & _value)
56+ }
57+ }
58+ }
59+
3060/// File specific constants.
3161private enum Constants {
3262 static let appCheckTokenHeader = " X-Firebase-AppCheck "
@@ -53,10 +83,12 @@ enum FunctionsConstants {
5383
5484 /// A map of active instances, grouped by app. Keys are FirebaseApp names and values are arrays
5585 /// containing all instances of Functions associated with the given app.
56- private static var instances : [ String : [ Functions ] ] = [ : ]
57-
58- /// Lock to manage access to the instances array to avoid race conditions.
59- private static var instancesLock : os_unfair_lock = . init( )
86+ #if compiler(>=6.0)
87+ private nonisolated ( unsafe) static var instances : AtomicBox < [ String : [ Functions ] ] > =
88+ AtomicBox ( [ : ] )
89+ #else
90+ private static var instances : AtomicBox < [ String : [ Functions ] ] > = AtomicBox ( [ : ] )
91+ #endif
6092
6193 /// The custom domain to use for all functions references (optional).
6294 let customDomain : String ?
@@ -304,30 +336,28 @@ enum FunctionsConstants {
304336 guard let app else {
305337 fatalError ( " `FirebaseApp.configure()` needs to be called before using Functions. " )
306338 }
307- os_unfair_lock_lock ( & instancesLock)
308-
309- // Unlock before the function returns.
310- defer { os_unfair_lock_unlock ( & instancesLock) }
311-
312- if let associatedInstances = instances [ app. name] {
313- for instance in associatedInstances {
314- // Domains may be nil, so handle with care.
315- var equalDomains = false
316- if let instanceCustomDomain = instance. customDomain {
317- equalDomains = instanceCustomDomain == customDomain
318- } else {
319- equalDomains = customDomain == nil
320- }
321- // Check if it's a match.
322- if instance. region == region, equalDomains {
323- return instance
339+
340+ return instances. withLock { instances in
341+ if let associatedInstances = instances [ app. name] {
342+ for instance in associatedInstances {
343+ // Domains may be nil, so handle with care.
344+ var equalDomains = false
345+ if let instanceCustomDomain = instance. customDomain {
346+ equalDomains = instanceCustomDomain == customDomain
347+ } else {
348+ equalDomains = customDomain == nil
349+ }
350+ // Check if it's a match.
351+ if instance. region == region, equalDomains {
352+ return instance
353+ }
324354 }
325355 }
356+ let newInstance = Functions ( app: app, region: region, customDomain: customDomain)
357+ let existingInstances = instances [ app. name, default: [ ] ]
358+ instances [ app. name] = existingInstances + [ newInstance]
359+ return newInstance
326360 }
327- let newInstance = Functions ( app: app, region: region, customDomain: customDomain)
328- let existingInstances = instances [ app. name, default: [ ] ]
329- instances [ app. name] = existingInstances + [ newInstance]
330- return newInstance
331361 }
332362
333363 @objc init ( projectID: String ,
@@ -576,34 +606,65 @@ enum FunctionsConstants {
576606 }
577607 }
578608
579- @available ( macOS 12 . 0 , iOS 15 . 0 , watchOS 8 . 0 , tvOS 15 . 0 , * )
580- private func callableStreamResult( fromResponseData data: Data ,
581- endpointURL url: URL ) throws -> JSONStreamResponse {
582- let data = try processedData ( fromResponseData: data, endpointURL: url)
609+ #if compiler(>=6.0)
610+ @available ( macOS 12 . 0 , iOS 15 . 0 , watchOS 8 . 0 , tvOS 15 . 0 , * )
611+ private func callableStreamResult( fromResponseData data: Data ,
612+ endpointURL url: URL ) throws -> sending JSONStreamResponse {
613+ let data = try processedData ( fromResponseData: data, endpointURL: url)
614+
615+ let responseJSONObject : Any
616+ do {
617+ responseJSONObject = try JSONSerialization . jsonObject ( with: data)
618+ } catch {
619+ throw FunctionsError ( . dataLoss, userInfo: [ NSUnderlyingErrorKey: error] )
620+ }
583621
584- let responseJSONObject : Any
585- do {
586- responseJSONObject = try JSONSerialization . jsonObject ( with: data)
587- } catch {
588- throw FunctionsError ( . dataLoss, userInfo: [ NSUnderlyingErrorKey: error] )
589- }
622+ guard let responseJSON = responseJSONObject as? [ String : Any ] else {
623+ let userInfo = [ NSLocalizedDescriptionKey: " Response was not a dictionary. " ]
624+ throw FunctionsError ( . dataLoss, userInfo: userInfo)
625+ }
590626
591- guard let responseJSON = responseJSONObject as? [ String : Any ] else {
592- let userInfo = [ NSLocalizedDescriptionKey: " Response was not a dictionary. " ]
593- throw FunctionsError ( . dataLoss, userInfo: userInfo)
627+ if let _ = responseJSON [ " result " ] {
628+ return . result( responseJSON)
629+ } else if let _ = responseJSON [ " message " ] {
630+ return . message( responseJSON)
631+ } else {
632+ throw FunctionsError (
633+ . dataLoss,
634+ userInfo: [ NSLocalizedDescriptionKey: " Response is missing result or message field. " ]
635+ )
636+ }
594637 }
638+ #else
639+ @available ( macOS 12 . 0 , iOS 15 . 0 , watchOS 8 . 0 , tvOS 15 . 0 , * )
640+ private func callableStreamResult( fromResponseData data: Data ,
641+ endpointURL url: URL ) throws -> JSONStreamResponse {
642+ let data = try processedData ( fromResponseData: data, endpointURL: url)
643+
644+ let responseJSONObject : Any
645+ do {
646+ responseJSONObject = try JSONSerialization . jsonObject ( with: data)
647+ } catch {
648+ throw FunctionsError ( . dataLoss, userInfo: [ NSUnderlyingErrorKey: error] )
649+ }
595650
596- if let _ = responseJSON [ " result " ] {
597- return . result( responseJSON)
598- } else if let _ = responseJSON [ " message " ] {
599- return . message( responseJSON)
600- } else {
601- throw FunctionsError (
602- . dataLoss,
603- userInfo: [ NSLocalizedDescriptionKey: " Response is missing result or message field. " ]
604- )
651+ guard let responseJSON = responseJSONObject as? [ String : Any ] else {
652+ let userInfo = [ NSLocalizedDescriptionKey: " Response was not a dictionary. " ]
653+ throw FunctionsError ( . dataLoss, userInfo: userInfo)
654+ }
655+
656+ if let _ = responseJSON [ " result " ] {
657+ return . result( responseJSON)
658+ } else if let _ = responseJSON [ " message " ] {
659+ return . message( responseJSON)
660+ } else {
661+ throw FunctionsError (
662+ . dataLoss,
663+ userInfo: [ NSLocalizedDescriptionKey: " Response is missing result or message field. " ]
664+ )
665+ }
605666 }
606- }
667+ #endif // compiler(>=6.0)
607668
608669 private func jsonData( jsonText: String ) throws -> Data {
609670 guard let data = jsonText. data ( using: . utf8) else {
0 commit comments