Skip to content

Instantly share code, notes, and snippets.

@srirammanian
Last active June 19, 2019 20:10
Show Gist options
  • Save srirammanian/9f37bdc07766c05b91a626d47456f382 to your computer and use it in GitHub Desktop.
Save srirammanian/9f37bdc07766c05b91a626d47456f382 to your computer and use it in GitHub Desktop.
Generic infinite paging list in SwiftUI
//
// PagingList.swift
//
// Created by Sriram Manian on 6/17/19.
// Copyright © 2019 Sriram Manian. All rights reserved.
//
import SwiftUI
import Combine
protocol PagingListRow {
associatedtype Item : Identifiable
init(_ item:Item)
}
struct PagingList<Data,Row,Content> : View where Data : RandomAccessCollection, Row : PagingListRow & View, Content : View, Data.Element == Row.Item {
typealias RowContentBuilder = (Row) -> Content
var combineIdentifier: CombineIdentifier = CombineIdentifier()
var items:Data
private var atEndOfStream = CurrentValueSubject<Int,Never>(0)
var rowType: Row.Type
var content: RowContentBuilder?
let itemsPerPage:Int
init(_ items: Data, rowType:Row.Type, itemsPerPage:Int = 30) {
self.items = items
self.rowType = rowType
self.itemsPerPage = itemsPerPage
}
init(_ items: Data, rowType:Row.Type, itemsPerPage:Int = 30, @ViewBuilder rowContent:@escaping RowContentBuilder) {
self.items = items
self.content = rowContent
self.rowType = rowType
self.itemsPerPage = itemsPerPage
}
func getContent(item:Row.Item) -> AnyView {
guard let c = self.content else { return AnyView(rowType.init(item)) }
if self.items.last!.id == item.id { return AnyView(c(rowType.init(item)).onAppear {
self.atEndOfStream.value = (self.items.count/self.itemsPerPage) + 1
})
} else {
return AnyView(c(rowType.init(item)))
}
}
var body: some View {
List {
ForEach(items.identified(by: \.id)) { item in
self.getContent(item: item)
}
}
}
public func onNextPage(perform action: @escaping (Int) -> Void) -> Self {
let _ = atEndOfStream
.filter { $0 > 0}
.throttle(for: .milliseconds(100), scheduler: RunLoop.main, latest: false)
.sink {
action($0)
}
return self
}
}
struct Blob : Identifiable {
let id:UUID
let name:String
init() {
let names = ["Quick", "Brown", "Fox", "Jumped"]
self.name = names.randomElement() ?? "AA"
id = UUID()
}
}
struct BlobRow : View, PagingListRow {
typealias Item = Blob
let item:Item
init(_ item: Item) {
self.item = item
}
var body: some View {
Text("\(item.name)")
}
}
struct BlobView : View {
@State var blobs:[Blob] = []
var body : some View {
VStack {
Text("Total:\(blobs.count)")
PagingList(blobs, rowType: BlobRow.self, itemsPerPage: 30) { $0.frame(height:9) }.onNextPage { _ in
self.blobs += Array(repeating: Blob(), count: 30)
}
.onAppear {
DispatchQueue.main.async {
self.blobs = Array(repeating: Blob(), count: 30)
}
}
Text("Total:\(blobs.count)")
}
}
}
#if DEBUG
struct PagingList_Previews : PreviewProvider {
static var previews: some View {
BlobView()
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment