Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Артем Новичков (Skyeng) — Как подружить свой ко...

Артем Новичков (Skyeng) — Как подружить свой код с async/await

Ozon Tech

June 02, 2022
Tweet

More Decks by Ozon Tech

Other Decks in Technology

Transcript

  1. Про себя • В iOS-разработке с 2014 года ; •

    Работаю в Skyeng и Нетологии ; • Публикую интересности на Github, пишу статьи.
  2. 6

  3. // on BookDetailsService
 func book(withID id: Int, callback: CallbackResult<DetailBookResponse>) {

    let request = Request<DetailBookResponse>(/* ... */ ) self.perform(request, callback: callback ) } 7
  4. // on BookDetailsService
 func book(withID id: Int, callback: CallbackResult<DetailBookResponse>) {

    let request = Request<DetailBookResponse>(/* ... */ ) self.perform(request, callback: callback ) } 8 // on BookDetailsService
 func book(withID id: Int) async throws -> DetailBookResponse { let request = Request<DetailBookResponse>(/* ... */ ) return await try self.perform(request ) } ↓
  5. // on BookDetailsService
 func book(withID id: Int, callback: CallbackResult<DetailBookResponse>) {

    let request = Request<DetailBookResponse>(/* ... */ ) self.perform(request, callback: callback ) } 9 // on BookDetailsService
 func book(withID id: Int) async throws -> DetailBookResponse { let request = Request<DetailBookResponse>(/* ... */ ) return await try self.perform(request ) } ↓
  6. // on BookDetailsService
 func book(withID id: Int, callback: CallbackResult<DetailBookResponse>) {

    let request = Request<DetailBookResponse>(/* ... */ ) self.perform(request, callback: callback ) } 10 // on BookDetailsService
 func book(withID id: Int) async throws -> DetailBookResponse { let request = Request<DetailBookResponse>(/* ... */ ) return await try self.perform(request ) } ↓
  7. // on BookDetailsService
 func book(withID id: Int, callback: CallbackResult<DetailBookResponse>) {

    let request = Request<DetailBookResponse>(/* ... */ ) self.perform(request, callback: callback ) } 11 // on BookDetailsService
 func book(withID id: Int) async throws -> DetailBookResponse { let request = Request<DetailBookResponse>(/* ... */ ) return await try self.perform(request ) } ↓
  8. // on BookDetailsService
 func book(withID id: Int, callback: CallbackResult<DetailBookResponse>) {

    let request = Request<DetailBookResponse>(/* ... */ ) self.perform(request, callback: callback ) } 12 // on BookDetailsService
 func book(withID id: Int) async throws -> DetailBookResponse { let request = Request<DetailBookResponse>(/* ... */ ) return await try self.perform(request ) } ↓
  9. // on RequestManager
 func perform<T>(_ request: Request<T>) async throws ->

    T { await try withCheckedThrowingContinuation { continuation in self.perform(request, callback: .immediate { result in continuation.resume(with: result ) } ) } } 13
  10. Continuation • Должен вызвать resume() один ра з • Не

    создаётся напряму ю • Сообщает о неправильном использовании 16
  11. 17

  12. 19 // on BookDetailsPresenter
 self.booksService.book(withID: 42, callback: .onMain { [weak

    self] result in do { let response = try result.get( ) // Обновляем U I } catch { // Обновляем U I } })
  13. Как раньше? • Использовать weak self при необходимост и •

    guard let self = self else { return } • Использовать DispatchQueue.main при обновлении интерфейс а • Callback hell 20
  14. 21 // on BookDetailsPresenter
 Task { do { let response

    = await try booksService.book(withID: 42 ) // Обновляем U I } catch { // Обновляем U I } }
  15. Task • Выполняет замыкание в асинхронном контексте, компилятор понимает, какой

    код безопасно (или не безопасно) писать в замыкании ; • Выполняется независимо от того, держите ли вы ссылку на него ; • Можно/нужно отменять. 22
  16. Галя, у нас отмена! 24 // on BookDetailsPresente r private

    var bookTask: Task<Void, Error> ? func viewDidLoad() { bookTask = Task { let response = await try booksService.book(withID: 42 ) // Обновляем UI } } func viewWillDisappear() { bookTask?.cancel( ) }
  17. SwiftUI 25 struct ContentView: View { var body: some View

    { Text("iOS 15 available" ) .task { /* ... */ } } }
  18. SwiftUI 26 struct ModalView: View { @StateObject var viewModel: ModalViewModel

    var body: some View { Text("iOS 13 available" ) .onAppear { viewModel.appear( ) } .onDisappear { viewModel.dissappear( ) } } }
  19. 27 fi nal class BookDetailsViewModel: ObservableObject { private var bookTask:

    Task<Void, Error> ? func appear() { bookTask = Task { /* ... */ } } func dissappear() { bookTask?.cancel( ) } }
  20. 28 @MainActor class UIView: UIResponde r @MainActor protocol BookDetailView {

    func setTitle(_ title: String ) } // on BookDetailsPresenter
 await MainActor.run { view?.setTitle(response.data.name) }
  21. 29 // on BookDetailsPresenter
 Task { do { let response

    = await try booksService.book(withID: 42 ) // Обновляем U I await view?.setTitle(response.data.name ) } catch { // Обновляем U I await MainActor.run { handle(error) } } }
  22. Actors • Потокобезопасный ти п • Проверка на этапе компиляци

    и • Global Actors (например, @MainActor ) • Distributed Actors 30
  23. Выводы • Синтаксический сахар безопасный код (проверки на этапе компиляции,

    меньше замыканий, обработка ошибок ) • Есть поддержка для системных API 33
  24. Выводы • Синтаксический сахар безопасный код (проверки на этапе компиляции,

    меньше замыканий, обработка ошибок ) • Есть поддержка для системных AP I • Внутренние оптимизации (cooperative thread pool) 34
  25. 35 Task { let noti fi cationCenter = Noti fi

    cationCenter.default let rects = noti fi cationCenter.noti fi cations(named: UIResponder.keyboardWillShowNoti fi cation ) .compactMap { noti fi cation -> CGRect? in let value = noti fi cation.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue return value?.cgRectValue } for await rect in rects { // Update the layout with keyboard rect print(rect ) } }