2020. 10. 2. 23:15ㆍSWIFT
클로저의 기본에 대해 포스팅을 한 적이 있었는데요.
이번에는 클로저 활용에 대해 포스팅을 해볼게요.
이전 포스팅을 안보신 분은 보고 오시면 좋을 것 같아요.
@escaping이라는 문구가 붙은 클로저를 혹시 보신적이 있을까요⁉️
이게 바로 오늘 포스팅에서 알아볼 탈출 클로저라는 것입니다.
더 자세히 예로 볼게요.
func withEscaping(completion: @escaping () -> Void) {
completion()
}
이런 식으로 @escaping이란 선언이 해당 클로저가 탈출을 허용하다고 명시해주는 것이라고 알면 될 것 같아요.
그럼 탈출이란 무엇일까요❓
저기 위에 withEscaping 함수에 전달된 클로저로 예를 들어 설명해볼게요.
위의 예제처럼 매개변수로 불러온 클로저를 해당 함수에서 사용하는건 문제가 되지 않아요.
그러나 만약, 저 클로저를 저장하고 다른 곳에서 호출하려고 하면 어떻게 될까요?
컴파일러 에러로 빨간줄이 호출됩니다.
이를 다른 곳에서 사용이 가능하게 만들기 위해서 @escaping을 선언해 탈출 클로저로 사용하는 것입니다.
즉, 해당 클로저를 어떤 구문 밖으로 탈출 시켜서 사용하겠다는 뜻입니다.
예로 다음과 같은 상황입니다.
// @escaping 선언이 없다면 구문 밖에서 사용이 불가능하기 때문에, 배열에 할당이 불가능합니다.
var completionHandlers: [() -> Void] = []
func withEscaping(completion: @escaping () -> Void) {
completionHandlers.append(completion)
}
보시면 받아온 completion을 함수 구문 밖인 배열에 넣는 것을 알 수 있죠⁉️
이를 통해, 나중에 언제든 배열에서 꺼내어서 사용하겠다는 것입니다.
즉, 함수 구문 밖으로 탈출을 한 것이죠.
그렇다면 한 번 더 명시적인 예제를 만들어서 볼게요.
class Myclass {
var x = 10
func callFunc() {
withEscaping { self.x = 100 }
withoutEscaping { x = 200 }
}
var completionHandlers: [() -> Void] = []
func withEscaping(completion: @escaping () -> Void) {
completionHandler.append(completion)
}
func withoutEscaping(completion: () -> Void) {
completion()
}
}
이렇게 생긴 MyClass라는 클래스가 있죠.
그럼 한 번 메소드를 실행시켜보고 결과가 어떻게 나오는지 볼게요.
let mc = MyClass()
mc.callFunc()
print(mc.x) // 200
mc.completionHandlers.first?()
print(mc.x) // 100
callFunc()을 호출하게 되면 먼저 withoutEscaping 메소드가 클로저를 바로 호출하기때문에, x의 값이 200으로 바뀌는 것을 확인 할 수 있어요.
이후 withEscaping에서는 클로저를 바로 호출하는 것이 아니라 completionHandlers 배열에 저장을 하게 되죠⁉️
그리고 호출하는 순간 x의 값이 100으로 바뀌는 것을 확인할 수 있어요.
이렇듯이 탈출 클로저을 사용하면 함수 구문 밖에서 사용을 할 수 있어요.
가장 단편적인 예를 보았고,
저는 이 클로저를 가장 자주 사용하고 많이 보았던 경우가 Completion Handler으로 사용하는 경우입니다.
예로, 네트워크 요청 작업이 있고 비동기적으로 이를 처리하고 이 처리가 끝난 후 동작하는 것을 Completion Handler에 명령하는 것입니다.
아마 iOS 개발을 하면서 네트워크 통신을 해보신 분이면 Completion이라는 변수를 많이 보았죠⁉️
바로 이곳에 여러분들이 원하는 동작을 하는 클로저를 선언해주시면 됩니다.
이 클로저들이 @escaping으로 선언되어있는 부분입니다.
예로, 제가 로그인에 사용한 코드를 보며 어떻게 사용하는지 볼게요.
private func requestSignup(_ url: String, _ headers: HTTPHeaders?, _ parameters: Parameters?, _ completion: @escaping (NetworkResult<Codable>) -> Void) {
guard let url = try? url.asURL() else { return }
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
.validate(statusCode: 200...500)
.responseDecodable(of: APIResponseData<APICantSortableDataResult<SignupResponse>, APIError>.self) { response in
switch response.result {
case .success(let signupResponse):
guard let statusCode = response.response?.statusCode else { return }
if statusCode == 200 {
completion(.success(signupResponse.data?.result)) }
else { completion(.requestErr(signupResponse.error?.errorMessage)) }
case .failure:
completion(.networkFail)
}
}
}
위에서 주목해야하는 부분은 completion을 언제 호출하는 지입니다.
보시면 statusCode == 200이라는 조건을 만족했을 때, completion(.success("object"))로 선언해주시는 것이 보이죠⁉️
저부분이 바로 통신이 성공했으니 200에 맞는 데이터를 넣어주고 탈출 클로저를 실행시켜주는 부분입니다.
그렇다면 실패했을 때는 실패했다는 것을 알 수 있는 데이터를 넣고 탈출 클로저를 실행시켜주면 되겠죠❓
여기는 통신을 하는 코드 부분이었고, completion에 클로저를 넣어주는 부분에서는 로그인 결과에 다른 처리를 해주면되겠죠.
✔️ 로그인에 성공했을 때
- 다음 화면으로 넘어간다.
- Token 값을 저장한다.
✔️ 로그인을 실패했을 때
- 로그인 실패 팝업 화면을 띄운다.
탈출 클로저를 사용해 결과를 받아오고 결과에 맞는 분기처리로 해당 함수 구문 밖에서도 적절한 동작을 할 수 있도록 설계할 수 있답니다.
오늘은 지난 번에 포스팅 했던 클로저(Closure)에 이어서 활용편인 탈출 클로저(Closure)에 대해 포스팅을 해봤어요🤗
잘못된 부분이나 궁금한 점이 있으면 댓글주세요~~
'SWIFT' 카테고리의 다른 글
[SWIFT] Patterns, Pattern Matching - 패턴과 패턴매칭 (1) (0) | 2021.01.10 |
---|---|
[SWIFT] 연산자 오버로딩 + 연산자 커스텀 (0) | 2020.11.28 |
[SWIFT] XML Parser 사용하기 (2) | 2020.09.11 |
[SWIFT] Closure(클로저) (0) | 2020.08.27 |
[SWIFT] Generic (0) | 2020.08.22 |