[SwiftUI] @escaping

Junyoung Park·2022년 8월 19일
0

SwiftUI

목록 보기
20/136
post-thumbnail
post-custom-banner

How to use escaping closures in Swift | Continued Learning #20

@escaping

구현 목표

  • 비동기적으로 처리된 데이터를 다루는 방법 중 하나인 이스케이핑 클로저를 사용
  • 비동기적 처리가 완료된 이후를 보장하는 completionHandler

구현 태스크

  1. 비동기 처리 방법 중 이스케이핑 클로저를 사용한다.
  2. 비동기 과정(데이터 다운로드 등)을 모방하기 위한 DispatchQueue.main.asyncAfter를 사용한다.
  3. weak self를 통해 뷰에서 사라지는 경우 참조 사이클을 끊는다.

핵심 코드

func getData() {
        downloadData5 { [weak self] data in
            self?.text = data.data
        }
    }
...
func downloadData4(completionHandler: @escaping (_ data: DownloadResult) -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            let result = DownloadResult(data: "New Data!")
            completionHandler(result)
        }
    }
    
    func downloadData5(completionHandler: @escaping DownloadCompletion) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            let result = DownloadResult(data: "New Data!")
            completionHandler(result)
        }
    }

struct DownloadResult {
    let data: String
}

typealias DownloadCompletion = (DownloadResult) -> ()
  • 클로저는 내부 값을 밖으로 전달하지 못하는 게 디폴트 (nonescaping)
  • @escaping 선언을 통해 밖으로 값 전달 가능 → 클로저의 특성 이용, 내부 작업이 끝날 때(= 비동기 작업이 끝나서 네트워크, 인터넷 등 데이터 다운로드가 완료되었을 때) 값을 전달 가능
  • completionHandler 클로저의 typealias로 클로저 타입 전달 가능

소스 코드

import SwiftUI

class EscapingViewModel: ObservableObject {
    @Published var text = "Hello!"
    
    init() {
        print("INIT")
    }
    
    deinit {
        print("DEINIT")
    }
    
    func getData() {
//        downloadData3 { [weak self] data in
//            self?.text = data
//        }
//        doSomething(text)
        downloadData5 { [weak self] data in
            self?.text = data.data
        }
    }
    
    func downloadData() -> String {
        return "New Data"
    }
    
    func downloadData2(completionHandler: (_ data: String) -> Void) {
        completionHandler("New DATA!")
    }
    
    func downloadData3(completionHandler: @escaping (_ data: String) -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            completionHandler("New Data!")
        }
    }
    
    func downloadData4(completionHandler: @escaping (_ data: DownloadResult) -> Void) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            let result = DownloadResult(data: "New Data!")
            completionHandler(result)
        }
    }
    
    func downloadData5(completionHandler: @escaping DownloadCompletion) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
            let result = DownloadResult(data: "New Data!")
            completionHandler(result)
        }
    }
    
    func doSomething(_ data: String) {
        print(data)
    }
}

struct DownloadResult {
    let data: String
}

typealias DownloadCompletion = (DownloadResult) -> ()

struct EscapingBootCamp: View {
    @StateObject private var viewModel = EscapingViewModel()
    var body: some View {
        Text(viewModel.text)
            .font(.largeTitle)
            .fontWeight(.semibold)
            .foregroundColor(.red)
            .onTapGesture {
                viewModel.getData()
            }
    }
}

구현 화면

profile
JUST DO IT
post-custom-banner

0개의 댓글