Created
June 11, 2019 17:13
-
-
Save shingohry/77f08a9a2a414164df8b1b00fcaba549 to your computer and use it in GitHub Desktop.
NetworkingWithCombine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
import Combine | |
/* | |
# References | |
- [ra1028/SwiftUI-Combine: This is an example project of SwiftUI and Combine using GitHub API.](https://github.com/ra1028/SwiftUI-Combine) | |
- [marty-suzuki/GitHubSearchWithSwiftUI: SwiftUI and Combine based GitHubSearch example.](https://github.com/marty-suzuki/GitHubSearchWithSwiftUI) | |
- [DataTaskPublisherを作ってみた](https://gist.github.com/yamoridon/16c1cc70ac46e50def4ca6695ceff772) | |
- [【iOS】Combineフレームワークまとめ(2019/6/9時点) - Qiita](https://qiita.com/shiz/items/5efac86479db77a52ccc) | |
*/ | |
// MARK: - ViewController | |
class ViewController: UIViewController { | |
let decorder: JSONDecoder = { | |
let decorder = JSONDecoder() | |
decorder.keyDecodingStrategy = .convertFromSnakeCase | |
return decorder | |
}() | |
var requestCancellable: Cancellable? | |
deinit { | |
requestCancellable?.cancel() | |
} | |
override func viewDidLoad() { | |
super.viewDidLoad() | |
request() | |
} | |
} | |
extension ViewController { | |
func request() { | |
let request = URLRequest(url: URL(string: "https://api.github.com/search/repositories?q=swift+combine")!) | |
requestCancellable = URLSession.shared.publisher(for: request) | |
.decode(type: Repositories.self, decoder: decorder) // decode to struct Repositories | |
.sink(receiveCompletion: { completion in // Attaches a subscriber with closure-based behavior. | |
switch completion { | |
case .finished: | |
print("finished") | |
case .failure(let error): | |
print("error:\(error.localizedDescription)") | |
} | |
}, receiveValue: { repositories in | |
print("repositories:\(repositories)") | |
}) | |
} | |
} | |
// MARK: - Publisher | |
struct RequestPublisher: Publisher { | |
typealias Output = Data | |
typealias Failure = RequestError | |
let session: URLSession | |
let request: URLRequest | |
// This function is called to attach the specified Subscriber to this Publisher by subscribe(_:) | |
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input { | |
let task = session.dataTask(with: request) { data, response, error in | |
DispatchQueue.main.async { | |
let httpReponse = response as? HTTPURLResponse | |
if let data = data, let httpReponse = httpReponse, 200..<300 ~= httpReponse.statusCode { | |
// Tells the subscriber that the publisher has produced an element. | |
_ = subscriber.receive(data) | |
// Tells the subscriber that the publisher has completed publishing. | |
subscriber.receive(completion: .finished) | |
} else if let httpReponse = httpReponse { | |
// Tells the subscriber that the publisher has completed publishing with a HTTP error. | |
let error = RequestError.http(code: httpReponse.statusCode, error: error) | |
subscriber.receive(completion: .failure(error)) | |
} else { | |
// Tells the subscriber that the publisher has completed publishing with a other error. | |
let error = RequestError.other | |
subscriber.receive(completion: .failure(error)) | |
} | |
} | |
} | |
// Tells the subscriber that it has successfully subscribed to the publisher and may request items. | |
let subscription = RequestSubscription(combineIdentifier: CombineIdentifier(), task: task) | |
subscriber.receive(subscription: subscription) | |
task.resume() | |
} | |
} | |
extension URLSession { | |
// return RequestPublisher | |
func publisher(for request: URLRequest) -> RequestPublisher { | |
return RequestPublisher(session: self, request: request) | |
} | |
} | |
struct RequestSubscription: Subscription { | |
let combineIdentifier: CombineIdentifier | |
let task: URLSessionTask | |
func request(_ demand: Subscribers.Demand) {} | |
func cancel() { | |
task.cancel() | |
} | |
} | |
// MARK: - Models | |
enum RequestError: Error { | |
case http(code: Int, error: Error?) | |
case other | |
} | |
struct Repositories: Codable { | |
let totalCount: Int | |
let items: [Repository] | |
} | |
struct Repository: Codable { | |
let id: Int | |
let name: String | |
let htmlUrl: String | |
} | |
extension JSONDecoder: TopLevelDecoder {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment