-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Closed
Milestone
Description
Description
Xcode Version: 15.2
Minimum Target(iOS): 16.1
Minimun Target(watchOS): 9.0
The GameCenterAuthProvider is currently not defined for watchOS, but its extension targets watchOS, which is causing issues when trying to build Firebase for watchOS.
Temporary Workaround:
install Firebase 10.29.0 Version.
Problem:
• The FirebaseAuth library includes GameCenterAuthProvider functionality, but it appears that the core provider itself does not support watchOS.
• However, the GameCenterAuthProvider extension is set to target watchOS, which leads to build errors when the library is used in a watchOS project.
Expected Behavior:
• Either GameCenterAuthProvider should be defined and supported for watchOS, or the extension targeting should exclude watchOS to avoid any conflicts in watchOS builds.
Steps to Reproduce:
1. Add FirebaseAuth as a dependency using SPM in a project with both iOS and watchOS targets.
2. Attempt to build the project for watchOS.
3. Observe the build error related to GameCenterAuthProvider.
Code:
Error Here ::>>
#if canImport(Combine) && swift(>=5.0)
import Combine
import FirebaseAuth
@available(swift 5.0)
@available(iOS 13.0, macOS 10.15, macCatalyst 13.0, tvOS 13.0, *)
@available(watchOS, unavailable)
public extension GameCenterAuthProvider { // Error: Cannot find type 'GameCenterAuthProvider' in scope
/// Creates an `AuthCredential` for a Game Center sign in.
///
/// The publisher will emit events on the **main** thread.
///
/// - Returns: A publisher that emits an `AuthCredential` when the credential is obtained
/// successfully, or an error otherwise. The publisher will emit on the *main* thread.
class func getCredential() -> Future<AuthCredential, Error> {
Future<AuthCredential, Error> { promise in
self.getCredential { authCredential, error in
if let error {
promise(.failure(error))
} else if let authCredential {
promise(.success(authCredential))
}
}
}
}
}
#endif // canImport(Combine) && swift(>=5.0)
GameCenterAuthProvider Defined ::>>
#if !os(watchOS)
import Foundation
import GameKit
// TODO: Delete this when minimum iOS version passes 13.5.
/// WarningWorkaround is needed because playerID is deprecated in iOS 13.0 but still needed until
/// 13.5 when the fetchItems API was introduced.
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
private protocol WarningWorkaround {
static func pre135Credential(localPlayer: GKLocalPlayer,
completion: @escaping (AuthCredential?, Error?) -> Void)
}
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
extension GameCenterAuthProvider: WarningWorkaround {}
/// A concrete implementation of `AuthProvider` for Game Center Sign In. Not available on watchOS.
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
@objc(FIRGameCenterAuthProvider) open class GameCenterAuthProvider: NSObject {
/// A string constant identifying the Game Center identity provider.
@objc public static let id = "gc.apple.com"
/// Creates an `AuthCredential` for a Game Center sign in.
@objc open class func getCredential(completion: @escaping (AuthCredential?, Error?) -> Void) {
/**
Linking GameKit.framework without using it on macOS results in App Store rejection.
Thus we don't link GameKit.framework to our SDK directly. `optionalLocalPlayer` is used for
checking whether the APP that consuming our SDK has linked GameKit.framework. If not, a
`GameKitNotLinkedError` will be raised.
**/
guard let _: AnyClass = NSClassFromString("GKLocalPlayer") else {
completion(nil, AuthErrorUtils.gameKitNotLinkedError())
return
}
let localPlayer = GKLocalPlayer.local
guard localPlayer.isAuthenticated else {
completion(nil, AuthErrorUtils.localPlayerNotAuthenticatedError())
return
}
if #available(iOS 13.5, macOS 10.15.5, macCatalyst 13.5, tvOS 13.4.8, *) {
localPlayer.fetchItems { publicKeyURL, signature, salt, timestamp, error in
if let error = error {
completion(nil, error)
} else {
let credential = GameCenterAuthCredential(withPlayerID: "",
teamPlayerID: localPlayer.teamPlayerID,
gamePlayerID: localPlayer.gamePlayerID,
publicKeyURL: publicKeyURL,
signature: signature,
salt: salt,
timestamp: timestamp,
displayName: localPlayer.displayName)
completion(credential, nil)
}
}
} else {
(GameCenterAuthProvider.self as WarningWorkaround.Type).pre135Credential(
localPlayer: localPlayer, completion: completion
)
}
}
@available(iOS, deprecated: 13.0)
@available(tvOS, deprecated: 13.0)
@available(macOS, deprecated: 10.15.0)
@available(macCatalyst, deprecated: 13.0)
fileprivate class func pre135Credential(localPlayer: GKLocalPlayer,
completion: @escaping (AuthCredential?, Error?)
-> Void) {
localPlayer
.generateIdentityVerificationSignature { publicKeyURL, signature, salt, timestamp, error in
if error != nil {
completion(nil, error)
} else {
/**
`localPlayer.alias` is actually the displayname needed, instead of
`localPlayer.displayname`. For more information, check
https://developer.apple.com/documentation/gamekit/gkplayer
**/
let displayName = localPlayer.alias
let credential = GameCenterAuthCredential(withPlayerID: localPlayer.playerID,
teamPlayerID: nil,
gamePlayerID: nil,
publicKeyURL: publicKeyURL,
signature: signature,
salt: salt,
timestamp: timestamp,
displayName: displayName)
completion(credential, nil)
}
}
}
/// Creates an `AuthCredential` for a Game Center sign in.
@available(iOS 13, tvOS 13, macOS 10.15, watchOS 8, *)
open class func getCredential() async throws -> AuthCredential {
return try await withCheckedThrowingContinuation { continuation in
getCredential { credential, error in
if let credential = credential {
continuation.resume(returning: credential)
} else {
continuation.resume(throwing: error!) // TODO: Change to ?? and generate unknown error
}
}
}
}
@available(*, unavailable)
@objc override public init() {
fatalError("This class is not meant to be initialized.")
}
}
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
@objc(FIRGameCenterAuthCredential)
class GameCenterAuthCredential: AuthCredential, NSSecureCoding {
let playerID: String
let teamPlayerID: String?
let gamePlayerID: String?
let publicKeyURL: URL?
let signature: Data?
let salt: Data?
let timestamp: UInt64
let displayName: String
/// - Parameter playerID: The ID of the Game Center local player.
/// - Parameter teamPlayerID: The teamPlayerID of the Game Center local player.
/// - Parameter gamePlayerID: The gamePlayerID of the Game Center local player.
/// - Parameter publicKeyURL: The URL for the public encryption key.
/// - Parameter signature: The verification signature generated.
/// - Parameter salt: A random string used to compute the hash and keep it randomized.
/// - Parameter timestamp: The date and time that the signature was created.
/// - Parameter displayName: The display name of the Game Center player.
init(withPlayerID playerID: String, teamPlayerID: String?, gamePlayerID: String?,
publicKeyURL: URL?, signature: Data?, salt: Data?,
timestamp: UInt64, displayName: String) {
self.playerID = playerID
self.teamPlayerID = teamPlayerID
self.gamePlayerID = gamePlayerID
self.publicKeyURL = publicKeyURL
self.signature = signature
self.salt = salt
self.timestamp = timestamp
self.displayName = displayName
super.init(provider: GameCenterAuthProvider.id)
}
// MARK: Secure Coding
static var supportsSecureCoding = true
func encode(with coder: NSCoder) {
coder.encode(playerID, forKey: "playerID")
coder.encode(teamPlayerID, forKey: "teamPlayerID")
coder.encode(gamePlayerID, forKey: "gamePlayerID")
coder.encode(publicKeyURL, forKey: "publicKeyURL")
coder.encode(signature, forKey: "signature")
coder.encode(salt, forKey: "salt")
coder.encode(timestamp, forKey: "timestamp")
coder.encode(displayName, forKey: "displayName")
}
required init?(coder: NSCoder) {
guard let playerID = coder.decodeObject(of: NSString.self, forKey: "playerID") as? String,
let teamPlayerID = coder.decodeObject(
of: NSString.self,
forKey: "teamPlayerID"
) as? String,
let gamePlayerID = coder.decodeObject(
of: NSString.self,
forKey: "gamePlayerID"
) as? String,
let timestamp = coder.decodeObject(of: NSNumber.self, forKey: "timestamp") as? UInt64,
let displayName = coder.decodeObject(
of: NSString.self,
forKey: "displayName"
) as? String else {
return nil
}
self.playerID = playerID
self.teamPlayerID = teamPlayerID
self.gamePlayerID = gamePlayerID
self.timestamp = timestamp
self.displayName = displayName
publicKeyURL = coder.decodeObject(forKey: "publicKeyURL") as? URL
signature = coder.decodeObject(of: NSData.self, forKey: "signature") as? Data
salt = coder.decodeObject(of: NSData.self, forKey: "salt") as? Data
super.init(provider: GameCenterAuthProvider.id)
}
}
#endif
Reproducing the issue
No response
Firebase SDK Version
11.4
Xcode Version
15.2
Installation Method
Swift Package Manager
Firebase Product(s)
Authentication
Targeted Platforms
iOS, watchOS
Relevant Log Output
No response
If using Swift Package Manager, the project's Package.resolved
Expand Package.resolved
snippet
Replace this line with the contents of your Package.resolved.
If using CocoaPods, the project's Podfile.lock
Expand Podfile.lock
snippet
Replace this line with the contents of your Podfile.lock!