With the iOS 11 announcement one of the things that most excited me was the MapKit updates, the highlight of course being clustering. Let's look at an implementation now.
Read moreGood Practice with RxSwift & MVVM - Exposing Variables
Just a quick tip for the use of RxSwift
within an MVVM architected application. Most people likely know to do this, but it never hurts to throw it out there.
In a view model, my instinct was to set it up like so:
public protocol PeopleViewModel {
/// Drives a collection of person summaries (UITableView or UICollectionView for example).
var personViewModels: Variable<[PersonViewModel]> { get }
/// Whether or not the perople are people loaded.
var isLoading: Variable<Bool>
/// Whether there are any people.
var isEmpty: Variable<Bool>
/// Fetches people after a given index (useful for paginated responses).
func fetchPeople(after index: Int)
}
Within the view, I would do the usual stuff:
private func configure(with viewModel: PeopleViewModel) {
viewModel.personViewModels.asObservable()
.bind(to: tableView.rx.items(cellIdentifier: cellIdentifier, cellType: PersonTableViewCell.self)) { (row: Int, cellModel: UserSummaryViewModeling, cell: UserTableViewCell) in
cell.viewModel = cellModel
viewModel.fetchPeople(after: row)
}
.disposed(by: disposeBag)
/// etc…
}
However, this was poor enforcement of access control and from within the view, if I was evil, I could do something like this:
viewModel.personViewModels.value = []
All my people are gone!!!
What I really want is:
public protocol PeopleViewModel {
var personViewModels: Driver<[PersonViewModel]> { get }
var isLoading: Driver<Bool>
var isEmpty: Driver<Bool>
func fetchPeople(after index: Int)
}
The implementation of which would look like:
final class PeopleViewModelImp {
let personViewModels: Driver<[PersonViewModel]> { get }
let isLoading: Driver<Bool>
let isEmpty: Driver<Bool>
fileprivate let items = Variable([PersonViewModel]())
fileprivate let isEmptyVariable = Variable(false)
fileprivate let isLoadingVariable = Variable(false)
fileptivate let apiClient: APIClient
fileprivate let disposeBag: DisposeBag
init(apiClient: APIClient) {
personViewModels = items.asDriver()
isEmpty = isEmptyVariable.asDriver()
isLoading = isLoadingVariable.asDriver()
self.apiClient = apiClient
fetchPeople(after: 0)
}
func fetchPeople(after index: Int) {
isLoadingVariable.value = true
apiClient.fetchPeople().subscribe(onNext: { people in
let viewModels = people.map(PersonViewModelImp.init(person:))
items.value += viewModels
isEmptyVariable.value = items.value.isEmpty
isLoadingVariable.value = false
})
.disposed(by: disposeBag)
}
}
Tada!
Now, in an ideal world, the fetching of the people would not be disposed of within the actual view model. The aim is to actually keep the view model free of a DisposeBag
. For now, this is fine, and certainly an improvement over the original.
The Painful Migration to Lock-swift
Recently I migrated the use of the Lock v1 SDK to the new Lock-Swift SDK and was left wrestling with issues for days. I hope this post will help anybody also having a difficult time, and this should serve in addition to the provided Migration guide which I found to be currently lacking.
Read more
UIStackView Disappointments
When working with UIStackView
within Interface Builder I would frequently encounter errors that would tell me the an impossible co-efficient could not be reconciled within auto-layout. I fixed this by not using UIStackView
. This was a difficult decision to make and only came after losing a few hours to these issues. I had actually argued to target iOS 9 in large part because of my excitement about this new tool; hopefully Apple will fix it in iOS 10. 😞
The aforementioned error:
Cannot find an outgoing row head for incoming head during optimization of variable with near-zero coefficient, which should never happen.
Yet another, more frustrating problem I was hitting involved the reliable crashing of Xcode when working with 4 horizontal stack views within a larger vertical stack view. I lost half a day to this and once again accepted that I would have to pretend UIStackView
does not exist.
Someone let me know if they ever fix it. 😒