• Stars
    star
    722
  • Rank 62,738 (Top 2 %)
  • Language
    Swift
  • License
    MIT License
  • Created about 6 years ago
  • Updated about 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Declarative, testable data source of UICollectionView and UITableView.

DataSourceKit

Declarative, testable data source of UICollectionView and UITableView.

Installation

There are 2 ways to install DataSourceKit to your project.

CocoaPods

Add a line pod "DataSourceKit" to your Podfile and run pod install.

Carthage

Add a line github "ishkawa/DataSourceKit" to your Cartfile and run carthage update.

Simple usage

  1. Let cells to conform BindableCell.
  2. Let view controllers to conform CellsDeclarator.
  3. Create an instance of CollectionViewDataSource and assign it to dataSource of UICollectionView.

Let cells to conform BindableCell

To make cells available in DataSourceKit machanism, let cells to conform BindableCell. BindableCell is a protocol which provide interfaces for registering cell to UICollectionView and binding cell with value.

For example, following implementation indicates that ReviewCell will be registered by UINib named "ReviewCell" and reuse identifier "Review", and the cell will be binded with value Review.

extension ReviewCell: BindableCell {
    static func makeBinder(value review: Review) -> CellBinder {
        return CellBinder(
            cellType: ReviewCell.self,
            nib: UINib(nibName: "ReviewCell", bundle: nil),
            reuseIdentifier: "Review",
            configureCell: { cell in
                cell.authorImageView.image = review.authorImage
                cell.authorNameLabel.text = review.authorName
                cell.bodyLabel.text = review.body
            })
    }
}

Let view controllers to conform CellsDeclarator

Next step is to declare arrangement of cells. CellsDeclarator is a protocol to do this.

Suppose we have following data in a view controller:

final class VenueDetailViewController: UIViewController {
    var venue: Venue
    var reviews: [Review]
    var relatedVenues: [Venue]
}

To declare arrangement of cells, pass makeBinder(value:) of cells to cell(_:) in declareCells(_:). Since call of cell(_:) will be converted to actual cell, call cell(_:) in the same order as you want to display cells.

Following example is the declaration of the demo which is displayed on the top of this page.

extension VenueDetailViewController: CellsDeclarator {
    typealias CellDeclaration = CellBinder

    func declareCells(_ cell: (CellDeclaration) -> Void) {
        cell(VenueOutlineCell.makeBinder(value: venue))

        if !reviews.isEmpty {
            cell(SectionHeaderCell.makeBinder(value: "Reviews"))
            for review in reviews {
                cell(ReviewCell.makeBinder(value: review))
            }
        }

        if !relatedVenues.isEmpty {
            cell(SectionHeaderCell.makeBinder(value: "Related Venues"))
            for relatedVenue in relatedVenues {
                cell(RelatedVenueCell.makeBinder(value: relatedVenue))
            }
        }
    }
}

In above code, it is assumed that VenueOutlineCell, SectionHeaderCell, ReviewCell and RelatedVenueCell conform to BindableCell protocol.

Assign CollectionViewDataSource to dataSource of UICollectionView

Final step is creating instance of CollectionViewDataSource and assigning cell declarations to it.

final class VenueDetailViewController: UIViewController {
    ...

    @IBOutlet private weak var collectionView: UICollectionView!

    private let dataSource = CollectionViewDataSource()

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        collectionView.dataSource = dataSource

        dataSource.cellDeclarations = cellDeclarations
    }
}

cellDeclarations of view controller is computed from result of declareCells(_:) of the view controller. When cellDeclarations of dataSource is updated, dataSource is ready for returning new arrangement. Then, you can invoke update of UICollectionView by any of reloadData(), reloadItems(at:), insertItems(at:) and deleteItems(at:).

Advanced usage

With some architectures such as MVVM and VIPER, it is important to separate logic from view. DataSourceKit has option to introduce the separation.

Expressing cell declarations by enum

CellsDeclarator have a type parameter named CellDeclaration, which represents type of element of cells arrangement. We can specify any type for this type parameter, even if it is an plain data which declared as struct or enum.

For example, following implementation declares cells by enum.

struct VenueDetailViewState {
    var venue: Venue
    var reviews: [Review]
    var relatedVenues: [Venue]
}

extension VenueDetailViewState: CellsDeclarator {
    enum CellDeclaration: Equatable {
        case outline(Venue)
        case sectionHeader(String)
        case review(Review)
        case relatedVenue(Venue)
    }

    func declareCells(_ cell: (CellDeclaration) -> Void) {
        cell(.outline(venue))

        if !reviews.isEmpty {
            cell(.sectionHeader("Reviews"))
            for review in reviews {
                cell(.review(review))
            }
        }

        if !relatedVenues.isEmpty {
            cell(.sectionHeader("Related Venues"))
            for relatedVenue in relatedVenues {
                cell(.relatedVenue(relatedVenue))
            }
        }
    }
}

Since VenueDetailViewState.CellDeclaration is just a plain data, it is easy to write test like below:

class VenueDetailViewStateTests: XCTestCase {
    func testEmptyRelatedVenues() {
        let venue = Venue(photo: nil, name: "Kaminarimon")
        let review1 = Review(authorImage: nil, authorName: "Yosuke Ishikawa", body: "Foo")
        let review2 = Review(authorImage: nil, authorName: "Masatake Yamoto", body: "Bar")

        let data = VenueDetailViewState(
            venue: venue,
            reviews: [
                review1,
                review2,
            ],
            relatedVenues: [])

        XCTAssertEqual(data.cellDeclarations, [
            .outline(venue),
            .sectionHeader("Reviews"),
            .review(review1),
            .review(review2),
        ])
    }
}

Associating enum declarations with cells

