Add google Sign-In with SwiftUI - ios

I am trying to add google sign-in with swiftUI whith UIKitViewController, and for some reason I have difficulties showing the button does not appear in style
The signIn and the button works perfect but at some point the style of the button stopped appearing
I'm adding the button in a uikit viewcontroller, because I couldn't think of another way to handle the Google delegate
Here's the preview https://ibb.co/tYhx62b
//
// GoogleSignInButtonView.swift
//
// Created by Ivan Schaab on 11/09/2019.
// Copyright © 2019 Ivan Schaab. All rights reserved.
//
import GoogleSignIn
import SwiftUI
struct GoogleSignInButtonView: View {
#EnvironmentObject var lvm: LoginViewModel
var body: some View {
HStack {
Spacer()
GoogleButtonViewControllerRepresentable { (token, user) in
// Google Login Success
// Now do Backend Validations
self.lvm.loginOauth(token: token, user: user)
}
Spacer()
}.frame(alignment: .center)
}
}
class GoogleButtonUIKitViewController: UIViewController {
var signInButton = GIDSignInButton()
override func viewDidLoad() {
super.viewDidLoad()
GIDSignIn.sharedInstance().clientID = Constants.GOOGLE_CLIENT_ID
self.view.addSubview(signInButton)
GIDSignIn.sharedInstance()?.presentingViewController = self
// Automatically sign in the user.
GIDSignIn.sharedInstance()?.restorePreviousSignIn()
}
}
struct GoogleButtonViewControllerRepresentable: UIViewControllerRepresentable
{
let vc = GoogleButtonUIKitViewController()
var googleResponse: (String, User) -> Void
func makeUIViewController(context: Context) -> GoogleButtonUIKitViewController {
return vc
}
func updateUIViewController(_ uiViewController: GoogleButtonUIKitViewController, context: Context) {}
func makeCoordinator() -> Coordinator {
Coordinator(vc: vc, googleResponse: googleResponse)
}
static func dismantleUIViewController(_ uiViewController: GoogleButtonUIKitViewController, coordinator: GoogleButtonViewControllerRepresentable.Coordinator) {
print("DISMANTLE")
}
class Coordinator: NSObject, GIDSignInDelegate {
var foo: (String, User) -> Void
init(vc: GoogleButtonUIKitViewController, googleResponse: #escaping (String, User) -> Void) {
self.foo = googleResponse
super.init()
GIDSignIn.sharedInstance()?.delegate = self
}
func sign(_ signIn: GIDSignIn!, didSignInFor googleUser: GIDGoogleUser!, withError error: Error!) {
if let error = error {
if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
print("The user has not signed in before or they have since signed out.")
} else {
print("\(error.localizedDescription)")
}
return
}
// let userId = googleUser.userID // For client-side use only!
let idToken = googleUser.authentication.idToken // Safe to send to the server
let email = googleUser.profile.email
if googleUser.profile.hasImage{
let imageUrl = googleUser.profile.imageURL(withDimension: 120)
print(" image url: ", imageUrl?.absoluteString ?? "NO URL")
}
let user : User = User(id: 1, name: googleUser.profile.givenName, surname: googleUser.profile.familyName, imgName: "" , email: googleUser.profile.email)
print("email: ",email ?? "NO EMAIL")
foo(idToken! , user)
}
}
}
#if DEBUG
struct SomeRepView_Previews: PreviewProvider {
static var previews: some View {
GoogleSignInButtonView().environmentObject(LoginViewModel())
}
}
#endif

This is a part of my code where I implement Facebook and Google login, using any button I hope it helps you
import SwiftUI
import GoogleSignIn
import FBSDKLoginKit
var body: some View {
LoadingView(isShowing: .constant(loading)) {
NavigationView {
ScrollView {
VStack {
Text("Or Login with").font(.footnote)
HStack {
Button(action: self.logginFb, label: {
Image("ic_facebook").foregroundColor(Color.white).frame(width: 20, height: 20)
})
.padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
.background(Color("facebook"))
.cornerRadius(8.0)
Button(action: self.socialLogin.attemptLoginGoogle, label: {
Image("ic_google").frame(width: 20, height: 20)
})
.padding(EdgeInsets(top: 8, leading: 16, bottom: 8, trailing: 16))
.background(Color.white)
.cornerRadius(8.0)
.shadow(radius: 4.0)
}.padding()
}.padding(.all, 32)
}.navigationBarTitle(Text("Login"))
}
}
}
func logginFb() {
socialLogin.attemptLoginFb(completion: { result, error in
})
}
struct SocialLogin: UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<SocialLogin>) -> UIView {
return UIView()
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<SocialLogin>) {
}
func attemptLoginGoogle() {
GIDSignIn.sharedInstance()?.presentingViewController = UIApplication.shared.windows.last?.rootViewController
GIDSignIn.sharedInstance()?.signIn()
}
func attemptLoginFb(completion: #escaping (_ result: FBSDKLoginManagerLoginResult?, _ error: Error?) -> Void) {
let fbLoginManager: FBSDKLoginManager = FBSDKLoginManager()
fbLoginManager.logOut()
fbLoginManager.logIn(withReadPermissions: ["email"], from: UIApplication.shared.windows.last?.rootViewController) { (result, error) -> Void in
completion(result, error)
}
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}
The configuration of google and facebook in AppDelegate is the same as its documentation.
Now with swiftUI it is not necessary to use the default buttons that you give us.
Google and Facebook custom buttons

