Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Combine入門
Search
shtnkgm
June 11, 2020
Programming
2
270
Combine入門
AppleのCombineフレームワークの基本的な内容について
shtnkgm
June 11, 2020
Tweet
Share
More Decks by shtnkgm
See All by shtnkgm
Property Wrappers
shtnkgm
0
330
Saliency Detection
shtnkgm
0
33
パフォーマンス改善とユニットテスト
shtnkgm
4
1.6k
iOSのコードベースレイアウト
shtnkgm
2
750
20190117_iOSLT_CBLinSwift.pdf
shtnkgm
0
77
SwiftとFunctional Reactive Programming
shtnkgm
0
160
20180710_iOSLT_iOSでDarkModeを実装する
shtnkgm
0
80
20180410_iOSLT_SwiftとProtocol-OrientedProgramming
shtnkgm
0
99
20180220_iOSLT_Swiftとオブジェクト間の通知のパターン
shtnkgm
0
110
Other Decks in Programming
See All in Programming
AIにコードを生成するコードを作らせて、再現性を担保しよう! / Let AI generate code to ensure reproducibility
yamachu
7
6.1k
Spring gRPC で始める gRPC 入門 / Introduction to gRPC with Spring gRPC
mackey0225
0
140
Parallel::Pipesの紹介
skaji
2
870
DevTalks 25 - Create your own AI-infused Java apps with ease
kdubois
2
120
Rails産でないDBを Railsに引っ越すHACK - Omotesando.rb #110
lnit
1
100
Zennの運営完全に理解した #完全に理解したTalk
wadayusuke
1
140
TypeScript Language Service Plugin で CSS Modules の開発体験を改善する
mizdra
PRO
3
2.4k
複数アプリケーションを育てていくための共通化戦略
irof
2
740
型付け力を強化するための Hoogle のすゝめ / Boosting Your Type Mastery with Hoogle
guvalif
1
230
バランスを見極めよう!実装の意味を明示するための型定義 TSKaigi 2025 Day2 (5/24)
whatasoda
2
780
Babylon.js 8.0のアプデ情報を 軽率にキャッチアップ / catch-up-babylonjs-8
drumath2237
0
110
バリデーションライブラリ徹底比較
nayuta999999
1
440
Featured
See All Featured
How to train your dragon (web standard)
notwaldorf
92
6k
Making the Leap to Tech Lead
cromwellryan
134
9.3k
Documentation Writing (for coders)
carmenintech
71
4.8k
Optimizing for Happiness
mojombo
378
70k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
233
17k
How to Ace a Technical Interview
jacobian
276
23k
Practical Orchestrator
shlominoach
188
11k
Facilitating Awesome Meetings
lara
54
6.4k
Code Reviewing Like a Champion
maltzj
523
40k
Agile that works and the tools we love
rasmusluckow
329
21k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
228
22k
GraphQLとの向き合い方2022年版
quramy
46
14k
Transcript
! Combineೖ 2020.6.11 iOSLT / @shtnkgm 1
Combineͱ • Swift͚Framework • WWDC2019Ͱൃද • iOS 13.0+ • ඇಉظॲཧΛѻ͏ͷ
2
UIKitͷඇಉظॲཧ • Taregt/ActionʢTimer, @IBAction ...ʣ • NotificationCenter • URLSession •
KVOʢKey-value observingʣ • Callback࣮ʢDelegate, Closureʣ 3
ඇಉظॲཧʹ͓͚Δ՝ • ωετͨ͠Closure Callbackࠈ • Delegateॻ͘ͷಡΉͷΊΜͲ͍͘͞ࠈ • ඇಉظΠϕϯτͷछྨ࣮͍Ζ͍ΖɺྲྀΕ͕Θ͔Βͳ͘ͳ Δ 4
Combine͜ΕΒͷඇಉظॲཧΛ Ұݩతɺએݴతʹѻ͏ͨΊͷAPI 5
ओͳAPI • Publisher • Subscriber • Operator 6
Publisher • Τϥʔ͕ͲͷΑ͏ʹൃੜ͢Δ͔Λఆٛ • SubscriberͷొΛՄೳʹ͢Δʢߪಡʣ • ࣮ܕʢstructʣ 7
Publisherͷఆٛ protocol Publisher { associatedtype Output associatedtype Failure: Error //
Subscriberͷొ func subscribe<S>(_ subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input } 8
NotificationCenterͰͷPublisher࣮ྫ extension NotificationCenter { struct Publisher: Combine.Publisher { typealias Output
= Notifiation typealias Failure = Never // Τϥʔͳ͍߹Neverܕ init(center: NotificationCenter, name: Notification.Name, object: Any? = nil) } } 9
ฦ͞ͳ͍ͷͬͯVoid͡Όͳ͍ͷʁ 10
Neverܕ public enum Never {} • ࣮caseͳ͠enum • ͭ·ΓΛͭ͘Εͳ͍͜ͱΛදݱ 11
Voidܕ public typealias Void = () • ࣮ۭͷλϓϧͷΤΠϦΞε • ͭ·ΓۭͷλϓϧΛฦ͍ͯ͠Δ
12
fatalErrorͷΓNeverܕ class ViewController: UIViewController init() { ... } required init?(coder
aDecoder: NSCoder) { // NeverܕΛฦ͢͜ͱͰϓϩάϥϜऴྃ fatalError("init(coder:) has not been implemented") } func hoge() -> Int { // ΓͷܕݕࠪߦΘΕͳ͍ͷͰ // ͜ΕͰίϯύΠϧ͕௨Δ fatalError() } } 13
Subscriber • ͱྃΛड͚औΔ • ࢀরܕʢclassʣ 14
Subscriberͷఆٛ protocol Subscriber { associatedtype Input associatedtype Failure : Error
// SubscriptionΛड͚औΔ func receive(subscription: Subscription) // ೖྗΛड͚औΔ func receive(_ input: Self.Input) -> Subscribers.Demand // ྃΛड͚औΔʢ༗ݶͷ߹ʣ func receive(completion: Subscribers.Completion<Self.Failure>) } 15
Subscriberͷ࣮ྫʢAssignʣ ࢦఆ͞ΕͨΩʔύεʹΛηοτ͢ΔSubscriber // Subscribersͨͩͷnamespaceͱͯ͠ͷenum // AssignͷଞʹɺDemand, Completion, Sink͕͋Δ extension Subscribers
{ class Assign<Root, Input>: Subscriber, Cancellable { typealias Faiure = Never init(object: Root, keyPath: ReferenceWritableKeyPath<Root, Input>) } } 16
Operator • Publisherϓϩτίϧʹ४ڌ • ͕มԽ͢Δ;Δ·͍Λఆٛ • ܕʢstructʣ 17
Operatorͷ࣮ྫʢMapʣ // ͜Εnamespace // MapҎ֎ʹSequence, Catch, ReceiveOn, SubscribeOn...ͳͲΊͬͪΌ͋Δ extension Publishers
{ struct Map<Upstream, Output> where Upstream : Publisher { typealias Failure = Upstream.Failure let upstream: Upstream let transform: (Upstream.Output) -> Output } } 18
OperatorͰͰ͖Δ͜ͱͷҰྫ • ؔతͳͷม • Ϧετૢ࡞ • ΤϥʔϋϯυϦϯά • εϨουɺΩϡʔΠϯάॲཧʢαϒεϨουॲཧʣ •
εέδϡʔϦϯάʢλΠϚʔॲཧʣ 19
Publisher, Subscriber, OperatorΛͬͯΈΔ ຐ๏ֶߍͱֶ class Wizard { var grade: Int
init(grade: Int) { self.grade = grade } } let merlin = Wizard(grade: 5) 20
Publisher, Subscriber, OperatorΛͬͯΈΔ let graduationPublisher = NotificationCenter.Publisher(center: .default, name: .graduated,
object: merlin) let gradeSubscriber = Subscribers.Assign(object: merlin, keyPath: \.grade) let converter = Publishers.Map(upstream: graduationPublisher) { note in return note.userInfo?["NewGrade"] as? Int ?? 0 } converter.subscribe(gradeSubscriber) 21
Publisherͷmap, assign֦ு extension Publisher { func map<T>(_ transform: @escaping (Self.Output)
-> T) -> Publishers.Map<Self, T> { return Publishers.Map(upstream: self, transform: transform) } } extension Publisher where Self.Failure == Never { func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root, Self.Output>, on object: Root) -> AnyCancellable { return Subscribers.Assign<Self, Input>(object: object, keyPath: keyPath) } } 22
ϝιουνΣΠϯͰΑΓએݴతʹ let cancellable = NotificationCenter.default.publisher(for: .graduated, object: merlin) .map {
note in return note.userInfo?["NewGrade"] as? Int ?? 0 } .assign(to: \.grade, on: merlin) 23
ϝιουνΣΠϯͰΑΓએݴతʹ compactMapfilterɺprefixͳͲArrayͰݟ׳Εͨؔ let cancellable = NotificationCenter.default.publisher(for: .graduated, object: merlin) .compactMap
{ note in return note.userInfo?["NewGrade"] as? Int } .filter { $0 >= 5 } .prefix(3) .assign(to: \.grade, on: merlin) 24
௨৴ॲཧʹ͑ͦ͏ͳͷଟ͍ https://developer.apple.com/documentation/combine/publishers • Zip, Zip3, Zip4, CombineLatest: ͪ߹Θͤ • Decode:
σίʔυॲཧ • Retry: ϦτϥΠ • Throttle, Debounce: ࿈ଓ͢ΔΠϕϯτΛݮΒ͢ʢ࿈ଧɺΠϯΫϦϝϯλϧαʔ νʣ • Timeout: λΠϜΞτ 25
௨৴ॲཧͷ࣮ྫ 26
class UserRepository { private var subscriptions = Set<AnyCancellable>() func fetchUsers()
-> Future<[User], UserAPIError> { return Future<[User], UserAPIError> { [unowned self] promise in URLSession .shared .dataTaskPublisher(for: url) .debounce(for: .milliseconds(500), scheduler: RunLoop.main) .retry(3) .map { $0.data } .decode(type: [User].self, decoder: JSONDecoder()) .receive(on: DispatchQueue.main) .sink(receiveCompletion: { _ in promise(.failure(.somethingWrong)) }, receiveValue: { users in promise(.success(users)) }) .store(in: &self.subscriptions) } } func cancelAll() { subscriptions.forEach { $0.cancel() } } } 27
Viewͱͷ࿈ܞ • SwiftUI • ObservableObject/Published • UIKit • UICollectionViewDiffableDataSource •
UITableViewDiffableDataSource 28
noppefoxwolf/Combinative • https://github.com/noppefoxwolf/Combinative • UIKitͷUIControllʹCombineΛ֦ு let button = UIButton() button.cmb.tap.sink
{ (button) in // do something } @IBOutlet weak var textField: UITextField! textField.cmb.text.sink { (text) in print(text) } 29
͓ΘΓ 30