FirebaseUI-iOS
FirebaseUI-iOS copied to clipboard
Add SwiftUI example
It'll be helpful to add a swiftUI example that uses the standard FirebaseUI. Here's skeleton code that works for me:
import SwiftUI
import FirebaseUI
import Firebase
public var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
public var screenHeight: CGFloat {
return UIScreen.main.bounds.height
}
struct LoginView : View {
@State private var viewState = CGSize(width: 0, height: screenHeight)
@State private var MainviewState = CGSize.zero
var body : some View {
ZStack {
CustomLoginViewController { (error) in
if error == nil {
self.status()
}
}.offset(y: self.MainviewState.height).animation(.spring())
MainView().environmentObject(DataStore()).offset(y: self.viewState.height).animation(.spring())
}
}
func status() {
self.viewState = CGSize(width: 0, height: 0)
self.MainviewState = CGSize(width: 0, height: screenHeight)
}
}
struct LoginView_Previews : PreviewProvider {
static var previews : some View {
LoginView()
}
}
struct CustomLoginViewController : UIViewControllerRepresentable {
var dismiss : (_ error : Error? ) -> Void
func makeCoordinator() -> CustomLoginViewController.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIViewController
{
let authUI = FUIAuth.defaultAuthUI()
let providers : [FUIAuthProvider] = [
FUIEmailAuth(),
FUIGoogleAuth(),
FUIOAuth.appleAuthProvider()
]
authUI?.providers = providers
authUI?.delegate = context.coordinator
let authViewController = authUI?.authViewController()
return authViewController!
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<CustomLoginViewController>)
{
}
//coordinator
class Coordinator : NSObject, FUIAuthDelegate {
var parent : CustomLoginViewController
init(_ customLoginViewController : CustomLoginViewController) {
self.parent = customLoginViewController
}
// MARK: FUIAuthDelegate
func authUI(_ authUI: FUIAuth, didSignInWith authDataResult: AuthDataResult?, error: Error?)
{
if let error = error {
parent.dismiss(error)
}
else {
parent.dismiss(nil)
}
}
func authUI(_ authUI: FUIAuth, didFinish operation: FUIAccountSettingsOperationType, error: Error?)
{
}
}
}
In my SceneDelegate.swift, I'm calling the following in scene:willConnectTo:
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
if Auth.auth().currentUser == nil {
window.rootViewController = UIHostingController(rootView: LoginView())
}
else {
window.rootViewController = UIHostingController(rootView:MainView().environmentObject(DataStore()))
}
self.window = window
window.makeKeyAndVisible()
}
Hope this helps.
What is DataStore() referring to in the line
MainView().environmentObject(DataStore()).offset(y: self.viewState.height).animation(.spring())
@chrisyue1998 it's a swift class that handles my data store.
@jtressle would you mind posting the datastore file I would like to understand better how that works with your auth login function?
My hope is that you are storing user info to pass on to firebase storage with it but, I am curious on the whole function.
@swells808 my user info is all handled by the Firebase Auth library. The only check I do is check if the user has logged in. After that, I count on the read/write permissions on Firebase to handle object restrictions. As shown above, if the user is not logged in, I present the login view.
if Auth.auth().currentUser == nil {
window.rootViewController = UIHostingController(rootView: LoginView())
}
else {
window.rootViewController = UIHostingController(rootView:MainView().environmentObject(DataStore()))
}
The datastore file does not contain any user info since we rely on Firebase read/write access restrictions.
Hope this helps.
@jtressle Thank you that explains a lot! Follow up question what types of info do you use the DataStore for if not user info?
Hi @jtressle I am trying to implement this in a sheet in swiftui so that it's a popup instead of a root view. However I am finding that at the end of the sign in flow the sheet closes but the delegate is not called.
When I use it as a root view everything works fine, any idea what may be causing the altered behavior in a swiftui sheet?
Interestingly it also works when using the popover modifier just not the sheet modifier
@mattbrandman I just encountered that myself. The problem is when authentication is complete, the sheet is dismissed, so the delegate methods don't get called because the Authentication view is garbage collected. You need to use a different object to handle the delegate method callbacks, don't keep them in the Coordinator like the above example because the Coordinator will no longer exist when the sheet is dismissed.
@arthurgarzajr I am encountering the same issue right now. Any chance you can post some code with the different object handling the delegate callbacks?
@arthurgarzajr I too am seeing what @mattbrandman is experiencing using the Auth view in the sheet. I've created an a class AuthuiDelegate that is basically the same as what the coordinator would be. I instantiate the object in the makeViewController method by doing let authuiDelegate = AuthuiDelegate(self) and set authui?.delegate = authuiDelegate. What am I missing?
@pedrovelmo, @smaldd14,
I created a view model that implemented the FUIAuthDelegate
protocol. The view model was then passed in as a delegate to the CustomLoginViewController
(based off of @jtressle's example).
Here it is in it's entirety, going off of the OP's sample code. You'll find differences in the coordinator and how CustomLoginViewController
is used. Notice the passed in delegate, which is my AuthViewModel
.
import SwiftUI
import FirebaseUI
import Firebase
public var screenWidth: CGFloat {
return UIScreen.main.bounds.width
}
public var screenHeight: CGFloat {
return UIScreen.main.bounds.height
}
struct LoginView : View {
@State private var viewState = CGSize(width: 0, height: screenHeight)
@State private var MainviewState = CGSize.zero
@ObservedObject var authViewModel = AuthViewModel()
var body : some View {
ZStack {
CustomLoginViewController(delegate: authViewModel) { (error) in
if error == nil {
self.status()
}
}.offset(y: self.MainviewState.height).animation(.spring())
MainView().environmentObject(DataStore()).offset(y: self.viewState.height).animation(.spring())
}
}
func status() {
self.viewState = CGSize(width: 0, height: 0)
self.MainviewState = CGSize(width: 0, height: screenHeight)
}
}
struct LoginView_Previews : PreviewProvider {
static var previews : some View {
LoginView()
}
}
struct CustomLoginViewController : UIViewControllerRepresentable {
var delegate: FUIAuthDelegate?
var dismiss : (_ error : Error? ) -> Void
func makeCoordinator() -> CustomLoginViewController.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> UIViewController
{
let authUI = FUIAuth.defaultAuthUI()
let providers : [FUIAuthProvider] = [
FUIEmailAuth(),
FUIGoogleAuth(),
FUIOAuth.appleAuthProvider()
]
authUI?.providers = providers
authUI?.delegate = self.delegate
let authViewController = authUI?.authViewController()
return authViewController!
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<CustomLoginViewController>)
{
}
//coordinator
class Coordinator: NSObject {
var parent: CustomLoginViewController
init(_ customLoginViewController : CustomLoginViewController) {
self.parent = customLoginViewController
}
}
}
import FirebaseUI
class AuthViewModel: NSObject, ObservableObject, FUIAuthDelegate {
func authUI(_ authUI: FUIAuth, didSignInWith authDataResult: AuthDataResult?, url: URL?, error: Error?) {
print("Signed in as \(Auth.auth().currentUser?.displayName)")
}
func authUI(_ authUI: FUIAuth, didSignInWith authDataResult: AuthDataResult?, error: Error?) {
print("Signed in")
}
func authUI(_ authUI: FUIAuth, didFinish operation: FUIAccountSettingsOperationType, error: Error?) {
print("Signed in")
}
}
I am trying to implement this and I was wondering if anyone had a sample project I could look at to help me implement this. I am having trouble with what the DataStore class does and how this all links together. I saw the explanation for datastore above but I'm still unsure how to implement it. Thanks
Hello,
I have implemented this and it works well. My only problem is that I can not figure out how to change the current view when the user logs in or out. Does anyone have an example showing how to do this?
Some help with DataStore
please.
Is it just an empty class or struct??
@nyxee it was just a ObservableObject that handled some snapshot listeners. I believe I've since removed the environment object in favor of using the git Resolver (https://github.com/hmlongco/Resolver).
Basically, just handle loading data after the user has logged in.
Why does it not work in preview? I lost three days on that.