Here & Now
Here & Now is a weather app that I made to experiment with an iOS app architecture based on nestable components and RxSwift.
View controllers are deliberately minimal. They wire up their root ViewComponent
and add its subview. Nested ViewComponents are used to break the UI code down into smaller logical pieces.
A ViewComponent
is defined as:
protocol ViewComponent {
// Streams used by the component
associatedtype Inputs
// Streams produced by the component
associatedtype Outputs
// Root view of the component, add as subview of parent component
var view: UIView { get }
init(disposedBy: DisposeBag)
// Subscribe to input streams, export streams produced by component
func start(_ inputs: Inputs) -> Outputs
}
extension ViewComponent {
// Stop any services started by the component
func stop() {}
}
Core UI logic is written in component protocol extensions for ease of testing. UI state changes are implemented as pure functions that operate on Rx types like Observable
. For example, the map style is a function of the current time at the map location. A light style is used when it’s day time at the map location, and a dark style when it’s night time.
extension MapComponent {
func mapStyle(forCameraPosition: Observable<GMSCameraPosition>,
date: Observable<Date>) -> Observable<MapStyle> {
let location = forCameraPosition.map(toLocation)
return uiScheme(forLocation: location, date: date)
.map { $0.style().mapStyle }
}
func uiScheme(forLocation: Observable<CLLocation>,
date: Observable<Date>) -> Observable<UIScheme> {
return Observable
.combineLatest(forLocation, date) { (l, d) in
if let dayTime = isDaytime(date: d, coordinate: l.coordinate) {
return dayTime ? .light : .dark
}
return .light
}
}
// ...
}
The source code for Here & Now is available on GitHub.