Note for SwiftUI 2.0 lifecycle: there is no standard AppDelegate so you also need to add an adapter in main App file:
#main
struct ExampleApp: App {
#UIApplicationDelegateAdaptor(ExampleAppDelegate.self) var appDelegate
and prepare the Delegate in a separate file:
//No #UIApplicationMain
class ExampleAppDelegate: NSObject, UIApplicationDelegate, GIDSignInDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
GIDSignIn.sharedInstance().clientID = "YOUR CLINET ID"
GIDSignIn.sharedInstance().delegate = self
return true
}
...

With the latest SwiftUI, you can setup the GIDSignInDelegate within your AppDelegate and ensure that it conforms to all methods. Then, within your SceneDelegate, when setting up the window, you can drop this line in to setup the presenting view controller:
GIDSignIn.sharedInstance()?.presentingViewController = window.rootViewController.
Lastly, when creating your button, set the action or tap gesture to call GIDSignIn.sharedInstance().signIn() and you should be good to go!

GoogleSignIn 6.2.4 has the signIn function that requires a presenting UIViewController.
To solve this, add a sceneDelegate to the environment so it's accessible to SwiftUI.
Set up an AppDelegate so we can hook up our SceneDelegate
class AppDelegate: NSObject, UIApplicationDelegate {
// Hook up our SceneDelegate
func application(_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let configuration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
if connectingSceneSession.role == .windowApplication {
configuration.delegateClass = SceneDelegate.self
}
return configuration
}
}
Set up a SceneDelegate as an ObservableObject to keep track of the window
// Set this up so we can access the window in the environment
class SceneDelegate: NSObject, ObservableObject, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
self.window = windowScene.keyWindow
}
}
Be sure to enable the AppDelegate in the App
#main
struct AdrichApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Finally, use it in a SwiftUI View to show your Continue with Google button
struct SignInWithGoogle: View {
#EnvironmentObject var sceneDelegate: SceneDelegate
var body: some View {
if let vc = sceneDelegate.window?.rootViewController {
continueWithGoogle(config: GIDConfiguration(clientID: "YOUR CLINET ID"), presenter: vc)
} else {
emptyView
}
}
private var emptyView: some View {
print("Unable to access the root view controller")
return EmptyView()
}
private func continueWithGoogle(config: GIDConfiguration, presenter: UIViewController) -> some View {
Button {
GIDSignIn.sharedInstance.signIn(with: config, presenting: presenter) { user, error in
if let error = error {
print(error)
return
}
guard
let authentication = user?.authentication,
let idToken = authentication.idToken
else {
return
}
let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: authentication.accessToken)
// they are now signed in with Google!
}
} label: {
Text("Continue with Google")
}
}
}

Related

how to sign in with google in swiftui when there is no app delegate file inside the project?

