Before WWDC 2019
Apple has always advertised MVC as the preferred architecture to develop iOS apps. So the community comes up with different alternative architectures to solve the “big view controllers” problem. MVVM is one of the most popular architecture used by iOS developers. Before WWDC 2019, most of MVVM implementations use some popular reactive framework such as RxSwift, ReactiveSwift or ReactiveKit along with their UI binding counterparts such as RxCocoa, ReactiveCocoa or Bond.
After WWDC 2019
In WWDC 2019, Apple has introduced two awesome frameworks which replace the above frameworks to provide reactive programming and UI bindings. From now on, developers can implement MVVM architecture for their apps using only native frameworks which provide many benefit such as stability, binary app size and consistent APIs etc…
Apple seems also to go away from MVC and adopt MVVM as the default architecture for apps development.
With SwiftUI, you don’t have to decide if you want to use visual tools or writting code to design, you can have both at the same time.
Some WWDC videos related to this topic:
- Integrating SwiftUI: https://developer.apple.com/videos/play/wwdc2019/231/
- Combine in Practice: https://developer.apple.com/videos/play/wwdc2019/721/
- Mastering Xcode Previews: https://developer.apple.com/videos/play/wwdc2019/233/
MVVM using Swift and Combine
I’ve created a simple project to demonstrate how we can use those native frameworks to implement MVVM architecture for iOS apps. The code is here on github: https://github.com/antranapp/PixabaySwiftUICombine/
This project demonstrates how to implement MVVM using SwiftUI and Combine to search and display images from pixabay.com. There are also some tests to demonstrate the testability of the implementation.
The View
is written using SwiftUI framework. Each View
has its own ViewModel
which is responsible to call a Service
to fetch data DataModel
from the remote server:
// View Model
func search(withTerm term: String) {
self.isActive = true
let dataUpdater = AnySubscriber<ImageListModel, Error>(
receiveValue: { [weak self] imageListModel -> Subscribers.Demand in
self?.images = imageListModel.hits
return .unlimited
},
receiveCompletion: { [weak self] completion in
if case let .failure(error) = completion {
print(error)
self?.images = []
}
self?.isActive = false
}
)
pixaBayService.fetch(searchTerm: term).subscribe(dataUpdater)
}
In View
, ViewModel
is declared as ObjectBinding
to provide binding functionality, so that the View
gets informed when ViewModel
gets new data from the Service
:
@ObjectBinding var viewModel = ImageListViewModel(pixaBayService: PixaBayService())
The Service
uses Combine
, specifically Future
to return data from an asynchronous call:
func fetch(searchTerm: String) -> Publishers.Future<ImageListModel, Error> {
let urlString = "https://pixabay.com/api/?key=107764-f19c20d5ca4d545d9b0a09de3&q=\(searchTerm)&image_type=photo&pretty=true"
let url = URL(string: urlString)!
return Publishers.Future { resolver in
URLSession.shared.dataTask(with: url) { data, _, error in
guard error == nil else {
return resolver(Result.failure(error!))
}
guard let data = data else {
return resolver(Result.failure(ServiceNetworkError.noData))
}
do {
let decoder = JSONDecoder()
let imageData = try decoder.decode(ImageListModel.self, from: data)
return resolver(Result.success(imageData))
} catch {
return resolver(Result.failure(error))
}
}.resume()
}
}
Conclusion
SwiftUI
and Combine
bring great foundations to build iOS apps with MVVM architecture. The best benifit is that you don’t have to rely on 3rd-party frameworks for your implementations. Native frameworks are always the best choices for awesome apps.
The code is open-sourced on Github