Reputation: 1151
Playing around in playground with protocol oriented networking code and on the last line where I bring everything together, Xcode crashes by saying
LLDB RPC server has crashed... and so on
There must be something terrifically wrong in my code and I'm sure it's on the last couple of lines but I can't figure it out.
import UIKit
import PlaygroundSupport
struct Repo {
let id: Int
let name: String
}
extension Repo {
init?(dict: JSONDict) {
guard let id = dict["id"] as? Int, let name = dict["name"] as? String else { return nil }
self.id = id
self.name = name
}
}
typealias JSONDict = [String:Any]
protocol Resource {
associatedtype Content
var url: URL { get }
func parse(data: Data) -> Content?
}
extension Resource {
func parseJSON(data: Data) -> Any? {
let json = try? JSONSerialization.jsonObject(with: data)
return json
}
}
struct RepoService: Resource {
let url = URL(string: "https://api.github.com/users/mkchoi212/repos")!
func parse(data: Data) -> [Repo]? {
guard let dictArray = self.parseJSON(data: data) as? [JSONDict] else { return nil }
return dictArray.flatMap(Repo.init)
}
}
final class WebService<Content> {
func load<R: Resource>(resource: R, completion: @escaping (R.Content?) -> ()) {
URLSession.shared.dataTask(with: resource.url) { data, _, _ in
let res = data.flatMap(resource.parse)
completion(res)
}.resume()
}
}
WebService().load(resource: RepoService) { res in
for elem in res {
print(elem)
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
Upvotes: 2
Views: 216
Reputation: 17544
working example
import Foundation
import PlaygroundSupport
struct Repo {
let id: Int
let name: String
}
extension Repo {
init?(dict: JSONDict) {
guard let id = dict["id"] as? Int, let name = dict["name"] as? String else { return nil }
self.id = id
self.name = name
}
}
typealias JSONDict = [String:Any]
protocol Resource {
associatedtype Content
var url: URL { get }
func parse(data: Data) -> Content?
}
extension Resource {
func parseJSON(data: Data) -> Any? {
let json = try? JSONSerialization.jsonObject(with: data)
return json
}
}
struct RepoService: Resource {
let url = URL(string: "https://api.github.com/users/mkchoi212/repos")!
func parse(data: Data) -> [Repo]? {
guard let dictArray = self.parseJSON(data: data) as? [JSONDict] else { return nil }
return dictArray.flatMap(Repo.init)
}
}
final class WebService {
func load<R: Resource>(resource: R, completion: @escaping (R.Content) -> ()) {
URLSession.shared.dataTask(with: resource.url) { data, _, _ in
if let res = data.flatMap(resource.parse) {
completion(res)
}
}.resume()
}
}
WebService().load(resource: RepoService()) { res in
for elem in res {
print(elem)
}
}
PlaygroundPage.current.needsIndefiniteExecution = true
gave me this result
Repo(id: 93473150, name: "bitbuf")
Repo(id: 28517340, name: "DIJKASTRAS")
Repo(id: 71310619, name: "git-strbuf")
Repo(id: 30558002, name: "Habitats-master")
Repo(id: 28517464, name: "Home-Automation-Project")
Repo(id: 33394787, name: "hw.")
Repo(id: 39911528, name: "JPSThumbnailAnnotation")
Repo(id: 37162894, name: "M3")
Repo(id: 39709018, name: "Maroon")
Repo(id: 41267183, name: "mkchoi212.github.io")
Repo(id: 28519262, name: "Money-Saving-Meals")
Repo(id: 39857111, name: "MRCircularProgressView")
Repo(id: 81773538, name: "papers-we-love")
Repo(id: 37074317, name: "socket.io-client-swift")
Repo(id: 28519322, name: "Wordsneak")
Upvotes: 0
Reputation: 539815
A crashing compiler is a bug and should be reported. Xcode 9 (beta) does not crash with your code but gives helpful error messages leading to a solution:
error: generic parameter 'Content' could not be inferred WebService().load(resource: RepoService) { res in ^
The generic placeholder Content
in class WebService<Content>
is
not used at all, remove it and make the class non-generic. Then:
error: generic parameter 'R' could not be inferred WebService().load(resource: RepoService) { res in ^
You have to pass an instance of RepoService
to the load
method,
and res
inside the closure is an optional.
With these changes
final class WebService {
func load<R: Resource>(resource: R, completion: @escaping (R.Content?) -> ()) {
URLSession.shared.dataTask(with: resource.url) { data, _, _ in
let res = data.flatMap(resource.parse)
completion(res)
}.resume()
}
}
WebService().load(resource: RepoService()) { res in
guard let res = res else { return }
for elem in res {
print(elem)
}
}
your code compiles and runs as expected, in Xcode 8.3.3 and 9.
Upvotes: 4