Skip to content

Commit 880055e

Browse files
author
Christian Elies
committed
refactor(advanced list): pagination is now available as a modifier, make use of view builder, removed unnecessary self; docs(): added code documentation to public API
1 parent f7d73f2 commit 880055e

File tree

9 files changed

+246
-148
lines changed

9 files changed

+246
-148
lines changed

README.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,108 @@ AdvancedList(yourData, content: { item in
297297
}
298298
```
299299
</details>
300+
301+
<details>
302+
<summary>Migration 4.0 -> 5.0</summary>
303+
304+
`Pagination` is now implemented as a `modifier` 💪 And last but not least the code documentation arrived 😀
305+
306+
**Before:**
307+
308+
```swift
309+
private lazy var pagination: AdvancedListPagination<AnyView, AnyView> = {
310+
.thresholdItemPagination(errorView: { error in
311+
AnyView(
312+
VStack {
313+
Text(error.localizedDescription)
314+
.lineLimit(nil)
315+
.multilineTextAlignment(.center)
316+
317+
Button(action: {
318+
// load current page again
319+
}) {
320+
Text("Retry")
321+
}.padding()
322+
}
323+
)
324+
}, loadingView: {
325+
AnyView(
326+
VStack {
327+
Divider()
328+
Text("Loading...")
329+
}
330+
)
331+
}, offset: 25, shouldLoadNextPage: {
332+
// load next page
333+
}, state: .idle)
334+
}()
335+
336+
@State private var listState: ListState = .items
337+
338+
AdvancedList(yourData, content: { item in
339+
Text("Item")
340+
}, listState: $listState, emptyStateView: {
341+
Text("No data")
342+
}, errorStateView: { error in
343+
VStack {
344+
Text(error.localizedDescription)
345+
.lineLimit(nil)
346+
347+
Button(action: {
348+
// do something
349+
}) {
350+
Text("Retry")
351+
}
352+
}
353+
}, loadingStateView: {
354+
Text("Loading ...")
355+
}, pagination: pagination)
356+
357+
```
358+
359+
**After:**
360+
361+
```swift
362+
@State private var listState: ListState = .items
363+
@State private var paginationState: AdvancedListPaginationState = .idle
364+
365+
AdvancedList(yourData, content: { item in
366+
Text("Item")
367+
}, listState: $listState, emptyStateView: {
368+
Text("No data")
369+
}, errorStateView: { error in
370+
VStack {
371+
Text(error.localizedDescription)
372+
.lineLimit(nil)
373+
374+
Button(action: {
375+
// do something
376+
}) {
377+
Text("Retry")
378+
}
379+
}
380+
}, loadingStateView: {
381+
Text("Loading ...")
382+
})
383+
.pagination(.init(type: .lastItem, shouldLoadNextPage: {
384+
paginationState = .loading
385+
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
386+
items.append(contentsOf: moreItems)
387+
paginationState = .idle
388+
}
389+
}) {
390+
switch paginationState {
391+
case .idle:
392+
EmptyView()
393+
case .loading:
394+
if #available(iOS 14.0, *) {
395+
ProgressView()
396+
} else {
397+
Text("Loading ...")
398+
}
399+
case let .error(error):
400+
Text(error.localizedDescription)
401+
}
402+
})
403+
```
404+
</details>

Sources/AdvancedList/private/Models/AdvancedListPaginationType.swift

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// AnyAdvancedListPagination.swift
3+
//
4+
//
5+
// Created by Christian Elies on 31.07.20.
6+
//
7+
8+
import SwiftUI
9+
10+
struct AnyAdvancedListPagination {
11+
let type: AdvancedListPaginationType
12+
let shouldLoadNextPage: () -> Void
13+
let content: () -> AnyView
14+
15+
init<Content: View>(_ pagination: AdvancedListPagination<Content>) {
16+
type = pagination.type
17+
shouldLoadNextPage = pagination.shouldLoadNextPage
18+
content = { AnyView(pagination.content()) }
19+
}
20+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// ListState+error.swift
3+
//
4+
//
5+
// Created by Christian Elies on 02.08.20.
6+
//
7+
8+
extension ListState {
9+
var error: Swift.Error? {
10+
guard case let ListState.error(error) = self else {
11+
return nil
12+
}
13+
return error
14+
}
15+
}

Sources/AdvancedList/public/Models/AdvancedListPagination.swift

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -5,56 +5,27 @@
55
// Created by Christian Elies on 15.08.19.
66
//
77

8-
import Combine
98
import SwiftUI
109

11-
public final class AdvancedListPagination<ErrorView: View, LoadingView: View>: NSObject, ObservableObject {
12-
let errorView: (Error) -> ErrorView
13-
let loadingView: () -> LoadingView
10+
/// Represents the pagination configuration.
11+
public struct AdvancedListPagination<Content: View> {
1412
let type: AdvancedListPaginationType
1513
let shouldLoadNextPage: () -> Void
16-
17-
public let objectWillChange = PassthroughSubject<Void, Never>()
18-
19-
public var state: AdvancedListPaginationState {
20-
didSet {
21-
objectWillChange.send()
22-
}
23-
}
24-
25-
init(@ViewBuilder errorView: @escaping (Error) -> ErrorView, @ViewBuilder loadingView: @escaping () -> LoadingView, type: AdvancedListPaginationType, shouldLoadNextPage: @escaping () -> Void, state: AdvancedListPaginationState) {
26-
self.errorView = errorView
27-
self.loadingView = loadingView
14+
let content: () -> Content
15+
16+
/// Initializes the pagination configuration with the given values.
17+
///
18+
/// - Parameters:
19+
/// - type: The type of the pagination, choose between `lastItem` or `thresholdItem`.
20+
/// - shouldLoadNextPage: A closure that is called everytime the end of a page is reached.
21+
/// - content: A closure providing a `View` which should be displayed at the end of a page.
22+
public init(
23+
type: AdvancedListPaginationType,
24+
shouldLoadNextPage: @escaping () -> Void,
25+
@ViewBuilder content: @escaping () -> Content
26+
) {
2827
self.type = type
2928
self.shouldLoadNextPage = shouldLoadNextPage
30-
self.state = state
31-
}
32-
}
33-
34-
extension AdvancedListPagination {
35-
public static func lastItemPagination(@ViewBuilder errorView: @escaping (Error) -> ErrorView, @ViewBuilder loadingView: @escaping () -> LoadingView, shouldLoadNextPage: @escaping () -> Void, state: AdvancedListPaginationState) -> AdvancedListPagination {
36-
AdvancedListPagination(errorView: errorView,
37-
loadingView: loadingView,
38-
type: .lastItem,
39-
shouldLoadNextPage: shouldLoadNextPage,
40-
state: state)
41-
}
42-
43-
public static func thresholdItemPagination(@ViewBuilder errorView: @escaping (Error) -> ErrorView, @ViewBuilder loadingView: @escaping () -> LoadingView, offset: Int, shouldLoadNextPage: @escaping () -> Void, state: AdvancedListPaginationState) -> AdvancedListPagination {
44-
AdvancedListPagination(errorView: errorView,
45-
loadingView: loadingView,
46-
type: .thresholdItem(offset: offset),
47-
shouldLoadNextPage: shouldLoadNextPage,
48-
state: state)
49-
}
50-
}
51-
52-
extension AdvancedListPagination where ErrorView == EmptyView, ErrorView == LoadingView {
53-
public static var noPagination: AdvancedListPagination {
54-
AdvancedListPagination(errorView: { _ in ErrorView() },
55-
loadingView: { LoadingView() },
56-
type: .noPagination,
57-
shouldLoadNextPage: {},
58-
state: .idle)
29+
self.content = content
5930
}
6031
}

Sources/AdvancedList/public/Models/AdvancedListPaginationState.swift

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,12 @@
77

88
import Foundation
99

10-
public enum AdvancedListPaginationState {
11-
case error(_ error: Error)
10+
/// Represents the different states of a pagination.
11+
public enum AdvancedListPaginationState: Equatable {
12+
/// The error state; use this state if an error occurs while loading a page.
13+
case error(_ error: NSError)
14+
/// The idle state; use this state if no page loading is in progress.
1215
case idle
16+
/// The loading state; use this state if a page is loaded.
1317
case loading
1418
}
15-
16-
extension AdvancedListPaginationState: Equatable {
17-
public static func ==(lhs: AdvancedListPaginationState,
18-
rhs: AdvancedListPaginationState) -> Bool {
19-
switch (lhs, rhs) {
20-
case (.error(let lhsError), .error(let rhsError)):
21-
return (lhsError as NSError) == (rhsError as NSError)
22-
case (.idle, .idle):
23-
return true
24-
case (.loading, .loading):
25-
return true
26-
default:
27-
return false
28-
}
29-
}
30-
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// AdvancedListPaginationType.swift
3+
//
4+
//
5+
// Created by Christian Elies on 16.08.19.
6+
//
7+
8+
/// Specifies the different pagination types.
9+
public enum AdvancedListPaginationType {
10+
/// Notifies the pagination configuration object when the last item in the list was reached.
11+
case lastItem
12+
/// Notifies the pagination configuration object when the given offset was passed.
13+
case thresholdItem(offset: Int)
14+
}

Sources/AdvancedList/public/Models/ListState.swift

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,12 @@
88

99
import Foundation
1010

11-
public enum ListState {
12-
case error(_ error: Error)
11+
/// Specifies the different states of an `AdvancedList`.
12+
public enum ListState: Equatable {
13+
/// The `error` state; displays the error view instead of the list to the user.
14+
case error(_ error: NSError)
15+
/// The `items` state (`default`); displays the items or the empty state view (if there are no items) to the user.
1316
case items
17+
/// The `loading` state; displays the loading state view instead of the list to the user.
1418
case loading
1519
}
16-
17-
extension ListState {
18-
var error: Error? {
19-
guard case let ListState.error(error) = self else {
20-
return nil
21-
}
22-
return error
23-
}
24-
}
25-
26-
extension ListState: Equatable {
27-
public static func ==(lhs: ListState, rhs: ListState) -> Bool {
28-
switch (lhs, rhs) {
29-
case (.error(let lhsError), .error(let rhsError)):
30-
return (lhsError as NSError) == (rhsError as NSError)
31-
case (.items, .items):
32-
return true
33-
case (.loading, .loading):
34-
return true
35-
default:
36-
return false
37-
}
38-
}
39-
}

0 commit comments

Comments
 (0)