Using Combine for URLSession in Swift — replacing RxSwift
This series is to illustrate the use of Swift Combine - the official reactive framework by Apple. As we have demonstrated using RxSwift to work with UITableView, this time we will transform it into Combine
. The source code of this tutorial is available on Github.
Let’s Get Started
We will start from creating another Xcode project, this time we will need CombineDataSource imported form Swift Package Manager. However, Combine is best used with SwiftUI in a MVVM pattern. This is only to replace the use of RxSwift
as demonstration purpose.
Initial set up
First, add CombineDataSource
to the Xcode project.
Xcode -> File -> Swift Packages -> Add Package Dependency -> Type in https://github.com/CombineCommunity/CombineDataSources.git
By importing Combine
and CombineDataSource
in the UIViewController
, then we can use the Combine syntaxes and bind to our UITableView
.
import Combine
import CombineDataSources
This time we also the same mock API response from https://api.myjson.com/bins/16w6h0 and declare the Place model.
struct Place: Decodable, Hashable {
let name: String
let desc: String
let url: String
}
Mock API response
[
{
"name": "Hong Kong",
"desc": "The place I live.",
"url": "https://en.wikipedia.org/wiki/Hong_Kong"
},
{
"name": "Singapore",
"desc": "A clean and disciplined place.",
"url": "https://en.wikipedia.org/wiki/Singapore"
},
{
"name": "Japan",
"desc": "Love those yummy sushi and sashimi.",
"url": "https://en.wikipedia.org/wiki/Japan"
}
]
Convert the code
We then declarePassthroughSubject
for array of place to catch the change. [AnyCancellable]()
allows us to bind the URLSession
dataTaskPublisher
with the UITableview
.
var places = PassthroughSubject<[Place], Never>()
var subscriptions = [AnyCancellable]()
And as we will implement the fetch data method, the value of the PassthroughSubject
will be updated accordingly.
func fetchData() {
if let url = URL(string: "https://api.myjson.com/bins/16w6h0") {
URLSession.shared.dataTaskPublisher(for: url)
.map { $0.data }
.decode(type: [Place].self, decoder: JSONDecoder())
.receive(on: RunLoop.main)
.sink(receiveCompletion: { completion in
print(completion) // finished
}) { place in
self.places.send(place)
}.store(in: &subscriptions)
}
}
The syntaxes then speaks for itself. An URLSession dataTaskPublisher
for the API is created and we extract the data
returned and decode with the Place
model. Received on Main Thread to update the UITableView
and the received value will send to thePassthroughSubject
.
And thePassthroughSubject
will be subscribed by UITableView’s row using the help of CombineDataSource.
places.bind(subscriber:
tableView.rowsSubscriber(
cellIdentifier: "cell",
cellType: UITableViewCell.self,
cellConfig: { cell, indexPath, model in
cell.textLabel?.text = "\(model.name), \(model.desc)"
}
)
)
.store(in: &subscriptions)
Then we can successfully achieve the same result using Combine
like the previous RxSwift example without RxSwift. But I have to mention once again this should be better used with SwiftUI so that we don’t even need to use CombineDataSource for extra dependency.
This is the end of this Combine
demonstration.
The source code is available on Github and hope you will find this useful :).
Cheers!