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
}
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 disclose
name 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)
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. ..
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()
}
}
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 the
nil` 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
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!
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