0Pricing
Swift Academy · Lección

Conectar callbacks heredados/Combine con async/await

Envuelva los completion handlers con withCheckedContinuation / withCheckedThrowingContinuation y conecte los publishers de Combine con AsyncSequence para usar bucles sencillos de for await .

Conectar callbacks heredados/Combine con async/await es una lección gratuita de Swift Academy en CoddyKit. Esta es la lección 2 de 3. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Swift Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Swift Academy incluye 3 lecciones en total.

¿Por qué crear puentes?

Objetivo: Use continuaciones para envolver callbacks y trate los flujos de Combine como AsyncSequence.

  • Continuaciones (sin lanzar errores / que lanzan errores)
  • Ejemplo de puente con URLSession
  • Consumo de Combine → for await

Continuación sin errores

Use withCheckedContinuation cuando la API heredada no pueda fallar. Reanude exactamente una vez.

// Old-style API
func fetchName(completion: @escaping (String) -> Void) {
    // simulate async
    Task { try? await Task.sleep(nanoseconds: 80_000_000); completion("Ana") }
}

// Async wrapper using continuation (non-throwing)
func fetchName() async -> String {
    await withCheckedContinuation { (cont: CheckedContinuation<String, Never>) in
        fetchName { value in cont.resume(returning: value) }
    }
}

Task {
    let name = await fetchName()
    print("name:", name) // "Ana"
}

Continuación que lanza errores

Use withCheckedThrowingContinuation para exponer los errores heredados mediante throw. Asegúrese de que haya una única ruta de reanudación.

enum NetError: Error { case offline; case badStatus }

func loadNumber(completion: @escaping (Result<Int, Error>) -> Void) {
    Task {
        try? await Task.sleep(nanoseconds: 80_000_000)
        completion(.failure(NetError.offline))
    }
}

func loadNumber() async throws -> Int {
    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<Int, Error>) in
        loadNumber { result in
            switch result {
            case .success(let v): cont.resume(returning: v)
            case .failure(let e): cont.resume(throwing: e)
            }
        }
    }
}

Task {
    do {
        let n = try await loadNumber()
        print(n)
    } catch {
        print("error:", error) // offline
    }
}

Puente con URLSession

Envuelva las API basadas en completion handler (por ejemplo, URLSession) para obtener una función limpia async throws. Recuerde reanudar exactamente una vez.

import Foundation

func getText(from url: URL) async throws -> String {
    try await withCheckedThrowingContinuation { (cont: CheckedContinuation<String, Error>) in
        let task = URLSession.shared.dataTask(with: url) { data, resp, err in
            if let err = err { cont.resume(throwing: err); return }
            guard let data = data, let text = String(data: data, encoding: .utf8) else {
                cont.resume(throwing: URLError(.badServerResponse)); return
            }
            cont.resume(returning: text)
        }
        task.resume()
    }
}

// Demo (works in environments with network access)
let demoURL = URL(string: "https://example.com")!
Task {
    do { let body = try await getText(from: demoURL); print(body.prefix(15)) }
    catch { print("network error:", error) }
}

Combine como AsyncSequence

Muchos publishers de Combine exponen .values, lo que permite recorrer las emisiones con for await sin crear puentes personalizados.

import Combine
import Foundation

let subject = PassthroughSubject<Int, Never>()

// Consume Combine as AsyncSequence:
Task {
    var produced: [Int] = []
    for await value in subject.values {          // Publisher.values -> AsyncSequence
        produced.append(value)
        if produced.count == 3 { break }         // stop after 3 values
    }
    print("collected:", produced)                // e.g., [1,2,3]
}

// Publish a few values
var cancellables = Set<AnyCancellable>()
Timer.publish(every: 0.05, on: .main, in: .common)
    .autoconnect()
    .prefix(3)
    .scan(0) { acc, _ in acc + 1 }
    .sink { subject.send($0) }
    .store(in: &cancellables)

Publisher → AsyncStream

Si .values no está disponible, cree un pequeño puente mediante AsyncStream. No olvide cancelar la suscripción cuando termine.

import Combine

// Bridge ANY Publisher to AsyncSequence using AsyncStream (simple version)
extension Publisher {
    func asAsyncStream() -> AsyncStream<Output> {
        AsyncStream { continuation in
            let cancel = self.sink(
                receiveCompletion: { _ in continuation.finish() },
                receiveValue: { value in continuation.yield(value) }
            )
            continuation.onTermination = { _ in cancel.cancel() }
        }
    }
}

// Demo:
let pub = [10, 20, 30].publisher
Task {
    var sum = 0
    for await v in pub.asAsyncStream() { sum += v }
    print("sum:", sum) // 60
}

Usar una continuación que lanza errores

Comprobación rápida: ¿Cuál es la mejor forma de convertir un callback que puede fallar en código async?

Repaso

Repaso: Envuelva los callbacks heredados con continuaciones (que lancen errores o no) y consuma Combine mediante .values o un pequeño puente con AsyncStream. Mantenga una sola reanudación y cancele las suscripciones al finalizar.

Preguntas frecuentes

¿La lección «Conectar callbacks heredados/Combine con async/await» es gratis?

Sí — el texto completo de «Conectar callbacks heredados/Combine con async/await» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Swift Academy, actualiza a CoddyKit PRO. El curso de Swift Academy incluye 3 lecciones en total.

¿Qué aprenderé en «Conectar callbacks heredados/Combine con async/await»?

Envuelva los completion handlers con withCheckedContinuation / withCheckedThrowingContinuation y conecte los publishers de Combine con AsyncSequence para usar bucles sencillos de for await . Practicas Swift Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Swift Academy?

No se requiere experiencia previa. Swift Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 3.

¿Cuánto tiempo toma la lección «Conectar callbacks heredados/Combine con async/await»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Swift Academy?

Sí. Cada lección de Swift Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Productor/consumidor, canalizaciones y tiempos de espera
  2. Conectar callbacks heredados/Combine con async/await
  3. Probar código async (XCTest)
← Volver a Swift Academy