SlideShare a Scribd company logo
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Не про SwiftUI, а про наш опыт с ним
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Просто хотелось нового
!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
Разделили между собой задачи на изучение
Засинкались и составили базу знаний
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
#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& "
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
# LIVE
Каждая вьюшка это композиция других вьюх
У каждой вьюшки есть модель - композиция
примитивных типов и моделей других вьюшек
У экрана есть View Model, которая загружает данные
и реагирует на действия
ViewModel – протокол, у которого может быть
множество реализаций
Плейсхолдеры для состояний загрузки это просто
Placeholders ≠ Stubs
Превью для разных состояний сильно помогают в
процессе разработки
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
Избавляемся от старья и переходим на SwiftUI / Руслан Кавецкий  (Agora)
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

PDF
MLOps journey at Swisscom: AI Use Cases, Architecture and Future Vision
PDF
Intro to Web Development Using Python and Django
PDF
Alkaire foster methodology
PDF
Regression Analysis and model comparison on the Boston Housing Data
PPTX
gdsc-html-ppt.pptx
PDF
Digital Transformation Strategy Powerpoint Presentation Slides
PDF
Project Output PowerPoint Presentation Slides
PDF
7 Ways to Lead Digital Transformation Without Being an IT Specialist
MLOps journey at Swisscom: AI Use Cases, Architecture and Future Vision
Intro to Web Development Using Python and Django
Alkaire foster methodology
Regression Analysis and model comparison on the Boston Housing Data
gdsc-html-ppt.pptx
Digital Transformation Strategy Powerpoint Presentation Slides
Project Output PowerPoint Presentation Slides
7 Ways to Lead Digital Transformation Without Being an IT Specialist

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

PPTX
2014.12.06 01 Александр Чернышев — Нафига Козе Баян или нужен ли вам swift, и...
PDF
C# Web. Занятие 11.
PPTX
Основы "мобильной" разработки на примере платформы iOs (iPhone)
PDF
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
PPT
Telerik Web aii
PPTX
iOS and Android Mobile Test Automation
PDF
Mobile automation uamobile
PDF
GraphQL API: Patterns | Андрей Чиж | Zlit Tech
PPT
Rich UI on Dojo Toolkit and Zend Framework
PPTX
jQuery как путь к RIA
PDF
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
PDF
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
PDF
Android: Как создать свое первое приложение?
PDF
Remote (dev)tools своими руками
PPTX
Online TechTalk “Flutter Mobile Development”
PDF
Особенности тестирования мобильных приложений (Android, iOS)
PPT
Netbeans Desktop Applications
PPTX
Антон Валюх - Использование паттерна Mvvm в android
PDF
О тестирование софта: мир качества, жуков и информации.
PDF
О тестирование софта: мир качества, жуков и информации. Атрощенков Сергей.
2014.12.06 01 Александр Чернышев — Нафига Козе Баян или нужен ли вам swift, и...
C# Web. Занятие 11.
Основы "мобильной" разработки на примере платформы iOs (iPhone)
Школа-студия разработки приложений для iOS. 2 лекция. MVC, View, Controllers
Telerik Web aii
iOS and Android Mobile Test Automation
Mobile automation uamobile
GraphQL API: Patterns | Андрей Чиж | Zlit Tech
Rich UI on Dojo Toolkit and Zend Framework
jQuery как путь к RIA
«MVVM в Swift», Александр Зимин, независимый iOS-разработчик
Александр Сычев "Статика и динамика. Как фреймворки помогут прокачать ваше пр...
Android: Как создать свое первое приложение?
Remote (dev)tools своими руками
Online TechTalk “Flutter Mobile Development”
Особенности тестирования мобильных приложений (Android, iOS)
Netbeans Desktop Applications
Антон Валюх - Использование паттерна Mvvm в android
О тестирование софта: мир качества, жуков и информации.
О тестирование софта: мир качества, жуков и информации. Атрощенков Сергей.
Ad

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