Applying Builder Pattern for Sign in process (User-Password, Facebook, Google)
In this post I will be talking about how to create a builder that conforms the sign in foundations for User-Password, Facebook, Google process.
What is a builder pattern?
The builder pattern is a design pattern (creational pattern) that allows us to create complex objects using sequence of actions.
With that being said let’s code an example.
The example
The goal of this example will be create a generic sign in instance that allows us to create different types of Sign in process, those are listed below:
- UserSignIn
This sign in contains username, password and session properties.
- FacebookSignIn
This sign in contains Facebook token and api key properties.
- GoogleSignIn
This sign in contains Google token, api key and host properties.
First we need to define a protocol that contains all the properties used in the builder.
protocol SignInDataSource { var email: String? { get } var password: String? { get } var facebookToken: String? { get } var googleToken: String? { get }}
We need to create a provider class that conforms the SignInDataSource protocol.
final class SignInProvider<T: SignIn>: SignInDataSource { private var elementType: SignInBuilderDataSource.Type fileprivate(set) var email: String? fileprivate(set) var password: String? fileprivate(set) var facebookToken: String? fileprivate(set) var googleToken: String? var instance: T? init() { }}
Also any class used in the Builder needs to conform the SignInBuilderDataSource protocol.
// MARK: - SignInBuilderDataSource Protocol/// Protocol that all generic SignInBuilder class should implement.protocol SignInBuilderDataSource { init()}
Now let’s create the builder class.
// MARK: - Sign in Builder/// Builder class used to simplify object creation.final class SignInBuilder<T: SignInBuilderDataSource> { private var innerBuild = SignInProvider<T>()
init() { }
/// Setter for setting email. /// - Parameters: /// - email: Email. /// - Returns: A `SignInBuilder` @discardableResult func setEmail(_ email: String) -> SignInBuilder { innerBuild.email = email return self }
/// Setter for setting password. /// - Parameters: /// - password: Password. /// - Returns: A `SignInBuilder` @discardableResult func setPassword(_ password: String) -> SignInBuilder { innerBuild.password = password return self }
/// Setter for setting Facebook token. /// - Parameters: /// - facebookToken: Facebook token. /// - Returns: A `SignInBuilder` @discardableResult func setFacebookToken(_ facebookToken: String) -> SignInBuilder { innerBuild.facebookToken = facebookToken return self }
/// Setter for setting Google token. /// - Parameters: /// - googleToken: Google token. /// - Returns: A `SignInBuilder` @discardableResult func setGoogleToken(_ googleToken: String) -> SignInBuilder { innerBuild.googleToken = googleToken return self }
/// Build the instance. /// - Returns: A `SignInProvider<T>` func build() -> SignInProvider<T> { return innerBuild }}
Now that we have the builder done, we can instantiate different cases and validate if they are working properly.
Note: @discardableResult use to suppress the “Result unused” warning.
UserSignIn class
The idea of this class is provides the common credentials used in a sign in process.
First create a class with that name and conforms SignInBuilderDataSource then use that class in the builder.
Apply the necessary methods to set those values and finally call the build method.
final class UserSignIn: SignInBuilderDataSource { let session = URLSession.shared required init() {}}
let userSignIn = SignInBuilder<UserSignIn>() .setEmail("test@host.com") .setPassword("test123") .build()print(userSignIn.email) // test@host.comprint(userSignIn.password) // test123print(userSignIn.instance?.session) // Shared sessionprint(userSignIn.facebookToken) // nilprint(userSignIn.googleToken) // nil
You can see that facebookToken and googleToken are nil because those are not necessary in this process.
FacebookSignIn class
The idea of this class is provides the token and the api key used in the Facebook sign in process.
First create a class with that name and conforms SignInBuilderDataSource then use that class in the builder.
Apply the necessary methods to set those values and finally call the build method.
final class FacebookSignIn: SignInBuilderDataSource { let apiKey = "some api key" required init() { }}
let facebookSignIn = SignInBuilder<FacebookSignIn>() .setFacebookToken("2318120938jnjnkjnkda123") .build()print(facebookSignIn.facebookToken) // 2318120938jnjnkjnkda123print(facebookSignIn.instance?.apiKey) // some api keyprint(facebookSignIn.email) // nilprint(facebookSignIn.password) // nilprint(facebookSignIn.googleToken) // nil
You can see that email, password and googleToken are nil because those are not necessary in this process.
GoogleSignIn class
The idea of this class is provides the google token, api key and host properties used in the GoogleSignIn sign in process.
First create a class with that name and conforms SignInBuilderDataSource then use that class in the builder.
Apply the necessary methods to set those values and finally call the build method.
final class GoogleSignIn: SignInBuilderDataSource { let apiKey = "some api key" let host = "https://google.com"
required init() { }}
let googleSignIn = SignInBuilder<GoogleSignIn>() .setGoogleToken("2318120938jnjnkjnkda1231qdas") .build()print(googleSignIn.googleToken) // 2318120938jnjnkjnkda1231qdasprint(googleSignIn.instance?.apiKey) // some api keyprint(googleSignIn.instance?.host) // https://google.comprint(googleSignIn.email) // nilprint(googleSignIn.password) // nilprint(googleSignIn.facebookToken) // nil
You can see that email, password and facebookToken are nil because those are not necessary in this process.
Conclusions
We can see that is possible share some properties to multiple sign in process and use methods for create the object with the necessary values.
I hope this can help you and apply the Builder Pattern concept to other cases.
Resource