I want to sign in to google according to the method found in this URL:
https://paulallies.medium.com/google-sign-in-swiftui-2909e01ea4ed
but the problem is that App delegate is not found in swiftui new project ?
since the app delegate requires client ID implementation as shown in URL
any idea and thanks
This is for everyone having problem with AppDelegate SwiftUI 3.0 new update
Add in app view
import Foundation
import SwiftUI
import Firebase
import GoogleSignIn
#main
struct YourApp: App {
let persistenceController = PersistenceController.shared
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
#available(iOS 9.0, *)
func application(_ application: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any])
-> Bool {
return GIDSignIn.sharedInstance.handle(url)
}
}
Then in loginView add inside login struct
#State var isLoading : Bool = false
func handleLogin(){
// google sign in
guard let clientID = FirebaseApp.app()?.options.clientID else { return }
// Create Google Sign In configuration object.
let config = GIDConfiguration(clientID: clientID)
isLoading = true
GIDSignIn.sharedInstance.signIn(with: config, presenting: getRootViewController()) { [self] user, error in
if let error = error {
isLoading = false
print(error.localizedDescription)
return
}
guard
let authentication = user?.authentication,
let idToken = authentication.idToken
else {
isLoading = false
return
}
let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: authentication.accessToken)
// Firebase Authentication
Auth.auth().signIn(with: credential) {result, err in
isLoading = false
if let error = error {
print(error.localizedDescription)
return
}
//Displaying User Name...
guard let user = result?.user else {
return
}
print(user.displayName ?? "Success" )
//updating user as logged in
withAnimation{
log_Status = true
// and this is where you want the user to go if successful
goHome()
}
}
}
}
where goHome() function is
public func goHome() {
if let window = UIApplication.shared.windows.first {
window.rootViewController = UIHostingController(rootView: HomeView())
window.makeKeyAndVisible()
}
}
also add this to loginView but as extension
extension View{
func getRect()->CGRect{
return UIScreen.main.bounds
}
// retrieve RootView
func getRootViewController()->UIViewController{
guard let screen = UIApplication.shared.connectedScenes.first as? UIWindowScene else {
return . init()
}
guard let root = screen.windows.first?.rootViewController else {
return .init()
}
return root
}
}
don't forget to give the button the handleLogin()
Button(action: {handleLogin()}) {
Image("Gmail")
}
.padding()
.foregroundColor(.white)
.background(Color.white)
.cornerRadius(.infinity)
and you are good to go my friends 🥰

How to auto-move/auto-adjust the save button for a note-taking app in swift?

