SlideShare a Scribd company logo
Не про SwiftUI, а про наш опыт с ним
Просто хотелось нового
!a# $% &o(o)*+*,-
Обсудили идею внутри команды
Посоветовались с бизнесом и подготовили к рискам
Изучили базовые основы SwiftUI на упражнениях
Посмотрели что есть в нашем приложении и как мы будем
решать это на SwiftUI
Разделили между собой задачи на изучение
Засинкались и составили базу знаний
!a# $% &o(o)*+*,-
Обсудили идею внутри команды
Посоветовались с бизнесом и подготовили к рискам
Изучили базовые основы SwiftUI на упражнениях
Посмотрели что есть в нашем приложении и как мы будем
решать это на SwiftUI
Разделили между собой задачи на изучение
Засинкались и составили базу знаний
!a# $% &o(o)*+*,-
Обсудили идею внутри команды
Посоветовались с бизнесом и подготовили к рискам
Изучили базовые основы SwiftUI на упражнениях
Посмотрели что есть в нашем приложении и как мы будем
решать это на SwiftUI
Разделили между собой задачи на изучение
Засинкались и составили базу знаний
.(o /a0o)o&o ,(o*( 01a(- ‫פ‬3o SwiftUI
Как работает Layout
Как работает Data Flow
Как работает механизм Preference
Как работает механизм Environment
Чем отличаются Property Wrappers
По желанию: как работают анимации
* Полезные ссылки будут в конце
!a# $% &o(o)*+*,-
Обсудили идею внутри команды
Посоветовались с бизнесом и подготовили к рискам
Изучили базовые основы SwiftUI на упражнениях
Посмотрели что есть в нашем приложении и как мы будем
решать это на SwiftUI
Разделили между собой задачи на изучение
Засинкались и составили базу знаний
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
!a# $% &o(o)*+*,-
Обсудили идею внутри команды
Посоветовались с бизнесом и подготовили к рискам
Изучили базовые основы SwiftUI на упражнениях
Посмотрели что есть в нашем приложении и как мы будем
решать это на SwiftUI
Разделили между собой задачи на изучение
Засинкались и составили базу знаний
!a# $% &o(o)*+*,-
Обсудили идею внутри команды
Посоветовались с бизнесом и подготовили к рискам
Изучили базовые основы SwiftUI на упражнениях
Посмотрели что есть в нашем приложении и как мы будем
решать это на SwiftUI
Разделили между собой задачи на изучение
Засинкались и составили базу знаний
#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
https://mixpanel.com/trends/#report/ios_15
Список поддерживаемых устройств у iOS 13 и iOS 14
одинаковый, каждый может обновиться при желании
#available(iOS 14, *)
Image loading
https://github.com/kean/NukeUI
https://github.com/cmtrounce/SwURL
https://github.com/geekaurora/SwiftWebImage
https://github.com/dmytro-anokhin/url-image
https://github.com/SDWebImage/SDWebImageSwiftUI
Выбрали Nuke
+ Уже используется в проекте
+ Проверен временем
+ Общий кэш с UIKit (потому что там тоже Nuke)
– Нет стабильной версии
Написали враппер, чтобы можно было легко заменить на
другой компонент
Image loading
iOS 15
AsyncImage
iOS 15
AsyncImage
Pull-to-refresh
Взяли основу из статьи https://swiftui-lab.com/scrollview-
pull-to-refresh
Выглядит не нативно и иногда дёргается
В iOS 15 появился Refreshable модификатор, но только
для List !
RxSwift + Combine
RxSwift + Combine
https://github.com/CombineCommunity
RxSwift + Combine
RxSwift → Combine
Combine → RxSwift
UIKit + SwiftUI
UIKit → SwiftUI
SwiftUI → UIKit
https://developer.apple.com/videos/play/wwdc2022/10072/
Use SwiftUI with UIKit
NEW
UIHostingConfiguration
Keyboard handling
Dark mode
И у картинок тоже так можно
Можно сгенерировать, можно написать
Dark mode
A1a+*(*#a
A1a+*(*#a
A35*(6#(u3a
A1a+*(*#a
A35*(6#(u3a
8a9) #o:*1& "
# LIVE
Каждая вьюшка это композиция других вьюх
У каждой вьюшки есть модель - композиция
примитивных типов и моделей других вьюшек
У экрана есть View Model, которая загружает данные
и реагирует на действия
ViewModel – протокол, у которого может быть
множество реализаций
Плейсхолдеры для состояний загрузки это просто
Placeholders ≠ Stubs
Превью для разных состояний сильно помогают в
процессе разработки
SwiftUI
Screen = View Controller
Использовать напрямую с UIHostingController
Обернуть в подкласс UIViewController
Views (SwiftUI)
Models (structs)
Screens (SwiftUI)
View Models (protocols)
View Models (classes)
Network / Services (classes)
Navigation (Router)
View Controllers (UIKit)
Views (UIKit)
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)
SwiftUI files
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
Навигация на уровне UIKit, вьюшки на уровне SwiftUI
Screen = View Controller
SwiftUI живёт в отдельном таргете с минимумом
зависимостей
Все зависимости в основном таргете
Analytics
DatadogSDK.swift
extension SwiftUI.View {

func trackRUMView(name: String) #$ some View {##%}

func trackRUMTapAction(name: String) #$ some View {##%}

}
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
Screen
View Model
@Published model
Actions
tap()
follow()
buy()
select()
etc
# LIVE
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

}
Screen
View Model
Id Id
struct UserView.Model {
let id: Int
let imageUrl: URL
…
}
Json/Dto Id
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
}
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
}
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
}
Screen
View Model
Json/Dto Json/Dto
struct UserView.Model {
let id: Int
let name: String
...
let data: UserJson
}
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
}
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
}
# LIVE
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
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: ##%

)
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
Вьюшки определяет свой набор actions внутри
модели
Вью модель замыкает json/dto внутри action
Пропадает необходимость прокидывать эти данные
во вью и обратно
Не всё конечно же было гладко
Не работал canvas preview: все библиотеки должны
поддерживать arm64, внедрили SPM
Иногда лэйаут на экране просто ломается: помогает установка
фиксированного фрейма
Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid
Не доступны все опции привычной кастомизации: https://
github.com/siteline/SwiftUI-Introspect
; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
SPM sucks
SPM sucks
Не работал canvas preview: все библиотеки должны
поддерживать arm64, внедрили SPM
Иногда лэйаут на экране просто ломается: помогает установка
фиксированного фрейма
Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid
Не доступны все опции привычной кастомизации: https://
github.com/siteline/SwiftUI-Introspect
; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
Fixed frame to fix screen layout
Не работал canvas preview: все библиотеки должны
поддерживать arm64, внедрили SPM
Иногда лэйаут на экране просто ломается: помогает установка
фиксированного фрейма
Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid
Не доступны все опции привычной кастомизации: https://
github.com/siteline/SwiftUI-Introspect
; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
Не работал canvas preview: все библиотеки должны
поддерживать arm64, внедрили SPM
Иногда лэйаут на экране просто ломается: помогает установка
фиксированного фрейма
Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid
Не доступны все опции привычной кастомизации: https://
github.com/siteline/SwiftUI-Introspect
; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
https://github.com/siteline/SwiftUI-Introspect
GeometryReader - самое крайнее средство для решения
проблемы
Мозг переключается не сразу, ему нужно время
Не боятся создавать разные View для похожих элементов
Лучше использовать 2 пробела вместо 4-х и не писать self
На чипах apple silicon работает лучше
.(o u01a+*
=o0& ‫פ‬636#+>?a6(,@ 16 ,3a0u
=o0& ‫פ‬636#+>?a6(,@ 16 ,3a0u
Custom formatting rules for SwiftUI files
2 ‫פ‬3o/6+a * /60 self
!a#*6 ‫פ‬3o/+6$% o,(a+*,-
Не всегда плавный скроллинг
Некоторый функционал не доступен на iOS 14
Какие-то вещи сделать просто невозможно
!a#*6 ‫פ‬3o/+6$% o,(a+*,-
Не всегда плавный скроллинг
Некоторые API доступны только с iOS 15
Какие-то вещи сделать просто невозможно
available(iOS 15, *)
@FocusState
AttributedString(markdown:)

safeAreaInset()

LazyVGrid(pinnedViews: .sectionHeaders)
iOS 15
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
available(iOS 15, *)
import MarkdownUI
if #available(iOS 15.0, *) {

Text(AttributedString(markdown: model.description))

} else {

Markdown(model.description)

}
AttributedString(markdown:)
available(iOS 15, *)
https://www.fivestars.blog/articles/safe-area-insets/
safeAreaInset()
LazyVGrid(pinnedViews: .sectionHeaders)
available(iOS 15, *)
!a#*6 ‫פ‬3o/+6$% o,(a+*,-
Не всегда плавный скроллинг
Некоторый функционал не доступен на iOS 14
Какие-то вещи сделать просто невозможно
https://github.com/siteline/SwiftUI-Introspect
!a#*6 360u+-(a(% ‫פ‬o+u?*+*
31 экран за 9 месяцев
!a#*6 360u+-(a(% ‫פ‬o+u?*+*
Радость
Скорость
Простота
Aa:o,(-
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.
Aa:o,(-
Для меня SwiftUI стал глотĸом свежого воздуха и
разнообразия в работе, первое время реально
тяжело, но голова быстро перестраивается.
Иногда я сĸучаю по UIKit и его гибĸости, ĸоторая
поĸа превосходит SwiftUI, но ĸажется это будет
недолго.
21 июня, вт
10:00 – 11:00
Егор Петров
SwiftUI vs UIKit: to be or not to be?
;#o3o,(-
App
SwiftUI
Чистый билд ~20 сек
Hot reload $
Чистый билд ~2 мин
Screens
Views
Models
View Models
Services
Network
Analytics
Navigation
View Controllers
Views
+
3rd party SDKs
B3o,(o(a
Перевести сервисы на Combine async/await
Перевести все экраны на SwiftUI
Перевести навигацию на SwiftUI
.(o :a+-<6
iOS 16
https://developer.apple.com/videos/all-videos/?q=swiftui
;,%+#*
https://developer.apple.com/tutorials/swiftui - туториалы от эпл
https://swiftui-lab.com - глубокие статьи про сложные штуки
https://www.fivestars.blog/swiftui - очень хорошо
https://swiftwithmajid.com - не только SwiftUI, кратко и по делу
;,%+#*
;,%+#*
Тоже что-то есть:
https://sarunw.com/tags/swiftui
https://www.swiftbysundell.com/tags/swiftui
https://serialcoder.dev/category/text-tutorials/swiftui
;,%+#*
Stanford SwiftUI course: https://cs193p.sites.stanford.edu
FAQ: https://fuckingswiftui.com
Improved SwiftUI docs: https://swiftontap.com
⭐ SwiftUI Layout System: https://kean.blog/post/swiftui-layout-system
Objective-С
UIKit
Interface Builder
Вопросы, предложения, комментарии @pycuk
→

More Related Content

Similar to Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий (Agora)

Mobile automation uamobile
Mobile automation uamobileMobile automation uamobile
Mobile automation uamobileUA Mobile
 
GraphQL API: Patterns | Андрей Чиж | Zlit Tech
GraphQL API: Patterns | Андрей Чиж | Zlit TechGraphQL API: Patterns | Андрей Чиж | Zlit Tech
GraphQL API: Patterns | Андрей Чиж | Zlit Tech
Zlit
 
Rich UI on Dojo Toolkit and Zend Framework
Rich UI on Dojo Toolkit and Zend FrameworkRich UI on Dojo Toolkit and Zend Framework
Rich UI on Dojo Toolkit and Zend Framework
Georgy Turevich
 
jQuery как путь к RIA
jQuery как путь к RIAjQuery как путь к RIA
jQuery как путь к RIA
GetDev.NET
 
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
Mail.ru Group
 
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
IT Event
 
Android: Как создать свое первое приложение?
Android: Как создать свое первое приложение?Android: Как создать свое первое приложение?
Android: Как создать свое первое приложение?Kuban Dzhakipov
 
Remote (dev)tools своими руками
Remote (dev)tools своими рукамиRemote (dev)tools своими руками
Remote (dev)tools своими руками
Roman Dvornov
 
Online TechTalk “Flutter Mobile Development”
Online TechTalk “Flutter Mobile Development”Online TechTalk “Flutter Mobile Development”
Online TechTalk “Flutter Mobile Development”
GlobalLogic Ukraine
 
Особенности тестирования мобильных приложений (Android, iOS)
Особенности тестирования мобильных приложений (Android, iOS)Особенности тестирования мобильных приложений (Android, iOS)
Особенности тестирования мобильных приложений (Android, iOS)
Эльвина Сакаева
 
Netbeans Desktop Applications
Netbeans Desktop ApplicationsNetbeans Desktop Applications
Netbeans Desktop Applications
scassau
 
Антон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в androidАнтон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в android
DataArt
 
О тестирование софта: мир качества, жуков и информации.
О тестирование софта: мир качества, жуков и информации.О тестирование софта: мир качества, жуков и информации.
О тестирование софта: мир качества, жуков и информации.
Sergey Atroschenkov
 
О тестирование софта: мир качества, жуков и информации. Атрощенков Сергей.
О тестирование софта: мир качества, жуков и информации.   Атрощенков Сергей.О тестирование софта: мир качества, жуков и информации.   Атрощенков Сергей.
О тестирование софта: мир качества, жуков и информации. Атрощенков Сергей.
IT-Доминанта
 
Bada
BadaBada
Bada
annakysil
 
NetBeans 6.0 Desktop
NetBeans 6.0 DesktopNetBeans 6.0 Desktop
NetBeans 6.0 Desktop
Iljas
 
End-2-End UI автоматизация в мобильном приложении. Наша реализация
End-2-End UI автоматизация в мобильном приложении. Наша реализацияEnd-2-End UI автоматизация в мобильном приложении. Наша реализация
End-2-End UI автоматизация в мобильном приложении. Наша реализация
SQALab
 
CodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлению
CodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлениюCodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлению
CodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлениюCodeFest
 
Интуит. Разработка приложений для iOS. Лекция 3. Views
Интуит. Разработка приложений для iOS. Лекция 3. ViewsИнтуит. Разработка приложений для iOS. Лекция 3. Views
Интуит. Разработка приложений для iOS. Лекция 3. ViewsГлеб Тарасов
 
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOSКурсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOSГлеб Тарасов
 

Similar to Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий (Agora) (20)

Mobile automation uamobile
Mobile automation uamobileMobile automation uamobile
Mobile automation uamobile
 
GraphQL API: Patterns | Андрей Чиж | Zlit Tech
GraphQL API: Patterns | Андрей Чиж | Zlit TechGraphQL API: Patterns | Андрей Чиж | Zlit Tech
GraphQL API: Patterns | Андрей Чиж | Zlit Tech
 
Rich UI on Dojo Toolkit and Zend Framework
Rich UI on Dojo Toolkit and Zend FrameworkRich UI on Dojo Toolkit and Zend Framework
Rich UI on Dojo Toolkit and Zend Framework
 
jQuery как путь к RIA
jQuery как путь к RIAjQuery как путь к RIA
jQuery как путь к RIA
 
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
 
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
 
Android: Как создать свое первое приложение?
Android: Как создать свое первое приложение?Android: Как создать свое первое приложение?
Android: Как создать свое первое приложение?
 
Remote (dev)tools своими руками
Remote (dev)tools своими рукамиRemote (dev)tools своими руками
Remote (dev)tools своими руками
 
Online TechTalk “Flutter Mobile Development”
Online TechTalk “Flutter Mobile Development”Online TechTalk “Flutter Mobile Development”
Online TechTalk “Flutter Mobile Development”
 
Особенности тестирования мобильных приложений (Android, iOS)
Особенности тестирования мобильных приложений (Android, iOS)Особенности тестирования мобильных приложений (Android, iOS)
Особенности тестирования мобильных приложений (Android, iOS)
 
Netbeans Desktop Applications
Netbeans Desktop ApplicationsNetbeans Desktop Applications
Netbeans Desktop Applications
 
Антон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в androidАнтон Валюх - Использование паттерна Mvvm в android
Антон Валюх - Использование паттерна Mvvm в android
 
О тестирование софта: мир качества, жуков и информации.
О тестирование софта: мир качества, жуков и информации.О тестирование софта: мир качества, жуков и информации.
О тестирование софта: мир качества, жуков и информации.
 
О тестирование софта: мир качества, жуков и информации. Атрощенков Сергей.
О тестирование софта: мир качества, жуков и информации.   Атрощенков Сергей.О тестирование софта: мир качества, жуков и информации.   Атрощенков Сергей.
О тестирование софта: мир качества, жуков и информации. Атрощенков Сергей.
 
Bada
BadaBada
Bada
 
NetBeans 6.0 Desktop
NetBeans 6.0 DesktopNetBeans 6.0 Desktop
NetBeans 6.0 Desktop
 
End-2-End UI автоматизация в мобильном приложении. Наша реализация
End-2-End UI автоматизация в мобильном приложении. Наша реализацияEnd-2-End UI автоматизация в мобильном приложении. Наша реализация
End-2-End UI автоматизация в мобильном приложении. Наша реализация
 
CodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлению
CodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлениюCodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлению
CodeFest 2013. Родионов А. — От Selenium к Watir — путь к просветлению
 
Интуит. Разработка приложений для iOS. Лекция 3. Views
Интуит. Разработка приложений для iOS. Лекция 3. ViewsИнтуит. Разработка приложений для iOS. Лекция 3. Views
Интуит. Разработка приложений для iOS. Лекция 3. Views
 
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOSКурсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
Курсы по мобильной разработке. 2 лекция. Построение интерфейсов в iOS
 

Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий (Agora)

  • 1.
  • 2.
  • 3. Не про SwiftUI, а про наш опыт с ним
  • 4.
  • 5.
  • 7. !a# $% &o(o)*+*,- Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  • 8. !a# $% &o(o)*+*,- Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  • 9. !a# $% &o(o)*+*,- Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  • 10. .(o /a0o)o&o ,(o*( 01a(- ‫פ‬3o SwiftUI Как работает Layout Как работает Data Flow Как работает механизм Preference Как работает механизм Environment Чем отличаются Property Wrappers По желанию: как работают анимации * Полезные ссылки будут в конце
  • 11. !a# $% &o(o)*+*,- Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  • 12. 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
  • 13. !a# $% &o(o)*+*,- Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  • 14. !a# $% &o(o)*+*,- Обсудили идею внутри команды Посоветовались с бизнесом и подготовили к рискам Изучили базовые основы SwiftUI на упражнениях Посмотрели что есть в нашем приложении и как мы будем решать это на SwiftUI Разделили между собой задачи на изучение Засинкались и составили базу знаний
  • 15.
  • 16. #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
  • 18. Список поддерживаемых устройств у iOS 13 и iOS 14 одинаковый, каждый может обновиться при желании #available(iOS 14, *)
  • 20. Выбрали Nuke + Уже используется в проекте + Проверен временем + Общий кэш с UIKit (потому что там тоже Nuke) – Нет стабильной версии Написали враппер, чтобы можно было легко заменить на другой компонент Image loading
  • 23. Pull-to-refresh Взяли основу из статьи https://swiftui-lab.com/scrollview- pull-to-refresh Выглядит не нативно и иногда дёргается В iOS 15 появился Refreshable модификатор, но только для List !
  • 26. RxSwift + Combine RxSwift → Combine Combine → RxSwift
  • 27. UIKit + SwiftUI UIKit → SwiftUI SwiftUI → UIKit
  • 30. Dark mode И у картинок тоже так можно Можно сгенерировать, можно написать
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 41. Каждая вьюшка это композиция других вьюх У каждой вьюшки есть модель - композиция примитивных типов и моделей других вьюшек У экрана есть View Model, которая загружает данные и реагирует на действия
  • 42. ViewModel – протокол, у которого может быть множество реализаций
  • 43. Плейсхолдеры для состояний загрузки это просто Placeholders ≠ Stubs Превью для разных состояний сильно помогают в процессе разработки
  • 44.
  • 45.
  • 46.
  • 48. Screen = View Controller Использовать напрямую с UIHostingController Обернуть в подкласс UIViewController
  • 49. Views (SwiftUI) Models (structs) Screens (SwiftUI) View Models (protocols) View Models (classes) Network / Services (classes) Navigation (Router) View Controllers (UIKit) Views (UIKit)
  • 50. 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)
  • 52. 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
  • 53. Навигация на уровне UIKit, вьюшки на уровне SwiftUI Screen = View Controller SwiftUI живёт в отдельном таргете с минимумом зависимостей Все зависимости в основном таргете
  • 54. Analytics DatadogSDK.swift extension SwiftUI.View { func trackRUMView(name: String) #$ some View {##%} func trackRUMTapAction(name: String) #$ some View {##%} }
  • 55. 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
  • 58. 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 }
  • 59. Screen View Model Id Id struct UserView.Model { let id: Int let imageUrl: URL … } Json/Dto Id
  • 60. 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 }
  • 61. 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 }
  • 62. 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 }
  • 63. Screen View Model Json/Dto Json/Dto struct UserView.Model { let id: Int let name: String ... let data: UserJson }
  • 64. 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 }
  • 65. 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 }
  • 67. 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
  • 68. 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: ##% )
  • 69. 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
  • 70. Вьюшки определяет свой набор actions внутри модели Вью модель замыкает json/dto внутри action Пропадает необходимость прокидывать эти данные во вью и обратно
  • 71. Не всё конечно же было гладко
  • 72. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect ; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
  • 75. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect ; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
  • 76. Fixed frame to fix screen layout
  • 77. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect ; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
  • 78. Не работал canvas preview: все библиотеки должны поддерживать arm64, внедрили SPM Иногда лэйаут на экране просто ломается: помогает установка фиксированного фрейма Не работал pull-to-refresh: заменили (Lazy)VStack на LazyVGrid Не доступны все опции привычной кастомизации: https:// github.com/siteline/SwiftUI-Introspect ; #a#*$* ‫פ‬3o/+6$a$* ,(o+#1u+*,- * #a# *5 36<*+*
  • 80. GeometryReader - самое крайнее средство для решения проблемы Мозг переключается не сразу, ему нужно время Не боятся создавать разные View для похожих элементов Лучше использовать 2 пробела вместо 4-х и не писать self На чипах apple silicon работает лучше .(o u01a+*
  • 83. Custom formatting rules for SwiftUI files 2 ‫פ‬3o/6+a * /60 self
  • 84. !a#*6 ‫פ‬3o/+6$% o,(a+*,- Не всегда плавный скроллинг Некоторый функционал не доступен на iOS 14 Какие-то вещи сделать просто невозможно
  • 85. !a#*6 ‫פ‬3o/+6$% o,(a+*,- Не всегда плавный скроллинг Некоторые API доступны только с iOS 15 Какие-то вещи сделать просто невозможно
  • 87. 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
  • 88. available(iOS 15, *) import MarkdownUI if #available(iOS 15.0, *) { Text(AttributedString(markdown: model.description)) } else { Markdown(model.description) } AttributedString(markdown:)
  • 91. !a#*6 ‫פ‬3o/+6$% o,(a+*,- Не всегда плавный скроллинг Некоторый функционал не доступен на iOS 14 Какие-то вещи сделать просто невозможно https://github.com/siteline/SwiftUI-Introspect
  • 92. !a#*6 360u+-(a(% ‫פ‬o+u?*+* 31 экран за 9 месяцев
  • 94. Aa:o,(- 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.
  • 95. Aa:o,(- Для меня SwiftUI стал глотĸом свежого воздуха и разнообразия в работе, первое время реально тяжело, но голова быстро перестраивается. Иногда я сĸучаю по UIKit и его гибĸости, ĸоторая поĸа превосходит SwiftUI, но ĸажется это будет недолго.
  • 96. 21 июня, вт 10:00 – 11:00 Егор Петров SwiftUI vs UIKit: to be or not to be?
  • 97. ;#o3o,(- App SwiftUI Чистый билд ~20 сек Hot reload $ Чистый билд ~2 мин Screens Views Models View Models Services Network Analytics Navigation View Controllers Views + 3rd party SDKs
  • 99. Перевести сервисы на Combine async/await Перевести все экраны на SwiftUI Перевести навигацию на SwiftUI .(o :a+-<6
  • 100. iOS 16
  • 102. https://developer.apple.com/tutorials/swiftui - туториалы от эпл https://swiftui-lab.com - глубокие статьи про сложные штуки https://www.fivestars.blog/swiftui - очень хорошо https://swiftwithmajid.com - не только SwiftUI, кратко и по делу ;,%+#*
  • 104. ;,%+#* Stanford SwiftUI course: https://cs193p.sites.stanford.edu FAQ: https://fuckingswiftui.com Improved SwiftUI docs: https://swiftontap.com ⭐ SwiftUI Layout System: https://kean.blog/post/swiftui-layout-system