Ver Fonte

[Auth] Add Swift 6 conformance to `FirebaseAuth/Sources/Swift/ActionCode/` directory (#14833)

Co-authored-by: Morgan Chen <morganchen12@gmail.com>
Nick Cooke há 10 meses atrás
pai
commit
e7eaa4e9ef

+ 4 - 1
FirebaseAuth/Sources/Swift/ActionCode/ActionCodeInfo.swift

@@ -14,8 +14,11 @@
 
 import Foundation
 
+// TODO(Swift 6 Breaking): This type is immutable. Consider removing `open` at
+// breaking change so checked Sendable can be used.
+
 /// Manages information regarding action codes.
-@objc(FIRActionCodeInfo) open class ActionCodeInfo: NSObject {
+@objc(FIRActionCodeInfo) open class ActionCodeInfo: NSObject, @unchecked Sendable {
   /// The operation being performed.
   @objc public let operation: ActionCodeOperation
 

+ 1 - 1
FirebaseAuth/Sources/Swift/ActionCode/ActionCodeOperation.swift

@@ -15,7 +15,7 @@
 import Foundation
 
 /// Operations which can be performed with action codes.
-@objc(FIRActionCodeOperation) public enum ActionCodeOperation: Int, @unchecked Sendable {
+@objc(FIRActionCodeOperation) public enum ActionCodeOperation: Int, Sendable {
   /// Action code for unknown operation.
   case unknown = 0
 

+ 92 - 14
FirebaseAuth/Sources/Swift/ActionCode/ActionCodeSettings.swift

@@ -12,32 +12,55 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import FirebaseCoreInternal
 import Foundation
 
+// TODO(Swift 6 Breaking): Consider breaking up into a checked Sendable Swift
+// type and unchecked Sendable ObjC wrapper class.
+
 /// Used to set and retrieve settings related to handling action codes.
-@objc(FIRActionCodeSettings) open class ActionCodeSettings: NSObject {
+@objc(FIRActionCodeSettings) open class ActionCodeSettings: NSObject,
+  @unchecked Sendable {
   /// This URL represents the state/Continue URL in the form of a universal link.
   ///
   /// This URL can should be constructed as a universal link that would either directly open
   /// the app where the action code would be handled or continue to the app after the action code
   /// is handled by Firebase.
-  @objc(URL) open var url: URL?
+  @objc(URL) open var url: URL? {
+    get { impl.url.value() }
+    set { impl.url.withLock { $0 = newValue } }
+  }
 
   /// Indicates whether the action code link will open the app directly or after being
   /// redirected from a Firebase owned web widget.
-  @objc open var handleCodeInApp: Bool = false
+  @objc open var handleCodeInApp: Bool {
+    get { impl.handleCodeInApp.value() }
+    set { impl.handleCodeInApp.withLock { $0 = newValue } }
+  }
 
   /// The iOS bundle ID, if available. The default value is the current app's bundle ID.
-  @objc open var iOSBundleID: String?
+  @objc open var iOSBundleID: String? {
+    get { impl.iOSBundleID.value() }
+    set { impl.iOSBundleID.withLock { $0 = newValue } }
+  }
 
   /// The Android package name, if available.
-  @objc open var androidPackageName: String?
+  @objc open var androidPackageName: String? {
+    get { impl.androidPackageName.value() }
+    set { impl.androidPackageName.withLock { $0 = newValue } }
+  }
 
   /// The minimum Android version supported, if available.
-  @objc open var androidMinimumVersion: String?
+  @objc open var androidMinimumVersion: String? {
+    get { impl.androidMinimumVersion.value() }
+    set { impl.androidMinimumVersion.withLock { $0 = newValue } }
+  }
 
   /// Indicates whether the Android app should be installed on a device where it is not available.
-  @objc open var androidInstallIfNotAvailable: Bool = false
+  @objc open var androidInstallIfNotAvailable: Bool {
+    get { impl.androidInstallIfNotAvailable.value() }
+    set { impl.androidInstallIfNotAvailable.withLock { $0 = newValue } }
+  }
 
   /// The Firebase Dynamic Link domain used for out of band code flow.
   #if !FIREBASE_CI
@@ -47,14 +70,22 @@ import Foundation
       message: "Firebase Dynamic Links is deprecated. Migrate to use Firebase Hosting link and use `linkDomain` to set a custom domain instead."
     )
   #endif // !FIREBASE_CI
-  @objc open var dynamicLinkDomain: String?
+  @objc open var dynamicLinkDomain: String? {
+    get { impl.dynamicLinkDomain.value() }
+    set { impl.dynamicLinkDomain.withLock { $0 = newValue } }
+  }
 
   /// The out of band custom domain for handling code in app.
-  @objc public var linkDomain: String?
+  @objc public var linkDomain: String? {
+    get { impl.linkDomain.value() }
+    set { impl.linkDomain.withLock { $0 = newValue } }
+  }
+
+  private let impl: SendableActionCodeSettings
 
   /// Sets the iOS bundle ID.
   @objc override public init() {
-    iOSBundleID = Bundle.main.bundleIdentifier
+    impl = .init()
   }
 
   /// Sets the Android package name, the flag to indicate whether or not to install the app,
@@ -70,13 +101,60 @@ import Foundation
   @objc open func setAndroidPackageName(_ androidPackageName: String,
                                         installIfNotAvailable: Bool,
                                         minimumVersion: String?) {
-    self.androidPackageName = androidPackageName
-    androidInstallIfNotAvailable = installIfNotAvailable
-    androidMinimumVersion = minimumVersion
+    impl
+      .setAndroidPackageName(
+        androidPackageName,
+        installIfNotAvailable: installIfNotAvailable,
+        minimumVersion: minimumVersion
+      )
   }
 
   /// Sets the iOS bundle ID.
   open func setIOSBundleID(_ bundleID: String) {
-    iOSBundleID = bundleID
+    impl.setIOSBundleID(bundleID)
+  }
+}
+
+private extension ActionCodeSettings {
+  /// Checked Sendable implementation of `ActionCodeSettings`.
+  final class SendableActionCodeSettings: Sendable {
+    let url = FIRAllocatedUnfairLock<URL?>(initialState: nil)
+
+    let handleCodeInApp = FIRAllocatedUnfairLock<Bool>(initialState: false)
+
+    let iOSBundleID: FIRAllocatedUnfairLock<String?>
+
+    let androidPackageName = FIRAllocatedUnfairLock<String?>(initialState: nil)
+
+    let androidMinimumVersion = FIRAllocatedUnfairLock<String?>(initialState: nil)
+
+    let androidInstallIfNotAvailable = FIRAllocatedUnfairLock<Bool>(initialState: false)
+
+    #if !FIREBASE_CI
+      @available(
+        *,
+        deprecated,
+        message: "Firebase Dynamic Links is deprecated. Migrate to use Firebase Hosting link and use `linkDomain` to set a custom domain instead."
+      )
+    #endif // !FIREBASE_CI
+    let dynamicLinkDomain = FIRAllocatedUnfairLock<String?>(initialState: nil)
+
+    let linkDomain = FIRAllocatedUnfairLock<String?>(initialState: nil)
+
+    init() {
+      iOSBundleID = FIRAllocatedUnfairLock<String?>(initialState: Bundle.main.bundleIdentifier)
+    }
+
+    func setAndroidPackageName(_ androidPackageName: String,
+                               installIfNotAvailable: Bool,
+                               minimumVersion: String?) {
+      self.androidPackageName.withLock { $0 = androidPackageName }
+      androidInstallIfNotAvailable.withLock { $0 = installIfNotAvailable }
+      androidMinimumVersion.withLock { $0 = minimumVersion }
+    }
+
+    func setIOSBundleID(_ bundleID: String) {
+      iOSBundleID.withLock { $0 = bundleID }
+    }
   }
 }

+ 4 - 1
FirebaseAuth/Sources/Swift/ActionCode/ActionCodeURL.swift

@@ -14,8 +14,11 @@
 
 import Foundation
 
+// TODO(Swift 6 Breaking): This type is immutable. Consider removing `open` at
+// breaking change so checked Sendable can be used.
+
 /// This class will allow developers to easily extract information about out of band links.
-@objc(FIRActionCodeURL) open class ActionCodeURL: NSObject {
+@objc(FIRActionCodeURL) open class ActionCodeURL: NSObject, @unchecked Sendable {
   /// Returns the API key from the link. nil, if not provided.
   @objc(APIKey) public let apiKey: String?
 

+ 6 - 0
FirebaseCore/Internal/Sources/Utilities/FIRAllocatedUnfairLock.swift

@@ -42,6 +42,12 @@ public final class FIRAllocatedUnfairLock<State>: @unchecked Sendable {
     os_unfair_lock_unlock(lockPointer)
   }
 
+  public func value() -> State {
+    lock()
+    defer { unlock() }
+    return state
+  }
+
   @discardableResult
   public func withLock<R>(_ body: (inout State) throws -> R) rethrows -> R {
     let value: R