I'm trying to isolate Firebase's phone authentication from their working-, with a real device and in a simulator, quickstart-ios as of firebase-ios-sdk v11.2. "Swizzling is disabled" in the simulator (2024), with which I want to achieve the recaptcha flow, first, as it's a fallback to APN's anyway.
ContentView.swift
enum FirebaseError: Error {
case Error
case VerificatrionEmpty
}
struct ContentView: View {
@State var verificationCode = ""
@State var newUsername = ""
@State var phoneNumber = ""
@State var countryCodeNumber = "+1"
@State var country = ""
@State var smsTextCode = ""
@State var loggedin = false
@State var verificationId = ""
@State var verifiable = false
//@State var session: User = User(coder: NSCoder())!
@State public var show: String = "home"
init() {
// Use Firebase library to configure APIs
//FirebaseApp.configure()
}
func signOut(){
if Auth.auth().currentUser != nil {
do {
try Auth.auth().signOut()
}
catch {
print (error)
}
}
loggedin = false
}
var body: some View {
if Auth.auth().currentUser == nil && !loggedin {
Form{
Section(footer: Text("Get an SMS code. Standard messaging rates apply.")) {
TextField("Country code", text: $countryCodeNumber)
.font(Font.system(size: 15))
.fontWeight(.semibold)
.frame(width: nil, height: nil, alignment: .leading)
.onChange(of: countryCodeNumber) {
verifiable = false
}
TextField("Enter your phone number", text: $phoneNumber)
.font(Font.system(size: 15))
.fontWeight(.semibold)
.frame(width: nil, height: nil, alignment: .leading)
.onChange(of: phoneNumber) {
verifiable = false
}
}
if verifiable {
Section{
TextField("SMS code", text: $verificationCode)
.font(Font.system(size: 15))
.fontWeight(.semibold)
.frame(width: nil, height: nil, alignment: .leading)
}
}
Button("Submit", action: {
if !verifiable {
/*let _ = Auth.auth().addStateDidChangeListener({ (auth, user) in
if let _ = user {
loggedin = true
//session = user
} else {
loggedin = false
//session = User(coder: NSCoder())!
}
})*/
//testing = true
//Auth.auth().settings?.isAppVerificationDisabledForTesting = true
if phoneNumber == "" {
print("No phone number")
return
}
if countryCodeNumber == "" {
return
}
let _ = PhoneAuthProvider.provider(auth: Auth.auth())
PhoneAuthProvider.provider().verifyPhoneNumber(countryCodeNumber + phoneNumber, uiDelegate: nil) { verificationID, error in
if error != nil {
print(error!.localizedDescription)
return
}
guard let verificationID = verificationID else {
print(FirebaseError.VerificatrionEmpty)
return
}
verificationId = verificationID
verifiable = true
}
//verifiable = true
} else {
let credential = PhoneAuthProvider.provider().credential(
withVerificationID: verificationId,
verificationCode: smsTextCode
)
Auth.auth().signIn(with: credential) { authResult, error in
if error != nil {
print(FirebaseError.Error)
return
}
guard let authResult = authResult else {
print(FirebaseError.VerificatrionEmpty)
return
}
_ = authResult
loggedin = true
}
//loggedin = true
}
})
}
}
if Auth.auth().currentUser != nil || loggedin {
if show == "leaderboard"{
}
if show == "home" {
}
Text(show != "home" ? "back" : "logout")
.font(Font.system(size: 15))
.fontWeight(.semibold)
.frame(width: nil, height: nil, alignment: .leading)
.onTapGesture {
if show == "home"{
//signOut()
loggedin = false
verifiable = false
} else {
show = "home"
}
}
}
}
}
#Preview {
ContentView()
}
App.swift (canHandle and openURLContexts func's, included)
import SwiftUI
import FirebaseCore
import FirebaseAuth
import FirebaseMessaging
@main
struct PassportApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
print("application is starting up. ApplicationDelegate didFinishLaunchingWithOptions.")
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("Yay! Got a device token 🥳 \(deviceToken)")
Auth.auth().setAPNSToken(deviceToken, type: .sandbox)//.unknown
//Messaging.messaging().setAPNSToken(deviceToken, type: .sandbox)
}
func application(_ application: UIApplication,
didReceiveRemoteNotification notification: [AnyHashable : Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if Auth.auth().canHandleNotification(notification) {
print("Can handle notifications")
completionHandler(.noData)
return
}
}
func application(_ application: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
if Auth.auth().canHandle(url) {
print("Can handle url")
return true
}
return false
}
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(
name: "Default Configuration",
sessionRole: connectingSceneSession.role
)
//let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
//sceneConfig.delegateClass = SceneDelegate.self
//return sceneConfig
}
}
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
//var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
//guard let windowScene = (scene as? UIWindowScene) else { return }
}
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if let incomingURL = userActivity.webpageURL {
let _ = incomingURL.absoluteString
}
}
//this should fix it
// Implementing this delegate method is needed when swizzling is disabled.
// Without it, reCAPTCHA's login view controller will not dismiss.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
for urlContext in URLContexts {
let url = urlContext.url
let _ = Auth.auth().canHandle(url)
}
// URL not auth related; it should be handled separately.
}
}
P.s. My GoogleService-Info.plist
was downloaded after enabling firebase authentication in the console and my xcode Target>Info>URL Type URL Scheme
includes my REVERSED_CLIENT_ID
, but I'm sure I would have encountered an error instead of this issue had I not. If my URL Scheme
is rather the Encoded App ID, it does not matter.
It works on the simulator (command + r) and a real device as is, but not #Preview. This might be as intended. I thought preview and simulator were the same at the time of asking this question.