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

Избавляемся от старья и переходим на SwiftUI

Избавляемся от старья и переходим на SwiftUI

Avatar for Ruslan Kavetsky

Ruslan Kavetsky

June 20, 2022
Tweet

More Decks by Ruslan Kavetsky

Other Decks in Programming

Transcript

  1. Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к

    рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  2. Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к

    рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  3. Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к

    рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  4. SwiftUI Как работает Layout Как работает Data Flow Как работает

    механизм Preference Как работает механизм Environment Чем отличаются Property Wrappers По желанию: как работают анимации * Полезные ссылки будут в конце
  5. Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к

    рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  6. SwiftUI+UIKit Image loading Pull-to-refresh RxSwift+Combine Handling keyboard Pagination Analytics Dark

    mode Colors Image assets Fonts Text processing Architecture Debugging Animations Actions Localization Navigation Profiling
  7. Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к

    рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  8. Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к

    рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  9. #available(iOS 14, *) VIEWS Lazy Stacks, Lazy Grids, TextEditor, ProgressViews,

    Progress Bar, Maps, Opening links, PageTabViewStyle, DatePicker, ColorPicker, Sign In With Apple MODIFIERS redacted(), scrollTo(), onChange(), appStoreOverlay() PROPERTY WRAPPERS StateObject, ScaledMetric, AppStorage iOS 14
  10. Список поддерживаемых устройств у iOS 13 и iOS 14 одинаковый,

    каждый может обновиться при желании #available(iOS 14, *)
  11. Выбрали Nuke + Уже используется в проекте + Проверен временем

    + Общий кэш с UIKit (потому что там тоже Nuke) – Нет стабильной версии Написали враппер, чтобы можно было легко заменить на другой компонент Image loading
  12. Pull-to-refresh Взяли основу из статьи https://swiftui-lab.com/scrollview- pull-to-refresh Выглядит не нативно

    и иногда дёргается В iOS 15 появился Refreshable модификатор, но только для List !
  13. "

  14. Каждая вьюшка это композиция других вьюх У каждой вьюшки есть

    модель - композиция примитивных типов и моделей других вьюшек У экрана есть View Model, которая загружает данные и реагирует на действия
  15. Плейсхолдеры для состояний загрузки это просто Placeholders ≠ Stubs Превью

    для разных состояний сильно помогают в процессе разработки
  16. Views (SwiftUI) Models (structs) Screens (SwiftUI) View Models (protocols) View

    Models (classes) Network / Services (classes) Navigation (Router) View Controllers (UIKit) Views (UIKit)
  17. SwiftUI Playground target Agora App target Views (SwiftUI) Models (structs)

    Screens (SwiftUI) View Models (protocols) View Models (classes) Network / Services (classes) Navigation (Router) View Controllers (UIKit) Views (UIKit)
  18. Views (SwiftUI) Models (structs) Screens (SwiftUI) View Models (protocols) View

    Models (classes) Network / Services (classes) Navigation (Router) View Controllers (UIKit) Views (UIKit) Introspect PreviewDevice Firebase AWS Amplitude Branch Stripe Datadog NukeUI Nuke SnapKit RxSwift RxCombine SVProgressHUD MarkdownUI
  19. Навигация на уровне UIKit, вьюшки на уровне SwiftUI Screen =

    View Controller SwiftUI живёт в отдельном таргете с минимумом зависимостей Все зависимости в основном таргете
  20. Analytics DatadogSDK.swift extension SwiftUI.View { func trackRUMView(name: String) #$ some

    View {##%} func trackRUMTapAction(name: String) #$ some View {##%} }
  21. import SwiftUI #& Do nothing for playground extension View {

    func datadogName(_ name: String) #$ some View { return self } func datadogActionName(_ name: String) #$ some View { return self } } import Datadog import SwiftUI extension View { func datadogName(_ name: String) #$ some View { trackRUMView(name: name) } func datadogActionName(_ name: String) #$ some View { trackRUMTapAction(name: name) } } Analytics hack View+Datadog.swift View+Datadog+Playground.swift
  22. protocol CommunityScreenViewModelProtocol: ObservableObject { var model: CommunityScreen<Self>.Model? { get }

    func load() func tapUser(id: Int) func tapPost(id: Int) func tapPlaylist(id: Int) func followUser(id: Int) } protocol CommunityScreenViewModelProtocol: ObservableObject { var model: CommunityScreen<Self>.Model? { get } func load() func action(action: CommunityScreen<Self>.Action) } struct CommunityScreen<ViewModel: CommunityScreenViewModelProtocol#' View { @ObservedObject var viewModel: ViewModel enum Action { case tapUser(id: Int) case tapPost(id: Int) case tapPlaylist(id: Int) case followUser(id: Int) } ##% } struct Model { let playlists: PlaylistsView.Model let followers: FollowersView.Model let posts: PostsView.Model enum Action { case tapUser(id: Int) case tapPost(id: Int) case tapPlaylist(id: Int) case followUser(id: Int) } var onAction: (Action) #$ Void } struct Model: Identifiable { let id: Int let imageUrl: URL let username: String let description: String let onUserTap: (Int) #$ Void let onUserFollow: (Int) #$ Void }
  23. Screen View Model Id Id struct UserView.Model { let id:

    Int let imageUrl: URL … } Json/Dto Id
  24. Screen View Model Id Name AnalyticsId Category Id Name AnalyticsId

    Category struct UserView.Model { let id: Int let id: imageUrl let name: String ... let name: String let analyticsId: Int let category: String }
  25. Screen View Model Id Name AnalyticsId Category Id Name AnalyticsId

    Category Placeholders? Stubs? struct UserView.Model { let id: Int let id: imageUrl let name: String ... let name: String let analyticsId: Int let category: String }
  26. Screen View Model Id Name AnalyticsId Category Id Name AnalyticsId

    Category struct UserView.Model { let id: Int let id: imageUrl let name: String ... let name: String let analyticsId: Int let category: String }
  27. Screen View Model Json/Dto Json/Dto struct UserView.Model { let id:

    Int let name: String ... let data: UserJson }
  28. Screen View Model Json/Dto Json/Dto struct UserView.Model { let id:

    Int let name: String ... let data: UserJson } UserJson {} +1 зависимость Network/Service struct UserView.Model { let id: Int let name: String ... let data: UserJson }
  29. Screen View Model Json/Dto Json/Dto struct UserView.Model { let id:

    Int let name: String ... let data: UserJson } UserJson {} +1 зависимость Network/Service struct UserView.Model { let id: Int let name: String ... let data: UserJson }
  30. struct Model: Identifiable { let id: Int let imageUrl: URL

    let username: String let description: String let onUserTap: () #$ Void let onUserFollow: () #$ Void } struct Model: Identifiable { let id: Int let imageUrl: URL let username: String let description: String enum Action { case tap case follow } let action: (Action) #$ Void } Actions in view
  31. Actions in view model struct UserDataTransformer { let onUserTap: (UserJson)

    #$ Void let onUserFollow: (UserJson) #$ Void func transform<VM>(json: CommunityScreenJson) #$ CommunityScreen<VM>.Model { return CommunityScreen.Model( playlists: ##%, followers: FollowersView.Model( header: json.followersHeader, users: json.followers.map { user in UserView.Model( id: user.id, imageUrl: user.imageUrl.url, username: user.username, description: user.description, onTap: { self.onUserTap(user) }, onFollow: { self.onUserFollow(user) } ) } ), posts: ##% )
  32. final class CommunityScreenViewModel: CommunityScreenViewModelProtocol { let network: Network let router:

    Router @Published var model: CommunityScreen<CommunityScreenViewModel>.Model? = nil func load() { network.loadCommunity { json in self.model = UserDataTransformer( onUserTap: { user in self.router.openUserScreen(id: user.id) }, onUserFollow: { user in self.network.followUser(id: user.id) } ) .transform(json: json) } } } Actions in view model
  33. Вьюшки определяет свой набор actions внутри модели Вью модель замыкает

    json/dto внутри action Пропадает необходимость прокидывать эти данные во вью и обратно
  34. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили

    SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect
  35. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили

    SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect
  36. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили

    SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect
  37. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили

    SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect
  38. GeometryReader - самое крайнее средство для решения проблемы Мозг переключается

    не сразу, ему нужно время Не боятся создавать разные View для похожих элементов Лучше использовать 2 пробела вместо 4-х и не писать self На чипах apple silicon работает лучше
  39. Не всегда плавный скроллинг Некоторые API доступны только с iOS

    15 Какие-то вещи сделать просто невозможно
  40. available(iOS 15, *) import Introspect content .introspectTextField { textView in

    textView.addTarget( textFieldTarget, action: #selector(TextFieldTarget.start), for: .editingDidBegin ) textView.addTarget( textFieldTarget, action: #selector(TextFieldTarget.finish), for: .editingDidEnd ) } @FocusState private var isFocused: Bool @FocusState // iOS 14 // iOS 15 https://github.com/siteline/SwiftUI-Introspect
  41. available(iOS 15, *) import MarkdownUI if #available(iOS 15.0, *) {

    Text(AttributedString(markdown: model.description)) } else { Markdown(model.description) } AttributedString(markdown:)
  42. Не всегда плавный скроллинг Некоторый функционал не доступен на iOS

    14 Какие-то вещи сделать просто невозможно https://github.com/siteline/SwiftUI-Introspect
  43. After using UIKit for a very long time, it can

    be a bit confusing to start implementing UIs with SwiftUI. At first, you are not sure which element or modifier to use for certain items. However, this feeling goes away very quickly and you start to understand the simplicity and the ease with which you are able to put building blocks together to achieve the desired outcome. It’s just a different way of looking at structure of UI and understanding how certain core concepts work.
  44. Для меня SwiftUI стал глотĸом свежого воздуха и разнообразия в

    работе, первое время реально тяжело, но голова быстро перестраивается. Иногда я сĸучаю по UIKit и его гибĸости, ĸоторая поĸа превосходит SwiftUI, но ĸажется это будет недолго.
  45. App SwiftUI Чистый билд ~20 сек Hot reload $ Чистый

    билд ~2 мин Screens Views Models View Models Services Network Analytics Navigation View Controllers Views + 3rd party SDKs
  46. https://developer.apple.com/tutorials/swiftui - туториалы от эпл https://swiftui-lab.com - глубокие статьи про

    сложные штуки https://www.fivestars.blog/swiftui - очень хорошо https://swiftwithmajid.com - не только SwiftUI, кратко и по делу