ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift Moya 설치 및 내용, RxMoya
    Swift 2021. 10. 13. 20:20

    URLSession, Alamofire를 사용한뒤, Moya 를 사용해보기로 했다. => 적용하고나서의 나 대만족

    우선 알아야되는 사항 13이상부터 가능함.

    - Moya' has a minimum deployment target of iOS 13.0

    설치방법

    pod file에 아래를 추가

    pod 'Moya'

    pod install 하면, alamofire와 함께 install 되는것을 확인 할 수 있다.

     

    RxSwift를 사용하면서 Moya를 사용한다면 아래처럼 설치해야한다.

    pod 'Moya/RxSwift'

    설명

    https://github.com/Moya/Moya

    Moya 란, Alamofire를 한번 더 감싼 네트워크 라이브러리 할 수 있다. 그렇다면 왜 굳이 이런게 필요할까? 보통 작성하는 APIManager or NetworkModel의 역할을 대신 해줌으로써 네트워크 추상화 계층을 만들어 준다.

    어디서 시작하는지, 어떻게 다시 시작하는지 에 대한 내용을 더 쉽게 만들 수 있다는 말.

     

    또 다음과 같은 멋진 기능도 해준다고 설명한다

    • Compile-time checking for correct API endpoint accesses.
    • Lets you define a clear usage of different endpoints with associated enum values.
    • Treats test stubs as first-class citizens so unit testing is super-easy.

    enum 타입 기반으로 type-safe한 레이어 구성이 가능하고 endPoint에 target 커스터마이징 자유도가 높아서 좋았다.

    특히 service를 만들때 단순히 TargetType을 준수하면서 쉽게 작성가능하다.

     

    아래가 git에서 작성해둔 Moya의 장점을 시각화한것이다.

     

    + 참고로 사용은 하지 않았지만 RxMoya도 있다.


    사용예시

    아래처럼 통신을 호출해서 사용한다.

    private let mainProvider = MoyaProvider<MainService>()
    
    mainProvider.request(.inquiryAllAccouunt(inquiryModel: InquiryRequestModel())) { (result) in
    			// self.showLoading.accept(true) loading 끄는 로직
    			switch result {
    			case .success(let response):
    				if let model = ModelClassName.decode(data: response.data) {
    					// data processing
    				}
    			case .failure(let error):
    				print("error: \(error)")
    			}
    		}

     

    RxSwift 예시

    provider = MoyaProvider<GitHub>()
    provider.rx.request(.userProfile("ashfurrow")).subscribe { event in
        switch event {
        case let .success(response):
            image = UIImage(data: response.data)
        case let .error(error):
            print(error)
        }
    }

    실제 Service 코드

    import Foundation
    import Moya
    
    enum RootService {
    	case doStart(rootModel: RootRequestModel) // 이런식으로 정의
    	case loggedIn(loginModel: LoginRequestModel)
    }
    
    extension RootService: TargetType {
    	var baseURL: URL {
    		let urlString = "https://~~"
    		guard let url = URL(string: urlString) else { fatalError() }
    		return url
    	}
    
    	var path: String {
    		switch self {
    		case .doStart(rootModel: _):
    			return "/testDoStart" // 이런식으로
    		case .loggedIn(loginModel: _):
    			return ""
    		}
    	}
    
    	var method: Moya.Method {
    		switch self {
    			case .doStart(rootModel: _), .loggedIn(loginModel: _):
    			return .post
    		}
    	}
    
    	var sampleData: Data {
    		switch self {
    		case .doStart(let test):
    			return "{'name':'\(test)'}".data(using: .utf8)!
    		case .loggedIn(let test):
    			return "{'name':'\(test)'}".data(using: .utf8)!
    		}
    	}
    
    	var task: Task {
    		switch self {
    		case .doStart(let rootModel): // 나는 이런식으로 작성 구글링 해보면 다른방법도 엄청 많음
    			return .requestCustomJSONEncodable(rootModel, encoder: JSONEncoder())
    		case .loggedIn(let loginModel):
    			return .requestCustomJSONEncodable(loginModel, encoder: JSONEncoder())
    		}
    	}
    
    	var headers: [String: String]? {
    		return ["Content-Type": "application/json"]
    	}
        
        var validationType: ValidationType {
        	return .successCodes // 이걸 사용하면 HTTP Code가 200에서 299인 경우 요청 성공으로 간주해줌
      }
    }

    결론

    실제로 소스코드가 굉장히 직관적이고 사용하기 편했다. 나는 또 사용할것같다. 실제로 뭔가 request response만 간결히 신경쓸 수 있었다고 생각한다.

    특히 코드에서 다른점을 보자면, Alamofire를 사용해도 Network Layer에 접근 할수 없으니(request에 Url 넣어줘야함) 따로 APIManager 이런걸 만드는데 템플릿이 없으니 enum 기반 type safe한 Moya가 재사용에도 유리했다.

     

    ++

    import Foundation
    import Moya
    
    enum APIEnvironment: String {
        case dev         = "https://~~"
        case test    = "https://~~"
    }
    
    struct NetworkManager {
        fileprivate let provider = MoyaProvider<RootService>()
        static let environment: APIEnvironment = .dev
    }
    
    // 이런식으로 NetworkManager 를 만들어두고 실제로는 적용해서 쓰면 된다.
    
    // 위의 target쪽에 아래처럼 baseURL 수정
    public var baseURL: URL {
            
            guard let url = URL(string: NetworkManager.environment.rawValue) else {
                fatalError("fatal error")
            }
            return url
        }

     

    조금 더 작성하자면 아래처럼 BaseAPI(명명 수정 가능)를 만들어 두고 사용하는것도 좋은 방법이다.

    protocol BaseAPI: TargetType {}
    
    extension BaseAPI {
    	var baseURL: URL {
    		guard let url = URL(string: MoyaNetworkManager.environment.rawValue) else {
    			fatalError("fatal error")
    		}
    		return url
    	}
    
    	var path: String { return "" }
    
    	var method: Moya.Method { .post }
    
    	var sampleData: Data { Data() }
    
    	var task: Task { .requestPlain }
    
    	var headers: [String: String]? {
    		return ["Content-Type": "application/json"]
    	}
    
    	public var validationType: ValidationType {
    		return .successCodes
    	}
    }
    
    // 내가 사용하는 service에서
    enum RootService {
    	case doStart(rootModel: RootRequestModel)
    }
    
    extension RootService: BaseAPI {
    	var sampleData: Data {
    		switch self {
    		case .doStart(let trxCode):
    			return "{'name':'\(trxCode)'}".data(using: .utf8)!
    		}
    	}
    
    	var task: Task {
    		switch self {
    		case .doStart(let rootModel):
    			return .requestCustomJSONEncodable(rootModel, encoder: JSONEncoder())
    		}
    	} // 사실 보통 path를 custom함.
    }

     

    참고

    https://ios-development.tistory.com/193 : Rx에 대한 예제는 글에 적지 못했지만, 여기를 많이 참고했다 감사합니다.

    728x90

    댓글

Designed by Tistory.