❤️ Support my apps ❤️
- Push Hero - pure Swift native macOS application to test push notifications
- PastePal - Pasteboard, note and shortcut manager
- Quick Check - smart todo manager
- Alias - App and file shortcut manager
- My other apps
❤️❤️😇😍🤘❤️❤️
For demo, check DemoMicro
Read more
- How to build SwiftUI style UICollectionView data source in Swift
- A better way to update UICollectionView data in Swift with diff framework
Most of the time, we want to apply model data to cell with smart diffing.
Micro provides type safe SwiftUI style data source for UICollectionView, with super fast diffing powered by DeepDiff.
Just declare a State
with SwiftUI style forEach
and Micro will reload with animated diffing
struct Blog: DiffAware {}
class BlogCell: UICollectionViewCell {}
let dataSource = DataSource(collectionView: collectionView)
dataSource.state = State {
ForEach(blogs) { blog in
Cell<BlogCell>() { context, cell in
cell.nameLabel.text = blog.name
}
.onSelect { context in
print("cell at index \(context.indexPath.item) is selected")
}
.onSize { context in
CGSize(
width: context.collectionView.frame.size.width,
height: 40
)
}
}
}
The above uses Swift 5.1 function builder syntax, which uses forEach
method under the hood. You can also do like below with forEach
method.
dataSource.state = forEach(blogs) { blog in
Cell<BlogCell>() { context, cell in
cell.nameLabel.text = blog.name
}
.onSelect { context in
print("cell at index \(context.indexPath.item) is selected")
}
.onSize { context in
CGSize(
width: context.collectionView.frame.size.width,
height: 40
)
}
}
Features
- Supports iOS 8+
- Declare in type safe manner with
forEach
context
provides additional information, likeUICollectionView
andIndexPath
- Automatic reload with smart diffing whenever
state
is set - By default, diffing is animated, you can use
dataSource.reload(newState:isAnimated:completion)
to specify animated and completion
By default, when you set state
on DataSource
, animated diffing is performed. If you want to set animated property and to listen to completion event, you can use reload
method
dataSource.reload(
newState: newState,
isAnimated: false,
completion: { finished in
print("reloade is finished")
}
)
You can declare different Cell
in forEach
with different kinds of cell.
struct Movie: DiffAware {
enum Kind: Equatable {
case show(String)
case loading
case ad
}
let diffId = UUID()
let kind: Kind
static func compareContent(_ a: Movie, _ b: Movie) -> Bool {
return a.kind == b.kind
}
}
class MovieCell: UICollectionViewCell {
let nameLabel: UILabel = .init()
}
class LoadingCell: UICollectionViewCell {}
class AdCell: UICollectionViewCell {}
let layout = UICollectionViewFlowLayout()
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
let dataSource = DataSource(collectionView: collectionView)
collectionView.dataSource = dataSource
collectionView.delegate = dataSource
let movies: [Movie] = [
Movie(kind: .show("Titanic")),
Movie(kind: .show("Batman")),
Movie(kind: .loading),
Movie(kind: .ad)
]
dataSource.state = forEach(movies) { movie in
switch movie.kind {
case .show(let name):
return Cell<MovieCell>() { context, cell in
cell.nameLabel.text = name
}
.onSelect { _ in
}
.onDeselect { _ in
}
.onWillDisplay { _, _ in
}
.onDidEndDisplay { _, _ in
}
.onSize { context in
CGSize(width: context.collectionView.frame.size.width, height: 40)
}
case .loading:
return Cell<LoadingCell>()
case .ad:
return Cell<AdCell>()
}
}
DataSource
is completely overridable, if you want to customize any methods, just subclass DataSource
, override methods and access its state.models
class CustomDataSource: DataSource {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let blog = state.models[indexPath.item] as? Blog
print(blog)
}
}
Micro is also available through Swift Package Manager
.package(url: "https://github.com/onmyway133/Micro", from: "1.2.0")
Khoa Pham, onmyway133@gmail.com
Micro is available under the MIT license. See the LICENSE file for more info.