Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
Tags
- Human interface guide
- 리팩토링
- ios
- Refactoring
- HIG
- uitableview
- 클린 코드
- UITextView
- 애니메이션
- Protocol
- UICollectionView
- map
- SWIFT
- Xcode
- combine
- swiftUI
- Observable
- 리펙터링
- Clean Code
- MVVM
- uiscrollview
- swift documentation
- ribs
- RxCocoa
- tableView
- clean architecture
- collectionview
- rxswift
- 스위프트
- 리펙토링
Archives
- Today
- Total
김종권의 iOS 앱 개발 알아가기
[iOS - swift] 1. Alamofire 사용 방법 - Network Layer 구현 (Moya 프레임워크처럼 사용하는 방법) 본문
iOS 응용 (swift)
[iOS - swift] 1. Alamofire 사용 방법 - Network Layer 구현 (Moya 프레임워크처럼 사용하는 방법)
jake-kim 2021. 10. 16. 02:201. Alamofire 사용 방법 - Network Layer 구현 (Moya 프레임워크처럼 사용하는 방법)
2. Alamofire 사용 방법 - 토큰 갱신 방법1 (Interceptor, adapt, retry)
3. Alamofire 사용 방법 - 토큰 갱신 방법2 (AuthenticationCredential, Authenticator, AuthenticationInterceptor)
4. Alamofire 사용 방법 - 로그 Log (EventMonitor)
Alamofire를 이용한 Network Layer
Network/Bases
- TargetType은 API들의 공통 Endpoint를 가지고 있는 모듈
- Alamofire에 내장되어 있는 protocol인 URLRequestConvertible의 asURLRequest를 구현하여 AF.request할 때 URLRequest객체 커스텀
import Alamofire
protocol TargetType: URLRequestConvertible {
var baseURL: String { get }
var method: HTTPMethod { get }
var path: String { get }
var parameters: RequestParams { get }
}
extension TargetType {
// URLRequestConvertible 구현
func asURLRequest() throws -> URLRequest {
let url = try baseURL.asURL()
var urlRequest = try URLRequest(url: url.appendingPathComponent(path), method: method)
urlRequest.setValue(ContentType.json.rawValue, forHTTPHeaderField: HTTPHeaderField.contentType.rawValue)
switch parameters {
case .query(let request):
let params = request?.toDictionary() ?? [:]
let queryParams = params.map { URLQueryItem(name: $0.key, value: "\($0.value)") }
var components = URLComponents(string: url.appendingPathComponent(path).absoluteString)
components?.queryItems = queryParams
urlRequest.url = components?.url
case .body(let request):
let params = request?.toDictionary() ?? [:]
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
}
return urlRequest
}
}
enum RequestParams {
case query(_ parameter: Encodable?)
case body(_ parameter: Encodable?)
}
extension Encodable {
func toDictionary() -> [String: Any] {
guard let data = try? JSONEncoder().encode(self),
let jsonData = try? JSONSerialization.jsonObject(with: data),
let dictionaryData = jsonData as? [String: Any] else { return [:] }
return dictionaryData
}
}
- RequestParams에서 알 수 있듯이, 모든 parameter들은 Encodable을 준수하는 struct를 받도록 설계했으므로, Encodable을 extension으로 toDictionary() 메소드도 정의
Domain, Request, Response 모델
- Domain 모델: Network에서 사용되는 모델이 아닌, UseCase에서 사용되는 핵심 모델
struct Login {
let name: String
}
- Request, Response 모델: API를 통해 호출하고 응답받을때 받을 모델
- Request는 Encodable을 준수, Response는 Decodable을 준수
struct LoginRequest: Encodable {
let userName: String
let password: String
}
- Response에서는 extension으로 toDomain 정의: Domain으로 변경하여 사용하는 쪽에서는 Domain모델을 받을 수 있도록 추후 completion에서 전달
struct LoginResponse: Decodable {
let name: String
let accessToken: String
let refreshToken: String
}
extension LoginResponse {
var toDomain: Login {
return Login(name: name)
}
}
API, Target 모듈
- LoginTarget: Login도메인에서 사용되는 Endpoint 정의
enum LoginTarget {
case login(LoginRequest)
case getUserDetails(UserDetailsRequest)
}
extension LoginTarget: TargetType {
var baseURL: String {
return "https://www.apiserver.com"
}
var method: HTTPMethod {
switch self {
case .login: return .post
case .getUserDetails: return .get
}
}
var path: String {
switch self {
case .login: return "/login"
case .getUserDetails: return "/details"
}
}
var parameters: RequestParams {
switch self {
case .login(let request): return .body(request)
case .getUserDetails(let request): return .body(request)
}
}
}
- LoginAPI: 외부에서 해당 모듈을 통해 접근
- 핵심: 사용하는쪽에서 받는 모델은 Domain모델인 Login을 사용할수 있게 completion에는 Login 모델을 명시하고,
API를 호출하는데 필요한 request, response는 도메인 모델이 아닌것으로 명시 (LoginRequest, LoginResponse)
- 핵심: 사용하는쪽에서 받는 모델은 Domain모델인 Login을 사용할수 있게 completion에는 Login 모델을 명시하고,
import Alamofire
struct LoginAPI {
/// 이름과 패스워드로 로그인
static func login(request: LoginRequest, completion: @escaping (_ succeed: Login?, _ failed: Error?) -> Void) {
AF.request(LoginTarget.login(request))
.responseDecodable { (response: AFDataResponse<LoginResponse>) in
switch response.result {
case .success(let response):
completion(response.toDomain, nil)
case .failure(let error):
completion(nil, error)
}
}
}
/// 유저 정보 조회
static func getUserDetails(request: UserDetailsRequest, completion: @escaping (_ succeed: Login?, _ failed: Error?) -> Void) {
AF.request(LoginTarget.getUserDetails(request))
.responseDecodable { (response: AFDataResponse<UserDetailsResponse>) in
switch response.result {
case .success(let response):
completion(response.toDomain, nil)
case .failure(let error):
completion(nil, error)
}
}
}
}
사용하는 쪽
- API호출은 static이므로 편리하게 접근 가능
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let request = LoginRequest(userName: "name", password: "1234")
LoginAPI.login(request: request) { succeed, failed in
if let succeed = succeed {
print(succeed)
}
}
}
}
* 전체 소스코드: https://github.com/JK0369/ExAlamofire
'iOS 응용 (swift)' 카테고리의 다른 글
Comments