Post

Closure to Async

Closure 함수를 Async 로 변경

continuation 활용

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/// 모든 할 일 목록 가져오기
    static func fetchTodos(page: Int = 1, completion: @escaping (Result<BaseListResponse<Todo>, ApiError>) -> Void ) {
        // 1. urlRequest 를 만든다
        let urlString = baseURL + "/todos" + "?page=\(page)"
        
        guard let url = URL(string: urlString) else {
            return completion(.failure(ApiError.notAllowedUrl))
        }
        
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "GET"
        urlRequest.addValue("application/json", forHTTPHeaderField: "accept")
        
         URLSession.shared.dataTask(with: urlRequest) { data, response, err in
            if let error = err {
                return completion(.failure(ApiError.unknown(error)))
            }
            guard let httpResponse = response as? HTTPURLResponse else {
                print("bad status code")
                return completion(.failure(ApiError.unknown(nil)))
            }
            
            switch httpResponse.statusCode {
            case 401:
                return completion(.failure(ApiError.unauthorized))
            default: print("default")
            }
            
            if !(200...299).contains(httpResponse.statusCode) {
                return completion(.failure(ApiError.badStatus(code: httpResponse.statusCode)))
            }
            
            if let jsonData = data {
                do {
                    let listResponse = try JSONDecoder().decode(BaseListResponse<Todo>.self, from: jsonData)
                    guard let todos = listResponse.data,
                          !todos.isEmpty else {
                        return completion(.failure(ApiError.noContent))
                    }
                    completion(.success(listResponse))
                } catch {
                    completion(.failure(.decodingError))
                }
            }
            
        }.resume()
    }

flatMap

아래 코드는 버튼 탭을 하면 Int 값을 전달하는 Observalble 을 생성가고, 그걸 구독하고 있다.

두번 탭을 통해 스트림 여러개가 누락없이 전부 오는것을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
testBtn.rx.tap
            .scan(0) { aNumber, _ -> Int in
                print("tapped")
                return aNumber + 1
            }.flatMap { tapNumber -> Observable<Int> in
                Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
                    .do(onNext: { intervalNumber in
                        print(#line, "tapNumber: \(tapNumber) - inervalNumber: \(intervalNumber)")
                    })
            }.subscribe(onNext: { intervalNumber in
                // print(#line, "- \(intervalNumber)") (보기 편하게 주석 처리함)
            }).disposed(by: distposeBag)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
tapped  
44 tapNumber: 1 - inervalNumber: 0
44 tapNumber: 1 - inervalNumber: 1
44 tapNumber: 1 - inervalNumber: 2
44 tapNumber: 1 - inervalNumber: 3
tapped 두번째 
44 tapNumber: 1 - inervalNumber: 4
44 tapNumber: 2 - inervalNumber: 0
44 tapNumber: 1 - inervalNumber: 5
44 tapNumber: 2 - inervalNumber: 1
44 tapNumber: 1 - inervalNumber: 6
44 tapNumber: 2 - inervalNumber: 2
44 tapNumber: 1 - inervalNumber: 7
44 tapNumber: 2 - inervalNumber: 3
44 tapNumber: 1 - inervalNumber: 8

flatMapLatest

동일하게 두번 탭을 해서 Observable 이 2개 생성되었지만

최신 생성한 두번째 탭 이벤트만 전달 하는걸 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
testBtn.rx.tap
                    .scan(0) { aNumber, _ -> Int in
                        print("tapped")
                        return aNumber + 1
                    }.flatMapLatest { tapNumber -> Observable<Int> in
                        Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance)
                            .do(onNext: { intervalNumber in
                                print(#line, "tapNumber: \(tapNumber) - inervalNumber: \(intervalNumber)")
                            })
                    }.subscribe(onNext: { intervalNumber in
                        // print(#line, "- \(intervalNumber)")
                    }).disposed(by: distposeBag)
1
2
3
4
5
6
7
8
9
10
11
tapped  
57 tapNumber: 1 - inervalNumber: 0
57 tapNumber: 1 - inervalNumber: 1
57 tapNumber: 1 - inervalNumber: 2
57 tapNumber: 1 - inervalNumber: 3
tapped 두번째 
57 tapNumber: 2 - inervalNumber: 0
57 tapNumber: 2 - inervalNumber: 1
57 tapNumber: 2 - inervalNumber: 2
57 tapNumber: 2 - inervalNumber: 3
57 tapNumber: 2 - inervalNumber: 4

Combine

flatMap

RxSwift 와 동일하게 사용하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
testBtn.tapPublisher
                    .handleEvents(receiveOutput: {
                        print("tapped")
                    })
                    .scan(0) { aNumber, _ -> Int in
                        return aNumber + 1
                    }
                    .flatMap { tapNumber -> AnyPublisher<Int, Never> in
                        Timer.publish(every: 1, on: .main, in: .common).autoconnect()
                            .scan(0) { aNumber, _ -> Int in
                                return aNumber + 1
                            }
                            .handleEvents(receiveOutput: { intervalNumber in
                                print(#line, "tapNumber: \(tapNumber) - intervalNumber: \(intervalNumber)")
                            }).eraseToAnyPublisher()
                    }
                    .sink(receiveValue: { intervalNumber in
                        //print(#line, "- \(intervalNumber)")
                    }).store(in: &subscriptions)
1
2
3
4
5
6
7
8
9
10
11
12
13
tapped
77 tapNumber: 1 - intervalNumber: 1
77 tapNumber: 1 - intervalNumber: 2
77 tapNumber: 1 - intervalNumber: 3
77 tapNumber: 1 - intervalNumber: 4
tapped
77 tapNumber: 1 - intervalNumber: 5
77 tapNumber: 2 - intervalNumber: 1
77 tapNumber: 1 - intervalNumber: 6
77 tapNumber: 2 - intervalNumber: 2
77 tapNumber: 1 - intervalNumber: 7
77 tapNumber: 2 - intervalNumber: 3
77 tapNumber: 1 - intervalNumber: 8

flatMapLatest

flatMap → map 으로 변경

.sink 위에 .switchToLatest() 를 추가 해야 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
testBtn.tapPublisher
                    .handleEvents(receiveOutput: {
                        print("tapped")
                    })
                    .scan(0) { aNumber, _ -> Int in
                        return aNumber + 1
                    }
                    .map { tapNumber -> AnyPublisher<Int, Never> in
                        Timer.publish(every: 1, on: .main, in: .common).autoconnect()
                            .scan(0) { aNumber, _ -> Int in
                                return aNumber + 1
                            }
                            .handleEvents(receiveOutput: { intervalNumber in
                                print(#line, "tapNumber: \(tapNumber) - intervalNumber: \(intervalNumber)")
                            }).eraseToAnyPublisher()
                    }
                    .switchToLatest()
                    .sink(receiveValue: { intervalNumber in
                        //print(#line, "- \(intervalNumber)")
                    }).store(in: &subscriptions)
1
2
3
4
5
6
7
8
9
10
tapped
98 tapNumber: 1 - intervalNumber: 1
98 tapNumber: 1 - intervalNumber: 2
98 tapNumber: 1 - intervalNumber: 3
98 tapNumber: 1 - intervalNumber: 4
tapped
98 tapNumber: 2 - intervalNumber: 1
98 tapNumber: 2 - intervalNumber: 2
98 tapNumber: 2 - intervalNumber: 3
98 tapNumber: 2 - intervalNumber: 4
This post is licensed under CC BY 4.0 by the author.

Trending Tags