Development record of developer who study hard everyday.

, ,

iOS Router pattern example

 iOS Router pattern example


1. Why do we need Router pattern?

If we use Router pattern, we can make network layer abstract.

So, We can avoid boilerplater code to make network request.


2. Example code

1)Http Router

protocol HttpRouter: URLRequestConvertible {

    var baseUrlString: String { get }

    var path: String { get }

    var method: HTTPMethod { get }

    var headers: HTTPHeaders? { get }

    var parameters: Parameters? { get }

    func body() throws -> Data?

    

    func request(usingHttpService service: HttpService) throws -> DataRequest

}


extension HttpRouter {

    var parameter: Parameters? { return nil }

    

    func body() throws -> Data? { return nil }

    

    func asURLRequest() throws -> URLRequest {

        var url = try baseUrlString.asURL()

        url.appendPathComponent(path)

        

        var request = try URLRequest(url: url, method: method, headers: headers)

        request.httpBody = try body()

        return request

    }

    

    func request(usingHttpService service: HttpService) throws -> DataRequest {

        return try service.request(asURLRequest())

    }

}


2) AuthHttpRouter

import Foundation

import Alamofire


enum AuthHttpRouter {

    case login(AuthModel)

    case signUp(AuthModel)

    case validate(token: String)

    case refreshToken(token: String)

    case logout(token: String)

    case validateEmail(email: String)

}


extension AuthHttpRouter: HttpRouter {

    

    

    var baseUrlString: String {

        return "https://letscodeeasy.com/groceryapi/public/api"

    }

    

    var path: String {

        switch(self) {

        case .login:

            return "login"

        case .signUp:

            return "register"

        case .validate:

            return "user"

        case .refreshToken:

            return "token/refresh"

        case .logout:

            return "logout"

        case .validateEmail:

            return "validate/email"

        }

    }

    

    var method: HTTPMethod {

        switch (self) {

        case .login, .signUp, .refreshToken, .logout, .validateEmail:

            return .post

        case .validate:

            return .get

        }

    }

    

    var headers: HTTPHeaders? {

        switch(self) {

        case .login, .signUp, .validateEmail:

            return ["Content-Type": "application/json; charset=UTF-8"]

        case .validate(let token), .refreshToken(let token), .logout(let token):

            return [

                "Authorization" : "Bearer \(token)",

                "Content-Type" : "application/json; charset=UTF-8"

            ]

        }

    }

    

    var parameters: Parameters? {

        return nil

    }


    func body() throws -> Data? {

        switch self {

        case .signUp(let user):

            return try JSONEncoder().encode(user)

        case .validateEmail(let email):

            return try JSONEncoder().encode(["email" : email])

        default:

            return nil

        }

    }

}

AuthHttpRouter class implement HttpRouter protocol.

So, We can pick up the path, method, body parameter when we make a network request.


3) HttpService

protocol HttpService {

    var sessionManager: Session { get set }

    func request(_ urlRequest: URLRequestConvertible) -> DataRequest

}


4) AuthHttpService

import Alamofire


final class AuthHttpService : HttpService {

    var sessionManager: Session = Session.default

    

    func request(_ urlRequest: URLRequestConvertible) -> DataRequest {

        return sessionManager.request(urlRequest).validate(statusCode: 200..<400)

    }

}

AuthHttpService implements HttpService protocol.

We make a network request using AuthHttpService class.

The detailed request content is exposed on lower level of network layer, AuthAPI. 


5) AuthAPI

import Foundation

import Combine


protocol AuthAPI {

    func checkEmail(email: String) -> Future<Bool, Never>

    func signUp(username: String, email: String, password: String) -> Future<(statusCode: Int, data: Data), Error>

}


6)AuthService

import Foundation

import Combine


enum SignUpError : Error {

    case emailExists

    case invalidData

    case invalidJSON

    case error(error: String)

}


enum AuthResult<T> {

    case success(value: T)

    case failure(message: String)

}


class AuthService {

    lazy var httpService = AuthHttpService()

    static let shared: AuthService = AuthService()

    private init() {}

}


extension AuthService: AuthAPI {

    func signUp(username: String, email: String, password: String) -> Future<(statusCode: Int, data: Data), Error> {

        return Future<(statusCode: Int, data: Data), Error> { [httpService] promise in

            do {

                try AuthHttpRouter

                    .signUp(AuthModel(name: username, email: email, password: password))

                    .request(usingHttpService: httpService)

                    .responseJSON { response in

                        guard

                            let statusCode = response.response?.statusCode,

                            let data = response.data,

                            statusCode == 200 else {

                            promise(.failure(SignUpError.invalidData))

                            return

                        }

                        promise(.success((statusCode: statusCode, data: data)))

                    }

            } catch {

                print("Something went wrong : \(error)")

                promise(.failure(SignUpError.invalidData))

            }

        }

        

    }

    

    func checkEmail(email: String) -> Future<Bool, Never> {

        return Future<Bool, Never> { [httpService] promise in

            do {

                try AuthHttpRouter

                    .validateEmail(email: email)

                    .request(usingHttpService: httpService)

                    .responseJSON { response in

                        guard response.response?.statusCode == 200 else {

                            print("checkEmail fail")

                            promise(.success(false))

                            return

                        }

                        promise(.success(true))

                        print("checkEmail success")

                    }

            } catch {

                promise(.success(false))

                print("checkEmail error")

            }

            

        }

    }

}

AuthService class implement AuthAPI protocol.

We start to make a network request on this layer.

If you use MVVM pattern, you make a network request using AuthService on ViewModel.



















Share:
Location: Markt 4, 53902 Bad Münstereifel, 독일

댓글 없음:

댓글 쓰기