Create a login function using Swift's Optional

A light review of Optional

What is Optional

Swift provides a special type called an optional type. An optional type is a type that can be defined for a value that is exceptionally nil apart from the type that it normally has.

For example, when the variable ʻoptionalName, which normally has a string, may be assigned nil in some special cases, the variable ʻoptionalName can be expressed asString?.

var optionalName: String? = "Brian"

optionalName = nil //Since it is an optional String type, nil can also be assigned.

By allowing the assignment of nil, even if nil is exceptionally included in the variable, it is possible to prevent the application from crashing by performing the nil check by conditional branching.

var optionalName: String? = "Brian"

optionalName = nil //Since it is an optional String type, nil can also be assigned.

if optionalName == nil {
    print("Nil is assigned to the name.")
} else {
    print( optionalName! ) //Optional value if not nil!It is necessary to disclose the value with
}

Optional binding syntax

As mentioned above, there is a way to determine whether the Optional type is nil with the if statement, but Swift also provides ** optional binding syntax **.

var optionalName: String? = "Brian"

if let name = optionalName {
    print( name )
}

By writing this way, if ʻoptionalName is not nil, the value is assigned to nameand you can enter the conditional branch. Also, at this time, it is not necessary to disclosename because the optional value is disclosed at the time of being assigned to name`.

For a more detailed explanation of Optional, please see here. Extreme Optional type of Swift [Detailed Swift 5th Edition Chapter4 Optional](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Swift-%E7%AC%AC5%E7%89%88- % E8% 8D% BB% E5% 8E% 9F-% E5% 89% 9B% E5% BF% 97 / dp / 48150407X)

Sign-in / sign-out processing as an example

Let's take the sign-in / sign-out process of the app as an example to see when this can be beneficial in Swift.

Although it is rough, this time we will assume an application that combines "Sign in View" for signing in, "Content View" after signing in, and "App State" that holds the user information that is signed in as follows. ..

struct1.png

Sample code

First is AppState. This is a class that holds the information of the user who signs in and is supposed to be referenced from each View.

The structure Session is defined as an EnvironmentObject.

///Repository class that stores user information
class User: Identifiable {
    public var uid: String
    public var email: String?

    init(uid: String, email: String) {
        self.uid = uid
        self.email = email
    }
}

class AppState: ObservableObject {
    
    //Structure for managing authentication status
    struct Session {
        var user: User?
        var handler: AuthStateDidChangeListenerHandle? //An object that holds user information that is signed in
    }
    
    @Published public var session = Session()
}

Next is the Interactor class that handles the processing (sign-in / sign-out) related to Session.

import Firebase

class SessionInteractor {
  
    public var appState: AppState

    ///Get EnvironmentObject from View component and store it in class property
    init(appState: AppState) {
        self.appState = appState
    }
  
    ///Process called every time the user's login status changes
  	public func listen() {
        self.appState.session.handler = Auth.auth().addStateDidChangeListener { (auth, user) in
            //Early return using guard let statement
            guard let user = user else {
                self.appState.session.user = nil
                return
            }            
            //Added to User property of Session type
            self.appState.session.user = User(
                uid: user.uid,
                email: user.email!
            )
        }
    }
  
    ///Sign-in process
    public func signIn() {
       //Sign-in process using Firebase Authentication
       //We are attaching FIRAuth objects, but detailed processing is omitted.
        Auth.auth().signIn(/* [Omission]Sign in with Firebase Authentication*/)
    }
  
    ///Sign out process
    public func signOut() -> Bool {
        do {
            try Auth.auth().signOut()
            self.appState.session.user = nil
            return true
        } catch {
            return false
        }
    }
}

Next is the code for each View.

import SwiftUI

struct SignInView: View {
    @EnvironmentObject var appState: AppState
    
    var body: some View {
        VStack() {
            TextField("mail address", text: $email)
            SecureField("password", text: $password)
            Button(action: self.signIn) {
                Text("Sign in")
            }
        }
    }
    
    ///Method to actually sign in
    private func signIn() {
       let sessionInteractor = SessionInteractor(appState: self.appState)
        let result = sessionInteractor.signIn()
    }
}

import SwiftUI

struct ContentView: View {
    @EnvironmentObject var appState: AppState
    
    var body: some View {
        VStack() {
            if let user = self.appState.session.user {
                Text("Hello,\(user.email!)")
                Button(action: self.signOut) {
                  Text("Sign out")
                }
            }
        }.onAppear(perform: self.loadUserInfo)
    }
    
  	//Call the sign-out process
    private func signOut() {
        let sessionInteractor = SessionInteractor(appState: self.appState)
        let result = sessionInteractor.signOut()
    }
}

Definition and disclosure of Optional

The ʻUser` object that AppState has is the heart of this time.

