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
320
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Combine入門
AppleのCombineフレームワークの基本的な内容について
shtnkgm
June 11, 2020
More Decks by shtnkgm
See All by shtnkgm
Property Wrappers
shtnkgm
0
370
Saliency Detection
shtnkgm
0
86
パフォーマンス改善とユニットテスト
shtnkgm
4
1.7k
iOSのコードベースレイアウト
shtnkgm
2
840
20190117_iOSLT_CBLinSwift.pdf
shtnkgm
0
120
SwiftとFunctional Reactive Programming
shtnkgm
0
200
20180710_iOSLT_iOSでDarkModeを実装する
shtnkgm
0
120
20180410_iOSLT_SwiftとProtocol-OrientedProgramming
shtnkgm
0
140
20180220_iOSLT_Swiftとオブジェクト間の通知のパターン
shtnkgm
0
170
Other Decks in Programming
See All in Programming
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
340
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.7k
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
190
AI時代のUIはどこへ行く?その2!
yusukebe
22
7.7k
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
430
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
820
エージェンティックRAGにAWSで入門しよう!
har1101
9
1.9k
LaravelLive Japan の裏方のすべて — 第188回 PHP勉強会@東京 (2026-06-24)
suguruooki
2
140
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
17
8.5k
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
0
140
任せる範囲はこう広がった / How the Scope of AI Delegation Has Expanded
nrslib
1
220
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
200
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
96
14k
Agile Actions for Facilitating Distributed Teams - ADO2019
mkilby
0
220
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
123
22k
Hiding What from Whom? A Critical Review of the History of Programming languages for Music
tomoyanonymous
2
880
Paper Plane (Part 1)
katiecoart
PRO
0
9.5k
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
180
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.9k
What does AI have to do with Human Rights?
axbom
PRO
1
2.2k
Art, The Web, and Tiny UX
lynnandtonic
304
22k
The Curse of the Amulet
leimatthew05
2
13k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
Why Our Code Smells
bkeepers
PRO
340
58k
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