Create SwiftUI app with Firebase Authentication
Tags: ios, swift, firebase, programming
Published: 2021-11-28 15:39:20

SwiftUI framework is already in its third iteration and with that, it is well and truly on its way to production-ready applications. Such applications are certainly going to need some sort of authentication. Since authenticating users can be tricky, you may want to rely on third-party solutions for that, such as Firebase. Question is then, how does one integrate Firebase and Google Sign-In to SwiftUI application?

This article will show you how to create iOS application using SwiftUI, create Firebase project and then use Firebase Authentication to integrate Google Sign-In SDK to your SwiftUI application.

Within this article we will cover following topics:

- Creation of Firebase project

- Creation of SwiftUI Lifecycle application

- Handling dependencies with CocoaPods

- Handling authentication within code

- Designing UI elements

## Creation of Firebase project  

In order to use Firebase in our SwiftUI app, we need to create new Firebase project in [Firebase console](https://console.firebase.google.com/). Create new project by clicking on *Add project*.

Once *Create a project* page opens, enter project name.
![Screenshot 2021-11-25 at 15.30.35.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1637850709945/kfO4wNtGn.png)

Next step is either enabling or disabling Google Analytics. It is **not** necessary for this demo, so it's up to you to enable it.

When project's Dashboard opens, click on **iOS icon**.
![Screenshot 2021-11-25 at 15.37.28.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1637851061945/BFZkjwsNV.png)

Next, fill out form for registering the app. Apple Bundle ID is important since it needs to be the same as bundle identifier, we will use later in Xcode project. Other two fields are optional. App Store ID is only needed if app will be uploaded to the App Store which we are not doing in this tutorial.

![Screenshot 2021-11-28 at 09.40.27.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638088881121/aNT2PW8G5.png)

Click on **Register app**. 
Next, we need to download *GoogleService-Info.plist* file which will be added to Xcode project later.

![Screenshot 2021-11-25 at 15.45.33.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1637851549327/fklRVppB8.png)

In steps 3 and 4 just click **Next** since we will cover them in more detail later in the tutorial. To finish registering the app, click **Continue to console**.

Almost finished with setting up Firebase project. Lastly, we need to enable *Authentication*. 

Click on **Authentication**, and then on **Get started**.
![Screenshot 2021-11-25 at 15.50.15.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1637851840818/h-9R4RnYL.png)

Now choose Google as Sign-in method provider.

![Screenshot 2021-11-25 at 15.52.26.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1637851980924/2k_9dzyN_.png)

Finally, enable it by toggling **Enable** button, add **Project public-facing name** and **Project support email**. Save this changes by clicking on **Save** button.

![Screenshot 2021-11-25 at 15.53.33.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1637852114664/phYZP9gIy.png)

We are done with Firebase settings and now we are moving to actual coding!

## Creation of SwiftUI Lifecycle application 

Next step is to actually create Xcode project. Open **Xcode**, select option **Create a new Xcode project**. Once new window opens, make sure that **iOS** is selected in header and then select **App**. Click **Next**.

Now we need enter some details about project.

- Product Name: demo
- Organization Identifier: com.firebase
- Interface: SwiftUI
- Language: Swift

Core Data and Tests are not needed for this tutorial, so it is up to you to include them into the project. 

![Screenshot 2021-11-28 at 09.38.27.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638088943581/ikMsr8pQZ.png)

Click **Next** and then choose folder where you want to save Xcode project. 

Great! We are ready for next section, handling dependencies with CocoaPods.

## Handling dependencies with CocoaPods

> CocoaPods is a dependency manager for Swift and Objective-C Cocoa projects. It has over 86 thousand libraries and is used in over 3 million apps. CocoaPods can help you scale your projects elegantly.
 
To handle project dependencies, this tutorial will use CocoaPods. If CocoaPods is not installed on your development machine, follow this [Getting Started](https://guides.cocoapods.org/using/getting-started.html#getting-started) guide on official CocoaPods page.

Using **Terminal**, navigate to your project. Use command similar to this one: `cd your/project/path`. 

Once in your project path, initialize empty pod file by executing `pod init` command. Open newly created *Podfile* to add dependencies needed for this project.

Add following content:
```
target 'swiftui' do
  # Comment the next line if you don't want to use dynamic frameworks
  use_frameworks!

  # Pods for swiftui
  pod 'Firebase/Core'
  pod 'Firebase/Auth'
  pod 'GoogleSignIn'
  pod 'SDWebImageSwiftUI'

end
``` 

To install these dependencies, execute `pod install` command. 

Awesome! Project setup is done, and its dependencies are successfully installed.

## Handling authentication within code

First step is to take *GoogleService-Info.plist* file we downloaded it in *Creation of Firebase project* section and drag&drop it into Xcode project. Once file is added to the project, we need to add **REVERSED_CLIENT_ID** key to project configuration. 

### Handling REVERSED_CLIENT_ID key

Do the following:
1.  Open your project configuration: double-click the project name in the left tree view. Select your app from the **TARGETS** section, then select the **Info** tab, and expand the **URL Types** section. 

2. Click the + button, and add a URL scheme for your reversed client ID. To find this value, open the *GoogleService-Info.plist* configuration file, and look for the **REVERSED_CLIENT_ID** key. Copy the value of that key and paste it into the **URL Schemes** box on the configuration page. Leave the other fields blank.
When completed, your config should look something similar to the following (but with your application-specific values):
![Screenshot 2021-11-27 at 21.45.09.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638045931556/1MejF97k0.png)

Now we can move on to some coding! 

### Adding AppDelegate

Since iOS 14 and SwiftUI 2.0, Apple added ability to include AppDelegate to SwiftUI Lifecycle app which we will need to add to our project.

Right click on project in Xcode left tree view and then select **New file...**. Add new Swift file named **AppDelegate**. Once create, add following contents to the file:

```
import Firebase
import GoogleSignIn

class AppDelegate: NSObject, UIApplicationDelegate {

    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
    ) -> Bool {
        FirebaseApp.configure()
        return true
    }

    func application(
      _ app: UIApplication,
      open url: URL,
      options: [UIApplication.OpenURLOptionsKey : Any] = [:]
    ) -> Bool {
        return GIDSignIn.sharedInstance.handle(url)
    }
}
```
 
### Adding helper functions

For `signInWithConfiguration:presentingViewController:callback:` method we will use later, we need to provide *presenting ViewController* and *GIDConfiguration* configuration thus we need to implement two helper functions.

GIDConfigurationExtensions.swift:
```
import Firebase
import GoogleSignIn

extension GIDConfiguration {

    /// Get GIDConfiguration instance by providing clientID from GoogleService-Info.plist file
    /// - Returns: GIDConfiguration instance
    static func getGIDConfigurationInstance() -> GIDConfiguration {
        GIDConfiguration(clientID: FirebaseApp.app()?.options.clientID ?? "")
    }
}
```

UIApplicationExtensions.swift
```
import UIKit

extension UIApplication {

    /// Get root UIViewController of application. If for whatever reason, UIViewController cannot be accessed,
    /// invoke fatalError() since UIViewController instance is crucial for application to work properly.
    /// - Returns: root UIViewController
    static func getRootViewController() throws -> UIViewController {
        let scenes = UIApplication.shared.connectedScenes
        let windowScene = scenes.first as? UIWindowScene
        let window = windowScene?.windows.first

        guard let uiViewController = window?.rootViewController else { fatalError() }
        return uiViewController
    }
}
```

With this finished, we can move start implementing ViewModel.

### Implementing LoginViewModel

Create new Swift file named LoginViewModel.

LoginViewModel:
```
import Firebase
import GoogleSignIn

final class LoginViewModel: ObservableObject {

    @Published var isUserLoggedIn = false

    func signIn() {
        do {
            let config = GIDConfiguration.getGIDConfigurationInstance()
            let uiViewController = try UIApplication.getRootViewController()

            GIDSignIn.sharedInstance.signIn(with: config, presenting: uiViewController) { user, error in
                guard error == nil else { return }
                guard user != nil else { return }
                self.isUserLoggedIn = true
          }
        } catch {
            print(error.localizedDescription)
        }
    }

    func signOut() {
        GIDSignIn.sharedInstance.signOut()
        isUserLoggedIn = false
    }
}
```


## Designing UI elements

Now comes last piece of the puzzle. Designing UI elements using SwiftUI framework and connecting UI with business logic we implemented earlier.

### LoginView

Create new **SwiftUI View** file named LoginView.

LoginView:
```
import SwiftUI

struct LoginView: View {

    @EnvironmentObject var viewModel: LoginViewModel

    var body: some View {
        VStack {
            Spacer()

            Button(action: {
                withAnimation {
                    viewModel.signIn()
                }
            }) {
                HStack {
                    Text("Sign in with google")
                        .fontWeight(.bold)
                }
            }
            .foregroundColor(.white)
            .padding()
            .frame(maxWidth: .infinity)
            .background(.black)
            .cornerRadius(12)
            .padding()

            Spacer()
        }
    }
}

struct LoginView_Previews: PreviewProvider {
    static var previews: some View {
        LoginView()
    }
}
```

LoginView will look something like this:

![Simulator Screen Shot - iPhone 12 - 2021-11-28 at 10.41.25.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638113786143/swXZQnBWh.png)

### HomeView

Create new **SwiftUI View** file named HomeView.

HomeView:
```
import GoogleSignIn
import SwiftUI
import SDWebImageSwiftUI

struct UserDetailView: View {

    let user: GIDGoogleUser?

    var body: some View {
        VStack {
            HStack {
                Text(user?.profile?.name ?? "")
                    .bold()
                    .font(.title2)

                Spacer()

                WebImage(url: user?.profile?.imageURL(withDimension: 75))
                    .resizable()
                    .placeholder(
                        Image(systemName: "person.fill")
                    )
                    .indicator(.activity)
                    .transition(.fade(duration: 0.5))
                    .frame(width: 75, height: 75, alignment: .center)
                    .scaledToFit()
                    .clipShape(Circle())
            }
            HStack {
                Text(user?.profile?.email ?? "")
                    .bold()
                    .font(.subheadline)
                Spacer()
            }

            Spacer()
        }
        .padding()
        .frame(height: 150, alignment: .center)
        .background(.thickMaterial)
        .cornerRadius(12)
    }
}

struct HomeView: View {

    @EnvironmentObject var viewModel: LoginViewModel
    private let user = GIDSignIn.sharedInstance.currentUser

    var body: some View {
        VStack {
            UserDetailView(user: user)

            Spacer()

            Button(action: {
                withAnimation {
                    viewModel.signOut()
                }
            }) {
                HStack {
                    Text("Sign out")
                        .fontWeight(.bold)
                }
            }
            .foregroundColor(.white)
            .padding()
            .frame(maxWidth: .infinity)
            .background(.black)
            .cornerRadius(12)
            .padding()
        }
        .padding()
    }
}

struct HomeView_Previews: PreviewProvider {
    static var previews: some View {
        HomeView()
    }
}
```

HomeView will look something like this:

![Simulator Screen Shot - iPhone 12 - 2021-11-28 at 10.41.52.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1638113753667/mpxTyhD8e.png)


### Connecting all together

All that is left to do is to connect it all together in **DemoApp** and **ContentView**.

DemoApp:
```
import SwiftUI

@main
struct DemoApp: App {

    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

    @StateObject var viewModel = LoginViewModel()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(viewModel)
        }
    }
}
```


ContentView:
```
import SwiftUI

struct ContentView: View {

    @EnvironmentObject var viewModel: LoginViewModel

    var body: some View {
        if viewModel.isUserLoggedIn {
            HomeView()
        } else {
            LoginView()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```

This is it! Integration of Firebase Authentication with Google Sign-In to SwiftUI application is done and our project is ready to be run on emulator or physical device!


## Conclusion

Thank you for reading and and I hope this article was useful for you since integrating Firebase Authentication with SwiftUI Lifecycle application is not exactly straightforward.

***

If you like my content and find it useful, please consider following me here on DEV community. If you are feeling extra generous, please consider [buying me a coffee](https://buymeacoffee.com/kfurjan).

Connect with me on [LinkedIn](https://www.linkedin.com/in/kevin-furjan/).