Slide 1

Slide 1 text

AbemaTV iOSࣾ಺ϥϯνษڧձ 2018/04/10 Yuji Hato 5෼ͰֶͿ ࠩ෼ߋ৽ͱRxDataSources

Slide 2

Slide 2 text

About me Yuji Hato CyberAgent, Inc. / AbemaTV, Inc. dekatotoro @dekatotoro Contributed services

Slide 3

Slide 3 text

RxDataSources

Slide 4

Slide 4 text

RxDataSources RxDataSources ͬͯԿʁ

Slide 5

Slide 5 text

RxDataSources RxSwiftͷͨΊͷUITableViewͱ UICollectionViewͷDataSource

Slide 6

Slide 6 text

RxDataSources Կ͕خ͍͠ͷʁ

Slide 7

Slide 7 text

RxDataSources UITableView, UICollectionViewͷ delete, insert, move͕ ؆୯ʹ࣮૷Ͱ͖Δ !

Slide 8

Slide 8 text

RxDataSources delete, insert, moveͷͨΊͷߋ৽ͷࠩ෼Λந ग़͢ΔΞϧΰϦζϜ͸ਖ਼֬ʹ΍Ζ͏ͱ͢Δͱ ͚ͬ͜͏೉͍͠…

Slide 9

Slide 9 text

RxDataSources ࠩ෼நग़ΞϧΰϦζϜ

Slide 10

Slide 10 text

RxDataSources ɾWagner-Fischer ɾHeckel ɾMyers ɾWu …

Slide 11

Slide 11 text

RxDataSources ࠩ෼நग़ΞϧΰϦζϜͷϥΠϒϥϦ

Slide 12

Slide 12 text

RxDataSources ɾosteslag/Changeset … Wagner-Fischer ɾjflinter/Dwifft … Myers ɾwokalski/Diff.swift … Myers ɾonyarnold/Differ … Myers(Diff.swiftͷfork) ɾonmyway133/DeepDiff … HeckelΆ͍ ɾInstagram/IGListKit … Heckel ɾRxSwiftCommunity/RxDataSources … Heckelվ ɾkazuhiro4949/EditDistance … Wu

Slide 13

Slide 13 text

RxDataSources IGListKit … SectionΛදݱͨ͠ଟ࣍ݩ഑ ྻͷࠩ෼ʹ͸ରԠ͍ͯ͠ͳ͍ RxDataSources … ϢχʔΫIDΛ࣋ͪɺ഑ ྻʹॏෳ͕ͳ͍લఏͰSectionͷࠩ෼ߋ৽

Slide 14

Slide 14 text

RxDataSources ↓ ͜ΕΘ͔Γ΍͍͢ https://github.com/horita-yuya/ DifferenceAlgorithmComparison

Slide 15

Slide 15 text

RxDataSources ࣮૷

Slide 16

Slide 16 text

RxDataSources

Slide 17

Slide 17 text

RxDataSources SectionModelͷprotocol

Slide 18

Slide 18 text

public protocol SectionModelType { associatedtype Item public var items: [Self.Item] { get } public init(original: Self, items: [Self.Item]) } RxDataSources

Slide 19

Slide 19 text

public protocol IdentifiableType { associatedtype Identity : Hashable public var identity: Self.Identity { get } } RxDataSources

Slide 20

Slide 20 text

public protocol AnimatableSectionModelType : SectionModelType, IdentifiableType where Self.Item : IdentifiableType, Self.Item : Equatable { } RxDataSources

Slide 21

Slide 21 text

RxDataSources SectionModel

Slide 22

Slide 22 text

