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

Combine入門

 Combine入門

AppleのCombineフレームワークの基本的な内容について

shtnkgm

June 11, 2020
Tweet

More Decks by shtnkgm

Other Decks in Programming

Transcript

  1. !
    Combineೖ໳
    2020.6.11 iOSLT / @shtnkgm
    1

    View Slide

  2. Combineͱ͸
    • Swift޲͚Framework
    • WWDC2019Ͱൃද
    • iOS 13.0+
    • ඇಉظॲཧΛѻ͏΋ͷ
    2

    View Slide

  3. UIKitͷඇಉظॲཧ
    • Taregt/ActionʢTimer, @IBAction ...ʣ
    • NotificationCenter
    • URLSession
    • KVOʢKey-value observingʣ
    • Callback࣮૷ʢDelegate, Closureʣ
    3

    View Slide

  4. ඇಉظॲཧʹ͓͚Δ՝୊
    • ωετͨ͠Closure Callback஍ࠈ
    • Delegateॻ͘ͷ΋ಡΉͷ΋ΊΜͲ͍͘͞஍ࠈ
    • ඇಉظΠϕϯτͷछྨ΋࣮૷΋͍Ζ͍ΖɺྲྀΕ͕Θ͔Βͳ͘ͳ
    Δ
    4

    View Slide

  5. Combine͸͜ΕΒͷඇಉظॲཧΛ
    Ұݩతɺએݴతʹѻ͏ͨΊͷAPI
    5

    View Slide

  6. ओͳAPI
    • Publisher
    • Subscriber
    • Operator
    6

    View Slide

  7. Publisher
    • ஋΍Τϥʔ͕ͲͷΑ͏ʹൃੜ͢Δ͔Λఆٛ
    • Subscriberͷొ࿥ΛՄೳʹ͢Δʢߪಡʣ
    • ࣮૷͸஋ܕʢstructʣ
    7

    View Slide

  8. Publisherͷఆٛ
    protocol Publisher {
    associatedtype Output
    associatedtype Failure: Error
    // Subscriberͷొ࿥
    func subscribe(_ subscriber: S)
    where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input
    }
    8

    View Slide

  9. 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

    View Slide

  10. ஋ฦ͞ͳ͍ͷͬͯVoid͡Όͳ͍ͷʁ
    10

    View Slide

  11. Neverܕ
    public enum Never {}
    • ࣮૷͸caseͳ͠enum
    • ͭ·Γ஋Λͭ͘Εͳ͍͜ͱΛදݱ
    11

    View Slide

  12. Voidܕ
    public typealias Void = ()
    • ࣮૷͸ۭͷλϓϧͷΤΠϦΞε
    • ͭ·ΓۭͷλϓϧΛฦ͍ͯ͠Δ
    12

    View Slide

  13. fatalErrorͷ໭Γ஋΋Neverܕ
    class ViewController: UIViewController
    init() {
    ...
    }
    required init?(coder aDecoder: NSCoder) {
    // NeverܕΛฦ͢͜ͱͰϓϩάϥϜऴྃ
    fatalError("init(coder:) has not been implemented")
    }
    func hoge() -> Int {
    // ໭Γ஋ͷܕݕࠪ΋ߦΘΕͳ͍ͷͰ
    // ͜ΕͰίϯύΠϧ͕௨Δ
    fatalError()
    }
    }
    13

    View Slide

  14. Subscriber
    • ஋ͱ׬ྃΛड͚औΔ
    • ࢀরܕʢclassʣ
    14

    View Slide

  15. 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)
    }
    15

    View Slide

  16. Subscriberͷ࣮૷ྫʢAssignʣ
    ࢦఆ͞ΕͨΩʔύεʹ஋Ληοτ͢ΔSubscriber
    // Subscribers͸ͨͩͷnamespaceͱͯ͠ͷenum
    // AssignͷଞʹɺDemand, Completion, Sink͕͋Δ
    extension Subscribers {
    class Assign: Subscriber, Cancellable {
    typealias Faiure = Never
    init(object: Root, keyPath: ReferenceWritableKeyPath)
    }
    }
    16

    View Slide

  17. Operator
    • Publisherϓϩτίϧʹ४ڌ
    • ஋͕มԽ͢Δ;Δ·͍Λఆٛ
    • ஋ܕʢstructʣ
    17

    View Slide

  18. Operatorͷ࣮૷ྫʢMapʣ
    // ͜Ε΋namespace
    // MapҎ֎ʹ΋Sequence, Catch, ReceiveOn, SubscribeOn...ͳͲΊͬͪΌ͋Δ
    extension Publishers {
    struct Map where Upstream : Publisher {
    typealias Failure = Upstream.Failure
    let upstream: Upstream
    let transform: (Upstream.Output) -> Output
    }
    }
    18

    View Slide

  19. OperatorͰͰ͖Δ͜ͱͷҰྫ
    • ؔ਺తͳ஋ͷม׵
    • Ϧετૢ࡞
    • ΤϥʔϋϯυϦϯά
    • εϨουɺΩϡʔΠϯάॲཧʢαϒεϨουॲཧʣ
    • εέδϡʔϦϯάʢλΠϚʔॲཧʣ
    19

    View Slide

  20. Publisher, Subscriber, OperatorΛ࢖ͬͯΈΔ
    ຐ๏ֶߍͱֶ೥
    class Wizard {
    var grade: Int
    init(grade: Int) {
    self.grade = grade
    }
    }
    let merlin = Wizard(grade: 5)
    20

    View Slide

  21. 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

    View Slide

  22. Publisherͷmap, assign֦ு
    extension Publisher {
    func map(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map {
    return Publishers.Map(upstream: self, transform: transform)
    }
    }
    extension Publisher where Self.Failure == Never {
    func assign(to keyPath: ReferenceWritableKeyPath,
    on object: Root) -> AnyCancellable {
    return Subscribers.Assign(object: object, keyPath: keyPath)
    }
    }
    22

    View Slide

  23. ϝιουνΣΠϯͰΑΓએݴతʹ
    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

    View Slide

  24. ϝιουνΣΠϯͰΑΓએݴతʹ
    compactMap΍filterɺ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

    View Slide

  25. ௨৴ॲཧʹ࢖͑ͦ͏ͳ΋ͷ΋ଟ͍
    https://developer.apple.com/documentation/combine/publishers
    • Zip, Zip3, Zip4, CombineLatest: ଴ͪ߹Θͤ
    • Decode: σίʔυॲཧ
    • Retry: ϦτϥΠ
    • Throttle, Debounce: ࿈ଓ͢ΔΠϕϯτΛݮΒ͢ʢ࿈ଧɺΠϯΫϦϝϯλϧαʔ
    νʣ
    • Timeout: λΠϜΞ΢τ
    25

    View Slide

  26. ௨৴ॲཧͷ࣮૷ྫ
    26

    View Slide

  27. class UserRepository {
    private var subscriptions = Set()
    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 Slide

  28. Viewͱͷ࿈ܞ
    • SwiftUI
    • ObservableObject/Published
    • UIKit
    • UICollectionViewDiffableDataSource
    • UITableViewDiffableDataSource
    28

    View Slide

  29. 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

    View Slide

  30. ͓ΘΓ
    30

    View Slide