This is what the app looks like when first opened:
I am content with this as the first screen the user sees.
The problem arises when the user hits the plus button to create a new note. The user touches the screen to add a new note and the keyboard covers up the save button at the bottom right corner. Is there any way to to have button appear above the keyboard on the top right side?
This is what it looks like:
I simply want the plus button above the keyboard on the right side so that the user can save the note. How would this be done?
Please help. Thank you!
Here is the entire code:
AppDelegate.swift:
import UIKit
import Firebase
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
FirebaseApp.configure()
return true
}
// MARK: UISceneSession Lifecycle
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)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
SceneDelegate.swift:
import UIKit
import SwiftUI
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = Host(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
}
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}
ContentView.swift:
import SwiftUI
import Firebase
struct ContentView: View {
var body: some View {
Home()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Home : View {
#ObservedObject var Notes = getNotes()
#State var show = false
#State var txt = ""
#State var docID = ""
#State var remove = false
var body : some View{
ZStack(alignment: .bottomTrailing) {
VStack(spacing: 0){
HStack{
Text("Notes").font(.title).foregroundColor(.white)
Spacer()
Button(action: {
self.remove.toggle()
}) {
Image(systemName: self.remove ? "xmark.circle" : "trash").resizable().frame(width: 23, height: 23).foregroundColor(.white)
}
}.padding()
.padding(.top,UIApplication.shared.windows.first?.safeAreaInsets.top)
.background(Color.red)
if self.Notes.data.isEmpty{
if self.Notes.noData{
Spacer()
Text("No Notes...")
Spacer()
}
else{
Spacer()
//Data is Loading ....
Indicator()
Spacer()
}
}
else{
ScrollView(.vertical, showsIndicators: false) {
VStack{
ForEach(self.Notes.data){i in
HStack(spacing: 15){
Button(action: {
self.docID = i.id
self.txt = i.note
self.show.toggle()
}) {
VStack(alignment: .leading, spacing: 12){
Text(i.date)
Text(i.note).lineLimit(1)
Divider()
}.padding(10)
.foregroundColor(.black)
}
if self.remove{
Button(action: {
let db = Firestore.firestore()
db.collection("notes").document(i.id).delete()
}) {
Image(systemName: "minus.circle.fill")
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(.red)
}
}
}.padding(.horizontal)
}
}
}
}
}.edgesIgnoringSafeArea(.top)
Button(action: {
self.txt = ""
self.docID = ""
self.show.toggle()
}) {
Image(systemName: "plus").resizable().frame(width: 18, height: 18).foregroundColor(.white)
}.padding()
.background(Color.red)
.clipShape(Circle())
.padding()
}
.sheet(isPresented: self.$show) {
EditView(txt: self.$txt, docID: self.$docID, show: self.$show)
}
.animation(.default)
}
}
class Host : UIHostingController<ContentView>{
override var preferredStatusBarStyle: UIStatusBarStyle{
return .lightContent
}
}
class getNotes : ObservableObject{
#Published var data = [Note]()
#Published var noData = false
init() {
let db = Firestore.firestore()
db.collection("notes").order(by: "date", descending: false).addSnapshotListener { (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
self.noData = true
return
}
if (snap?.documentChanges.isEmpty)!{
self.noData = true
return
}
for i in snap!.documentChanges{
if i.type == .added{
let id = i.document.documentID
let notes = i.document.get("notes") as! String
let date = i.document.get("date") as! Timestamp
let format = DateFormatter()
format.dateFormat = "MM/YY"
let dateString = format.string(from: date.dateValue())
self.data.append(Note(id: id, note: notes, date: dateString))
}
if i.type == .modified{
// when data is changed...
let id = i.document.documentID
let notes = i.document.get("notes") as! String
for i in 0..<self.data.count{
if self.data[i].id == id{
self.data[i].note = notes
}
}
}
if i.type == .removed{
// when data is removed...
let id = i.document.documentID
for i in 0..<self.data.count{
if self.data[i].id == id{
self.data.remove(at: i)
if self.data.isEmpty{
self.noData = true
}
return
}
}
}
}
}
}
}
struct Note : Identifiable {
var id : String
var note : String
var date : String
}
struct Indicator : UIViewRepresentable {
func makeUIView(context: UIViewRepresentableContext<Indicator>) -> UIActivityIndicatorView {
let view = UIActivityIndicatorView(style: .medium)
view.startAnimating()
return view
}
func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<Indicator>) {
}
}
struct EditView : View {
#Binding var txt : String
#Binding var docID : String
#Binding var show : Bool
var body : some View{
ZStack(alignment: .bottomTrailing) {
MultiLineTF(txt: self.$txt)
.padding()
.background(Color.black.opacity(0.05))
Button(action: {
self.show.toggle()
self.SaveData()
}) {
Text("Save").padding(.vertical).padding(.horizontal,25).foregroundColor(.white)
}.background(Color.red)
.clipShape(Capsule())
.padding()
}.edgesIgnoringSafeArea(.bottom)
}
func SaveData(){
let db = Firestore.firestore()
if self.docID != ""{
db.collection("notes").document(self.docID).updateData(["notes":self.txt]) { (err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
}
}
else{
db.collection("notes").document().setData(["notes":self.txt,"date":Date()]) { (err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
}
}
}
}
struct MultiLineTF : UIViewRepresentable {
func makeCoordinator() -> MultiLineTF.Coordinator {
return MultiLineTF.Coordinator(parent1: self)
}
#Binding var txt : String
func makeUIView(context: UIViewRepresentableContext<MultiLineTF>) -> UITextView{
let view = UITextView()
if self.txt != ""{
view.text = self.txt
view.textColor = .black
}
else{
view.text = "Type Something"
view.textColor = .gray
}
view.font = .systemFont(ofSize: 18)
view.isEditable = true
view.backgroundColor = .clear
view.delegate = context.coordinator
return view
}
func updateUIView(_ uiView: UITextView, context: UIViewRepresentableContext<MultiLineTF>) {
}
class Coordinator : NSObject,UITextViewDelegate{
var parent : MultiLineTF
init(parent1 : MultiLineTF) {
parent = parent1
}
func textViewDidBeginEditing(_ textView: UITextView) {
if self.parent.txt == ""{
textView.text = ""
textView.textColor = .black
}
}
func textViewDidChange(_ textView: UITextView) {
self.parent.txt = textView.text
}
}
}
Remove your .edgesIgnoringSafeArea(.bottom) attached to the ZStack.
The safe area includes the area taken up by the keyboard. Right now, because you've explicitly told it to ignore the safe area, it's not moving to accommodate the keyboard. If it respects the safe area, the button will move with the keyboard.

Hide Title Bar in SwiftUI App for macCatalyst

How can I hide the Title Bar in the new SwiftUI App Protocol?
Since the AppDelegate.swift and SceneDelegate.swift protocols are gone, I cant follow this documentation anymore:
https://developer.apple.com/documentation/uikit/mac_catalyst/removing_the_title_bar_in_your_mac_app_built_with_mac_catalyst
I can't implement this code:
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 }
#if targetEnvironment(macCatalyst)
if let titlebar = windowScene.titlebar {
titlebar.titleVisibility = .hidden
titlebar.toolbar = nil
}
#endif
}
}
Hope it's still possible with the new AppProtocol..
Thanks in advance
This is how to hide the titlebar:
struct ContentView: View {
var body: some View {
ZStack {
Text("Example UI")
}
.withHostingWindow { window in
#if targetEnvironment(macCatalyst)
if let titlebar = window?.windowScene?.titlebar {
titlebar.titleVisibility = .hidden
titlebar.toolbar = nil
}
#endif
}
}
}
extension View {
fileprivate func withHostingWindow(_ callback: #escaping (UIWindow?) -> Void) -> some View {
self.background(HostingWindowFinder(callback: callback))
}
}
fileprivate struct HostingWindowFinder: UIViewRepresentable {
var callback: (UIWindow?) -> ()
func makeUIView(context: Context) -> UIView {
let view = UIView()
DispatchQueue.main.async { [weak view] in
self.callback(view?.window)
}
return view
}
func updateUIView(_ uiView: UIView, context: Context) {
}
}
On your Scene, set .windowStyle(_:) to HiddenTitleBarWindowStyle().
#main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.windowStyle(HiddenTitleBarWindowStyle())
}
}
EDIT: Ah crap. While this API is supposedly available for Mac Catalyst according to the online documentation, it looks like it’s not actually marked as such in frameworks so you can’t use it.