enum DownloadSeriesSectionModel: AnimatableSectionModelType { typealias Item = DownloadSeriesSectionItem case episodeList(season: DownloadSeason?, items: [Item]) case other(items: [Item]) // Mark: - IdentifiableType var identity: String { … } // Mark: - SectionModelType var items: [DownloadSeriesSectionItem] { switch self { case .episodeList(_, let items): return items case .other(let items): return items } } init(original: DownloadSeriesSectionModel, items: [Item]) { switch original { case .episodeList(let season, _): self = .episodeList(season: season, items: items) case .other: self = .other(items: items) } } } RxDataSources

Slide 23

Slide 23 text

RxDataSources SectionItemModel

Slide 24

Slide 24 text

enum DownloadSeriesSectionItem: IdentifiableType, Equatable { case episode(downloadMedia: DownloadMedia) case seeOtherEpisode(series: DownloadSeries) // Mark: - IdentifiableType var identity: String { … } // Mark: - Equatable static func == (lhs: DownloadSeriesSectionItem, rhs: DownloadSeriesSectionItem) -> Bool { … } } RxDataSources

Slide 25

Slide 25 text

RxDataSources AnimatableSectionModel

Slide 26

Slide 26 text

public struct AnimatableSectionModel { public var model: Section public var items: [Item] public init(model: Section, items: [ItemType]) { self.model = model self.items = items } } extension AnimatableSectionModel : AnimatableSectionModelType { public typealias Item = ItemType public typealias Identity = Section.Identity public var identity: Section.Identity { return model.identity } public init(original: AnimatableSectionModel, items: [Item]) { self.model = original.model self.items = items } public var hashValue: Int { return self.model.identity.hashValue } } RxDataSources Α͘ݟͨΒ3Y%BUB4PVSDFT ʹ"OJNBUBCMF4FDUJPO.PEFM͕ ༻ҙ͞Ε͍ͯΔͷͰ͜ΕΛ࢖͏ ͷ͕CFUUFS

Slide 27

Slide 27 text

RxDataSources RxTableViewSectionedAnimatedDataSource

Slide 28

Slide 28 text

open class RxTableViewSectionedAnimatedDataSource : TableViewSectionedDataSource , RxTableViewDataSourceType { … public init( animationConfiguration: AnimationConfiguration = AnimationConfiguration(), decideViewTransition: @escaping DecideViewTransition = { _, _, _ in .animated }, configureCell: @escaping ConfigureCell, titleForHeaderInSection: @escaping TitleForHeaderInSection = { _, _ in nil }, titleForFooterInSection: @escaping TitleForFooterInSection = { _, _ in nil }, canEditRowAtIndexPath: @escaping CanEditRowAtIndexPath = { _, _ in false }, canMoveRowAtIndexPath: @escaping CanMoveRowAtIndexPath = { _, _ in false }, sectionIndexTitles: @escaping SectionIndexTitles = { _ in nil }, sectionForSectionIndexTitle: @escaping SectionForSectionIndexTitle = { _, _, index in index } ) { self.animationConfiguration = animationConfiguration self.decideViewTransition = decideViewTransition super.init( configureCell: configureCell, titleForHeaderInSection: titleForHeaderInSection, titleForFooterInSection: titleForFooterInSection, canEditRowAtIndexPath: canEditRowAtIndexPath, canMoveRowAtIndexPath: canMoveRowAtIndexPath, sectionIndexTitles: sectionIndexTitles, sectionForSectionIndexTitle: sectionForSectionIndexTitle ) } … } RxDataSources

Slide 29

Slide 29 text

final class DownloadSeriesDelegate: NSObject, UITableViewDelegate { … lazy var dataSource: RxTableViewSectionedAnimatedDataSource = .init( animationConfiguration: AnimationConfiguration(insertAnimation: .fade, reloadAnimation: .none, deleteAnimation: .fade), configureCell: { [weak self] dataSource, table, indexPath, item in guard let me = self else { return UITableViewCell() // Should never reach here. } switch item { case .episode(let downloadMedia): let cell = table.dequeueReusableCell(DownloadListMediaCell.self, forIndexPath: indexPath) cell.configure(downloadMedia: downloadMedia) … return cell case .seeOtherEpisode(let series): let cell = table.dequeueReusableCell(DownloadSeeOtherEpisodeCell.self, forIndexPath: indexPath) cell.rx.tapGesture .subscribe(onNext: { [weak self] in … }) .disposed(by: cell.reusableDisposeBag) … return cell } }) … RxDataSources

Slide 30

Slide 30 text

final class DownloadSeriesViewStream { … let sectionModels: Property<[DownloadSeriesSectionModel]> private let _sectionModels = Variable<[DownloadSeriesSectionModel]>([]) … } RxDataSources

Slide 31

Slide 31 text

final class DownloadSeriesViewController: UIViewController { … viewStream.sectionModels.asObservable() // IMPORTANT: // crashճආɺཁௐࠪ. .throttle(1.0, latest: true, scheduler: ConcurrentMainScheduler.instance) .bind(to: tableView.rx.items(dataSource: delegate.dataSource)) .disposed(by: rx.disposeBag) … RxDataSources

Slide 32

Slide 32 text

RxDataSources UITableViewDelegate͸ʁ

Slide 33

Slide 33 text

// MARK: - UITableViewDelegate func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { … } func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { … } func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { … } func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { … } … RxDataSources

Slide 34

Slide 34 text

Conclusion

Slide 35

Slide 35 text

Conclusion • ࠩ෼நग़ΞϧΰϦζϜͷϥΠϒϥϦ΋ݕ౼͠Α͏ • DiffΞϧΰϦζϜ͸ࣗલͰ࡞Ζ͏ͱ͢Δͱ͚ͬ͜͏େม • RxSwift࢖͍ͬͯΔͳΒRxDataSourcesͰྑͦ͞͏

Slide 36

Slide 36 text

Thank you