[SWIFT] URLSession with URLSession and Combine normally

Overview

I investigated how to write URLSession normally and how to write in Combine.

Subject

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.

Ordinary URLSession

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

URL Session with Combine

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

Specify a name that you do not know whether it is JsonDecoder or not.

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?
}

What is URLSession.shared?

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

What is element-> Data?

It just specifies the closure arguments. It works even if there is no -> Data part.

Closures — The Swift Programming Language (Swift 5.3)

I want to process the elements of the array one by one

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)")
})

the end

Recommended Posts

URLSession with URLSession and Combine normally
[macOS] Process Target-Action with Combine
Use java with MSYS and Cygwin
Distributed tracing with OpenCensus and Java
Install Java and Tomcat with Ansible
Item 32: Combine generics and varargs judiciously
Introducing Bootstrap with Webpacker and npm
Use Git with SourceTree and Eclipse
Use JDBC with Java and Scala.
Hello world with Kotlin and JavaFX
Writing code with classes and instances
Hello World with Docker and C
Processing speed with and without static
Output PDF and TIFF with Java 8
Data linkage with Spark and Cassandra
Drag and drop files with JavaFX
Copy and paste test with RSpec
View Slider with GWT and SmartGWT
Hello World with GWT 2.8.2 and Maven
Microservices With Docker and Cloud Performance
Encrypt with Java and decrypt with C #
Shape and serialize nicely with Jackson