CollectionViewDataSource has a type parameter named CellDeclaration too. If this parameter differs from CellBinder, initializer of CollectionViewDataSource takes a function (CellDeclaration) -> CellBinder, because CollectionViewDataSource finally needs CellBinder to assemble actual cells.

final class VenueDetailViewController: UIViewController {
    @IBOutlet private weak var collectionView: UICollectionView!

    private let dataSource = CollectionViewDataSource<VenueDetailViewState.CellDeclaration> { cellDeclaration in
        switch cellDeclaration {
        case .outline(let venue):
            return VenueOutlineCell.makeBinder(value: venue)
        case .sectionHeader(let title):
            return SectionHeaderCell.makeBinder(value: title)
        case .review(let review):
            return ReviewCell.makeBinder(value: review)
        case .relatedVenue(let venue):
            return RelatedVenueCell.makeBinder(value: venue)
        }
    }

    private var state = VenueDetailViewState() {
        didSet {
            collectionView.reloadData()
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
        layout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
        collectionView.dataSource = dataSource

        dataSource.cellDeclarations = state.cellDeclarations
    }
}

More Repositories

1

APIKit

Type-safe networking abstraction layer that associates request type with response type.
Swift
1,994
star
2

DIKit

A statically typed dependency injector for Swift.
Swift
303
star
3

ISRefreshControl

(deprecated) iOS4-compatible UIRefreshControl
Objective-C
228
star
4

ISColumnsController

paginated container view controller.
Objective-C
108
star
5

ISDiskCache

LRU disk cache for iOS.
Objective-C
86
star
6

sandbox

Objective-C
60
star
7

UINavigationController-Transition

extension for custom transition by blocks.
Objective-C
58
star
8

iosdc-2018-demo

Swift
47
star
9

ISBackGesture

equips UIViewController with recognizing swipe to back.
Objective-C
38
star
10

NSRunLoop-PerformBlock

extension of NSRunLoop for waiting.
Objective-C
31
star
11

UIWebView-Progress

integrates NJKWebViewProgress into UIWebView.
Objective-C
28
star
12

storyboard-2018-demo

Swift
27
star
13

ISRemoveNull

extension of NSArray and NSDictionary to remove NSNull from them.
Objective-C
25
star
14

ISInteractiveEdgesNavigationBar

subclass of UINavigationBar which can handle touch events on both edges.
Objective-C
22
star
15

ISInvocationHookProxy

A proxy object that hooks each NSInvocation of target.
Objective-C
20
star
16

ISMemoryCache

NSDictionary-based memory cache.
Objective-C
19
star
17

ios_mvvm_test_example

Swift
18
star
18

ISHTTPOperation

a subclass of NSOperation to wrap asynchronous NSURLConnection.
Objective-C
13
star
19

talks

Swift
13
star
20

wire_example

Go
11
star
21

ISNetwork

minimal NSURLConnection wrapper (NSOperation based)
Objective-C
11
star
22

ISAlternativeRefreshControl

a template for creating custom UIRefreshControl.
Objective-C
11
star
23

ISMethodSwizzling

functions to swizzle instance/class methods.
Objective-C
9
star
24

iOS5UIRefreshControlTotorial

http://atnd.org/events/39505
Objective-C
8
star
25

APIKit-AlamofireAdapter

Alamofire adapter for APIKit.Session (alpha).
Swift
8
star
26

slackstream

Go
7
star
27

ishkawa.github.com

HTML
5
star
28

flutter_add_to_app_demo

Dart
5
star
29

ISCyclicPagesView

Objective-C
5
star
30

ISRevealController

container controller which has slidable UINavigationController and menu UIViewController.
Objective-C
3
star
31

GHFSupport

3
star
32

custom_json_serializable_example

Dart
3
star
33

IRMCommandOperation

an operation to send command to iRemocon.
Objective-C
3
star
34

NSDictionary-URLQuery

Ruby
3
star
35

ISTableViewController

a viewcontroller for the combination of UITableView and NSMutableArray
Objective-C
2
star
36

GHP

Swift
2
star
37

KIFNextExample

an example project for KIF 2.0.0
Objective-C
2
star
38

ISGcovFlusher

Test observer class which calls __gcov_flush() on completion of XCTests.
Ruby
2
star
39

ISFakeInterfaceOrientation

Objective-C
2
star
40

ttw

an extremely minimal twitter client.
Objective-C
2
star
41

slackmute

Go
2
star
42

XCTestCase-ExpectationWithCondition

extension of XCTestCase to create XCTestExpectation which waits for the condition to be fulfilled.
Swift
2
star
43

SenAsyncTestCase

a subclass of SenTestCase which is compatible with asynchronous tests.
Objective-C
2
star
44

AutoIssueExample

Qiita hackathon
Objective-C
1
star
45

ISMessageAlertView

alert view with ISTextView (subclass of UITextView)
Objective-C
1
star
46

NSDate-Twitter

NSDate category for Twitter
1
star
47

UIColor-Code

UIColor by color code.
Objective-C
1
star
48

term_twitter

twitter client for terminal
Python
1
star
49

RunTestsForAllDestinationsExample

Objective-C
1
star
50

CoreDataExperiment

Objective-C
1
star
51

GIRIcon

Objective-C
1
star
52

ISDataManager

Objective-C
1
star
53

ReadHub

source code reader
Objective-C
1
star
54

Newton

ๅคงไบบใฎๅคไผ‘ใฟใฎ่‡ช็”ฑ็ ”็ฉถใ€‚
Objective-C
1
star
55

remind

command to send task to Reminder (iCloud).
Objective-C
1
star
56

PMDR

extremely minimal pomodoro timer
Objective-C
1
star
57

ISTwitterAuthorizeViewController

a view controller to get access token for twitter (using OAuthCore and ISNetwork)
Objective-C
1
star