Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

https://github.com/onmyway133/Micro

🏎Fast diffing and type safe SwiftUI style data source for UICollectionView
https://github.com/onmyway133/Micro

datasource diff reload safe swift swiftui uicollectionview

Last synced: 3 months ago
JSON representation

🏎Fast diffing and type safe SwiftUI style data source for UICollectionView

Lists

README

        

## Micro

❀️ Support my apps ❀️

- [Push Hero - pure Swift native macOS application to test push notifications](https://onmyway133.com/pushhero)
- [PastePal - Pasteboard, note and shortcut manager](https://onmyway133.com/pastepal)
- [Quick Check - smart todo manager](https://onmyway133.com/quickcheck)
- [Alias - App and file shortcut manager](https://onmyway133.com/alias)
- [My other apps](https://onmyway133.com/apps/)

β€οΈβ€οΈπŸ˜‡πŸ˜πŸ€˜β€οΈβ€οΈ



For demo, check [DemoMicro](https://github.com/onmyway133/Micro/tree/master/Example/DemoMicro)

Read more

- [How to build SwiftUI style UICollectionView data source in Swift](https://onmyway133.github.io/blog/How-to-build-SwiftUI-style-UICollectionView-data-source-in-Swift/)
- [A better way to update UICollectionView data in Swift with diff framework](https://medium.com/flawless-app-stories/a-better-way-to-update-uicollectionview-data-in-swift-with-diff-framework-924db158db86)

## Description

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](https://github.com/onmyway133/DeepDiff).
Just declare a `State` with SwiftUI style `forEach` and Micro will reload with animated diffing

```swift
struct Blog: DiffAware {}
class BlogCell: UICollectionViewCell {}

let dataSource = DataSource(collectionView: collectionView)
dataSource.state = State {
ForEach(blogs) { blog in
Cell() { 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.

```swift
dataSource.state = forEach(blogs) { blog in
Cell() { 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, like `UICollectionView` and `IndexPath`
- 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

## Advanced

### Animate reloading

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

```swift
dataSource.reload(
newState: newState,
isAnimated: false,
completion: { finished in
print("reloade is finished")
}
)
```

### Complex model with multiple cell types

You can declare different `Cell` in `forEach` with different kinds of cell.

```swift
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() { 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()
case .ad:
return Cell()
}
}
```

### Customize with subclass

`DataSource` is completely overridable, if you want to customize any methods, just subclass `DataSource`, override methods and access its `state.models`

```swift
class CustomDataSource: DataSource {
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let blog = state.models[indexPath.item] as? Blog
print(blog)
}
}
```

## Installation

**Micro** is also available through [Swift Package Manager](https://swift.org/package-manager/)

```swift
.package(url: "https://github.com/onmyway133/Micro", from: "1.2.0")
```

## Author

Khoa Pham, [email protected]

## License

**Micro** is available under the MIT license. See the [LICENSE](https://github.com/onmyway133/Micro/blob/master/LICENSE.md) file for more info.