Continuation

Updated:

Continuation

When you are working with SDK and API that are not updated for swift concurrency you can use a continuation to convert them to be usable with your asynchrony swift concurrency code

  • Simple async code with fetched single image from API by using async await and URLSession.shared.data
import SwiftUI

// MARK: - SERVICE
class CheckedContinuationBootCampNetworkManager {
func getData(url: URL) async throws -> Data {
do {
  let (data, _) = try await URLSession.shared.data(from: url, delegate: nil)
  return data
} catch  {
  throw error
}
}
}


// MARK: - VIEWMODEL
class CheckedContinuationBootCampViewModel: ObservableObject {
// MARK: -  PROPERTY
@Published var image: UIImage? = nil
let manager = CheckedContinuationBootCampNetworkManager()
// MARK: -  INIT
// MARK: -  FUNCTION
func getImage() async {
guard let url = URL(string: "https://picsum.photos/300") else { return }

do {
  let data = try await manager.getData(url: url)

  if let image = UIImage(data: data) {
    await MainActor.run(body: {
      self.image = image
    })
  }
} catch {
  print(error)
}
}
}

// MARK: - VIEW
struct CheckedContinuationBootCamp: View {
// MARK: -  PROPERTY
@StateObject private var vm = CheckedContinuationBootCampViewModel()
// MARK: -  BODY
var body: some View {
  ZStack {
    if let image = vm.image {
      Image(uiImage: image)
        .resizable()
        .scaledToFit()
        .frame(width: 200, height: 200)
    }
  } //: ZSTACK
  .task {
    await vm.getImage()
  }
}
}

스크린샷

But unfortunately a lof of SDKs and APIs are not yet updated for async and await some of them maybe will be updated in the future. You want to be able to convert their SDK form as it is to async await

So, use completion handler oen of the original ways that we did asynchronous code in swift and using escaping closures

class CheckedContinuationBootCampNetworkManager {

func getData2(url: URL) async throws -> Data {
return try await withCheckedThrowingContinuation { continuation in
URLSession.shared.dataTask(with: url) { data, response, error in
  // withCheckedThrowingContinuation in we get data and we resume it once perfect
  if let data = data {
    continuation.resume(returning: data)
  } else if let error = error {
    continuation.resume(throwing: error)
  } else {
    continuation.resume(throwing: URLError(.badURL))
  }
  // need to resume it exactly once -> will be crashed APP
  // continuation.resume(throwing: URLError(.badURL))
}
.resume()
}
}
}


// MARK: - VIEWMODEL
class CheckedContinuationBootCampViewModel: ObservableObject {
// MARK: -  PROPERTY
@Published var image: UIImage? = nil
let manager = CheckedContinuationBootCampNetworkManager()
// MARK: -  INIT
// MARK: -  FUNCTION
func getImage() async {
  guard let url = URL(string: "https://picsum.photos/300") else { return }

  do {
    let data = try await manager.getData2(url: url)

    if let image = UIImage(data: data) {
      await MainActor.run(body: {
        self.image = image
      })
    }
  } catch {
    print(error)
  }
}
}

// MARK: - SERVICE
class CheckedContinuationBootCampNetworkManager {
func getHeartImageFromDatabase(completionHandler: @escaping (_ image: UIImage) -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
  completionHandler(UIImage(systemName: "heart.fill")!)
}
}

func getHeartImageFromDatabase2() async -> UIImage {
await withCheckedContinuation { continuation in
  getHeartImageFromDatabase { image in
    continuation.resume(returning: image)
  }
}
}
}


// MARK: - VIEWMODEL
class CheckedContinuationBootCampViewModel: ObservableObject {
// MARK: -  PROPERTY
@Published var image: UIImage? = nil
let manager = CheckedContinuationBootCampNetworkManager()
// MARK: -  INIT
// MARK: -  FUNCTION

func getHeartImage() async {
manager.getHeartImageFromDatabase { [weak self ] image in
  self?.image = image
}
}

func getHeartImage2() async {
self.image = await manager.getHeartImageFromDatabase2()
}
}

// MARK: - VIEW
struct CheckedContinuationBootCamp: View {
// MARK: -  PROPERTY
@StateObject private var vm = CheckedContinuationBootCampViewModel()
// MARK: -  BODY
var body: some View {
ZStack {
  if let image = vm.image {
    Image(uiImage: image)
      .resizable()
      .scaledToFit()
      .frame(width: 200, height: 200)
  }
} //: ZSTACK
.task {
  // await vm.getImage()
  // await vm.getHeartImage()
  await vm.getHeartImage2()
}
}
}

스크린샷


🗃 Reference

SwiftUI Thinking - https://youtu.be/Tw_WLMIfEPQ

Leave a comment