I investigated how to write URLSession normally and how to write in Combine.
We will use Qiita's LGTM acquisition API as an example. Reference: Get LGTM and View of your article with Qiita API. --Qiita
Currently LGTM gets about 5 articles LGTM.
https://qiita.com/api/v2/items/a9ead7285d10aadf5643/likes
You can get it by hitting this URL with GET.
import Foundation
let qiitaURL = URL(string: "https://qiita.com/api/v2/items/a9ead7285d10aadf5643/likes")!
struct LGTM: Codable {
var created_at: String
var user: User
}
struct User: Codable {
var id: String
}
let session = URLSession.shared.dataTask(with: qiitaURL) {data, response, error in
if let error = error {
fatalError("Error: \(error.localizedDescription)")
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
fatalError("Error: invalid HTTP response code")
}
guard let data = data else {
fatalError("Error: missing response data")
}
do {
let lgtms = try JSONDecoder().decode([LGTM].self, from: data)
lgtms.forEach{ lgtm in
print("username: \(lgtm.user.id), created_at \(lgtm.created_at)")
}
}
catch {
print("Error: \(error.localizedDescription)")
}
}
session.resume()
The type of let session
is ʻURLSessionDataTask. ʻCommunicate by calling resume of URLSessionDataTask
.
The definition of the URLSession method dataTask is as follows.
open func dataTask(with url: URL, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask
The description in Combine is as follows.
import Foundation
import Combine
var cancellable = URLSession.shared
.dataTaskPublisher(for: qiitaURL)
.tryMap { element -> Data in
guard let httpResponse = element.response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return element.data
}
.decode(type: [LGTM].self, decoder: JSONDecoder())
.sink(receiveCompletion: {
print("complete! \($0)")
}, receiveValue: { lgtms in
lgtms.forEach{ lgtm in
print("username: \(lgtm.user.id), created_at \(lgtm.created_at)")
}
})
The processing has changed a little, but I feel that Combine has less processing in the completionHandler and has a better outlook.
Tips
If you specify something that is not in the Json field name, ʻError: The data could n’t be read because it is missing.` and JsonDecoder gives an error.
For example, if you define a struct as follows.
struct User: Codable {
var id: String
var aaa: String
}
In such a case, it is possible to prevent an error by entering nil
by setting the field name that may not exist to Optional.
struct User: Codable {
var id: String
var aaa: String?
}
URLSession.shared is a singleton to use when you want to write quickly. Since it works with the default behavior, advanced processing cannot be performed.
shared | Apple Developer Documentation
It just specifies the closure arguments.
It works even if there is no -> Data
part.
Closures — The Swift Programming Language (Swift 5.3)
In Combine, the URLSession subscribe process is currently nested by forEach.
.decode(type: [LGTM].self, decoder: JSONDecoder())
.sink(receiveValue: { lgtms in
lgtms.forEach{ lgtm in
print("username: \(lgtm.user.id), created_at \(lgtm.created_at)")
}
}
You can use flatMap
to get rid of this nesting.
.decode(type: [LGTM].self, decoder: JSONDecoder())
.flatMap {$0.publisher}
.sink(receiveCompletion: {
print("complete! \($0)")
}, receiveValue: { lgtm in
print("username: \(lgtm.user.id), created_at \(lgtm.created_at)")
})
Recommended Posts