In Xcode12 and SwiftUI, how do you navigate to the next screen after Firebase Google Auth?

There seems to be lack of info regarding the newest Xcode because all I have been seeing mentioned ViewController and AppDelegate files, which I do not have...
Instead, I managed to make an AppDelegate class inside my app's main swift file (My project's name is Particeps, and thus the main file is ParticepsApp.swift)
However, I can't figure out how to go from the 'SignInview' to the 'MyCurrentLocationView' once the user is logged in. Below are my codes
ParticepsApp.swift
import SwiftUI
import Firebase
import GoogleSignIn
#main
struct ParticepsApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
var body: some Scene {
WindowGroup {
SignInView(info: self.delegate)
}
}
}
class AppDelegate: NSObject,UIApplicationDelegate,GIDSignInDelegate,ObservableObject{
#Published var email = ""
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance()?.clientID =
FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
guard let user = user else{
print(error.localizedDescription)
return}
let credential = GoogleAuthProvider.credential(withIDToken: user.authentication.idToken, accessToken: user.authentication.accessToken)
Auth.auth().signIn(with: credential) { (result, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
self.email = (result?.user.email)!
print(result?.user.email as Any)
}
}
}
SignInView.swift
import SwiftUI
import GoogleSignIn
struct SignInView: View {
#ObservedObject var info : AppDelegate
var body: some View {
VStack {
Button(action: {
GIDSignIn.sharedInstance()?.presentingViewController = UIApplication.shared.windows.first?.rootViewController
GIDSignIn.sharedInstance()?.signIn()
}) {
Text("Sign In")
.foregroundColor(.white)
.fontWeight(.bold)
.padding(.vertical,10)
.padding(.horizontal, 45)
.background(Color.red)
.clipShape(Capsule())
}
Text(info.email)
.padding(.top,25)
}
}
}
MyCurrentLocationView.swift
import SwiftUI
import MapKit
struct MyCurrentLocationView: View {
var body: some View {
Home()
}
}
struct Home: View {
.
.
.
}
Thanks a lot!

FirebaseUI and SwiftUI LoginG

I´m trying to implement the firebaseUI login in an iOS app using swiftUI.
I´m able to use the main login view, but i can not control the flow after the user was loged on.
This is the code of my "login controller"
import SwiftUI
import FirebaseUI
struct CustomLogin: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> UIViewController {
let authUI = FUIAuth.defaultAuthUI()
let providers: [FUIAuthProvider] = [
FUIEmailAuth(),
FUIFacebookAuth()
// FUIPhoneAuth(authUI:FUIAuth.defaultAuthUI()),
]
authUI?.providers = providers
let authViewController = authUI?.authViewController()
// let controller = UIViewController()
// authUI!.delegate = self
return authViewController!
}
func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}
struct CustomLogin_Previews: PreviewProvider {
static var previews: some View {
CustomLogin()
}
}
I can show the login flow by add CustomLogin())to my content view.
how can I manage the call backs after the user has logged on?
The user was created in firebase, but the view don´t update.
if someone has implemented a firebaseUI login with SwiftUI I appreciated it.
Thanks
I was able to implement something that worked based off of your code and some Apple tutorials. Basically, you need to add a coordinator to your UIViewControllerRepresentable. You can add your delegate to the coordinator.
Here's my scratch code that works:
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?)
{
}
}
}

Resources