Skip to content

Commit 500fe19

Browse files
committed
clean up a bit of the readme and the extra code
1 parent 57351b5 commit 500fe19

File tree

4 files changed

+66
-31
lines changed

4 files changed

+66
-31
lines changed

Example/Example/AppDelegate.swift

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,10 @@
44
//
55

66
import UIKit
7-
import Combine
87

98
@UIApplicationMain
109
class AppDelegate: UIResponder, UIApplicationDelegate {
11-
var subscriptions = [AnyCancellable]()
12-
1310
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
14-
let publisher = Future<String, Never> { promise in
15-
print("request network data")
16-
17-
DispatchQueue.main.async {
18-
promise(.success("JSON"))
19-
}
20-
}
21-
.eraseToAnyPublisher()
22-
.assertMaxSubscriptions(1)
23-
.share()
24-
25-
publisher
26-
.sink { print($0) }
27-
.store(in: &subscriptions)
28-
29-
publisher
30-
.sink { print($0) }
31-
.store(in: &subscriptions)
32-
3311
return true
3412
}
3513

Example/Example/ViewController.swift

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class ViewController: UIViewController {
3737

3838
// Publisher to emit data to the table
3939
var data = PassthroughSubject<[[Person]], Never>()
40+
var subscriptions = [AnyCancellable]()
4041

4142
private var flag = false
4243

@@ -58,16 +59,18 @@ class ViewController: UIViewController {
5859
// A plain list with a single section -> Publisher<[Person], Never>
5960
data
6061
.map { $0[0] }
61-
.subscribe(tableView.rowsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in
62+
.subscribe(retaining: tableView.rowsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in
6263
cell.nameLabel.text = "\(indexPath.section+1).\(indexPath.row+1) \(model.name)"
6364
}))
65+
.store(in: &subscriptions)
6466

6567
case .multiple:
6668
// Table with sections -> Publisher<[[Person]], Never>
6769
data
68-
.subscribe(tableView.sectionsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in
70+
.subscribe(retaining: tableView.sectionsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in
6971
cell.nameLabel.text = "\(indexPath.section+1).\(indexPath.row+1) \(model.name)"
7072
}))
73+
.store(in: &subscriptions)
7174

7275
case .sections:
7376
// Table with section driven by `Section` models -> Publisher<[Section<Person>], Never>
@@ -77,9 +80,10 @@ class ViewController: UIViewController {
7780
return Section(header: "Header", items: persons, footer: "Footer")
7881
}
7982
}
80-
.subscribe(tableView.sectionsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in
83+
.subscribe(retaining: tableView.sectionsSubscriber(cellIdentifier: "Cell", cellType: PersonCell.self, cellConfig: { cell, indexPath, model in
8184
cell.nameLabel.text = "\(indexPath.section+1).\(indexPath.row+1) \(model.name)"
8285
}))
86+
.store(in: &subscriptions)
8387

8488
case .noAnimations:
8589
// Use custom controller to disable animations
@@ -89,7 +93,8 @@ class ViewController: UIViewController {
8993
controller.animated = false
9094

9195
data
92-
.subscribe(tableView.sectionsSubscriber(controller))
96+
.subscribe(retaining: tableView.sectionsSubscriber(controller))
97+
.store(in: &subscriptions)
9398
}
9499

95100
reload()

README.md

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
1.3 [Subscribing a completing publisher](#subscribing-a-completing-publisher)
1818

19-
1.4 Batched/Paged list of elements
19+
1.4 [List loaded in batches](#list-loaded-in-batches)
2020

2121
2. [**Installation**](#installation)
2222

@@ -104,14 +104,67 @@ Just([Person(name: "test"])
104104

105105
This will keep the subscriber and the data source alive until you cancel the subscription manually or it is released from memory.
106106

107-
#### Batched/Paged list of elements
107+
#### List loaded in batches
108108

109-
A common pattern in list based views is to load a very long list of elements in "batches" or "pages". (The distinction being that pages imply ordered, equal-length batches.)
109+
A common pattern for list views is to load a very long list of elements in "batches" or "pages". (The distinction being that pages imply ordered, equal-length batches.)
110110

111-
**CombineDataSources** includes a data source allowing you to easily implement the batched list pattern called `BatchesDataSource`.
111+
**CombineDataSources** includes a data source allowing you to easily implement the batched list pattern called `BatchesDataSource` and a table view controller `TableViewBatchesController` which wraps loading items in batches via the said data source and managing your UI.
112+
113+
In case you want to implement your own custom logic, you can use directly the data source type:
114+
115+
```swift
116+
let input = BatchesInput(
117+
reload: resetSubject.eraseToAnyPublisher(),
118+
loadNext: loadNextSubject.eraseToAnyPublisher()
119+
)
120+
121+
let dataSource = BatchesDataSource<String>(
122+
items: ["Initial Element"],
123+
input: input,
124+
initialToken: nil,
125+
loadItemsWithToken: { token in
126+
return MockAPI.requestBatchCustomToken(token)
127+
})
128+
```
129+
130+
`dataSource` is controlled via the two inputs:
131+
132+
- `input.reload` (to reload the very first batch) and
133+
134+
- `loadNext` (to load each next batch)
135+
136+
The data source has four outputs:
137+
138+
- `output.$items` is the current list of elements,
139+
140+
- `output.$isLoading` whether it's currently fetching a batch of elements,
141+
142+
- `output.$isCompleted` whether the data source fetched all available elements, and
143+
144+
- `output.$error` which is a stream of `Error?` elements where errors by the loading closure will bubble up.
145+
146+
In case you'd like to use the provided controller the code is fairly simple as well. You use the standard table view items controller and `TableViewBatchesController` like so:
147+
148+
```swift
149+
let itemsController = TableViewItemsController<[[String]]>(cellIdentifier: "Cell", cellType: UITableViewCell.self, cellConfig: { cell, indexPath, text in
150+
cell.textLabel!.text = "\(indexPath.row+1). \(text)"
151+
})
152+
153+
let tableController = TableViewBatchesController<String>(
154+
tableView: tableView,
155+
itemsController: itemsController,
156+
initialToken: nil,
157+
loadItemsWithToken: { nextToken in
158+
MockAPI.requestBatch(token: nextToken)
159+
}
160+
)
161+
```
162+
163+
`tableController` will set the table view data source, fetch items, and display cells with the proper animations.
112164

113165
## Todo
114166

167+
- [ ] much better README, pls
115168
- [ ] use a @Published for the time being instead of withLatestFrom
116169
- [ ] make the batches data source prepend or append the new batch (e.g. new items come from the top or at the bottom)
117170
- [ ] cover every API with tests

Sources/CombineDataSources/BatchesDataSource/BatchesDataSource.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,6 @@ public struct BatchesDataSource<Element> {
195195
.store(in: &subscriptions)
196196

197197
batchRequest
198-
.assertMaxSubscriptions(1)
199198
.flatMap { token in
200199
return loadNextCallback(token)
201200
.map { result -> ResponseResult in

0 commit comments

Comments
 (0)