//Structure for managing authentication status
struct Session {
    var user: User? //A structure that has user information as a property, although the description is omitted.
    var handler: AuthStateDidChangeListenerHandle? //An object that holds user information that is signed in
}

These are defined as Optional because the object of the signed-in user is nil before sign-in, after sign-out, etc. After signing in and entering the user information in the User object, we disclose that information in the View.

var body: some View {
    VStack() {
        if let user = self.appState.session.user {
            Text("Hello,\(user.email!)") // !Disclose and display in
            Button(action: self.signOut) {
              Text("Sign out")
            }
        }
    }.onAppear(perform: self.loadUserInfo)
}

What I would like to pay attention to here is the part that branches using ** optional binding syntax **.

if let user = self.appState.session.user {

By writing this, it doesn't matter if the ʻUser object becomes nilafter the user signs out. On the contrary, if this is not described, the View side will receive thenil` assigned at the time of sign-out, and the application will crash.

Fatal error: Unexpectedly found nil while unwrapping an Optional value
try Auth.auth().signOut()
self.appState.session.user = nil //User information is stripped off when signing out

struct2.png

Summary

This time, I took sign-in / sign-out as an example, but I think that there are many cases where multiple views refer to the same value when creating a mobile application. The more views you are referencing, the more likely you are to receive ** unexpected nil **, which will cause your app to crash, but you can prevent them by leveraging the Optional type. .. Everything shouldn't be Optional, but for properties and variables ** that are supposed to be assigned to ** nil, use Optional!

reference

Try Firebase Authentication on iOS Extreme Optional type of Swift [Detailed Swift 5th Edition Chapter4 Optional](https://www.amazon.co.jp/%E8%A9%B3%E8%A7%A3-Swift-%E7%AC%AC5%E7%89%88- % E8% 8D% BB% E5% 8E% 9F-% E5% 89% 9B% E5% BF% 97 / dp / 48150407X)

Recommended Posts

Create a login function using Swift's Optional
Create a filtering function using acts-as-taggable-on
Create a fortune using Ruby
Create a name input function
[Android] Create a calendar using GridView
Create a Jetty project using Eclipse
Create a tomcat project using Eclipse
Create a Java project using Eclipse
[Implementation procedure] Create a user authentication function using sorcery in Rails
Login function
Create a web environment quickly using Docker
[Rails] Create an evaluation function using raty.js
[Rails withdrawal] Create a simple withdrawal function with rails
Creating a user authentication function using devise
Create a RESTful API service using Grape
Make a login function with Rails anyway
Create a prefectural select bar using active_hash
Create authentication function in Rails application using devise
[Android] Create a sliding menu without using NavigationView
Create a Privoxy + Tor environment instantly using Docker
Let's create a REST API using WildFly Swarm.
[Rails] How to create a graph using lazy_high_charts
Story of implementing login function using gem sorcery
[Note] Summary of rails login function using devise ①
Create a portfolio app using Java and Spring Boot
Create a Java development environment using jenv on Mac
Create an EC site with Rails 5 ⑨ ~ Create a cart function ~
A memorandum when trying to create a GUI using JavaFX
I tried to make a login function in Java
[Java] Create a filter
[Rails DM] Let's create a notification function when DM is sent!
Create a tomcat project using Eclipse Pleiades All in One
Create a MOB using the Minecraft Java Mythicmobs plugin | Preparation 1