Vlad Tkachenko
Ссылка
нажмите — покажем
нажмите — покажем
😄 Когда хочется и новый стек трогать и легаси сильно не шатать
Если коротко, то тут статья, а тут макрос, который сгенерирует async/await обертки над старыми callback-based функциями.
Если чуть подробнее, то главная мысль не переписывать всё сразу, а создать мост между старым API и новым с помощью continuations и Swift-макросов, которые генерируют обёртки автоматически.
Если в вашем проекте сетевой слой и другие корные модули написаны через completion handlers, то полный рефакторинг повлечёт большой объём изменений, нагрузку на тестирование и риски для стабильности.
Используя bridge-подход можно включить async/await, сохранив обратную совместимость, поддержать постепенную миграцию и при этом не тормозить развитие продуктовых фич.
func info(
for id: String,
completion: @escaping (NetworkResponse<Response, BaseErrorResponse>) -> Void
) {
let request = Action.getInfo(id: id)
guard var url = pathProvider.createURL(type: request) else {
completion(.failure(.urlNotFound))
return
}
let endPoint = RequestEndpoint(
url: url,
parameters: request.parameters
)
networkClient.request(endpoint: endPoint, responseHandler: completion)
}
Добавляя withCheckedContinuation мы создаем мост между кодом, основанным на коллбэках, и современным миром асинхронности.
Параметр continuation это наша связь с асинхронным контекстом.
Для предоставления результата continuation.resume() нужно вызвать ровно один раз.
Можно вернуть значение через resume(returning:) или ошибки через resume(throwing:), а также resume(with result: sending Result<T,E>) если вы используется тип Swift Result.
func info(
for id: String
) async -> NetworkResponse<Response, BaseErrorResponse> {
return await withCheckedContinuation { continuation in
info(for: id) { result in
continuation.resume(returning: result)
}
}
}
CheckedContinuation: Включает проверки в рантайме для обнаружения ошибок, если мы возобновляли continuations более одного раза или совсем не возобновляли их.
UnsafeContinuation: Выключает проверки безопасности для лучшей производительности.
Плюсы:
↗️ Минимальные изменения в существующих модулях и обратная совместимость.
↗️ Пошаговая миграция: можно переводить отдельные вызовы/фичи.
↗️ Автоматизация через макросы уменьшает рутину и шанс ошибок.
↗️ Команда может продолжать продуктовую разработку параллельно с миграцией.
Минусы?
↘️ async/await обычно используют с throws, что даёт лаконичную обработку do/try/catch. Если старый нетворкинг возвращает Result<Response, Error>, то простая обёртка async -> Result не даёт преимуществ throws, клиенты всё равно будут делать switch/case или разбирать результат вручную.
↘️ resume нужно вызвать ровно один раз. Смешанная логика (ошибка сети + таймаут + cancellation + повтор попытки) повышает риск двойного resume или забытого resume, а это либо краш, либо подвисший await.
↘️ сам withCheckedContinuation имеет небольшой накладной расход и массовое применение без профилирования может дать неожиданный overhead. UnsafeContinuation быстрее, но теряет проверки.
🐊 Лично мне кажется, что на проекте с большим количеством легаси апи стоит с осторожностью внедрять такого рода кодогенерацию. С первого взгляда кажется удобным, но на деле может только добавить проблем. И было бы неплохо дописать макрос для нескольких реализаций метода с возвратом Result и с обработкой ошибок через throws.
📎 Modern Concurrency and Legacy code
#D #Arch #Legacy #Macros
👏
7 · 232 ·