Swift - 编写通用 HTTP 请求
Swift - Writing a generic HTTP request
因此,我正在尝试通过 Swift 的新 Decodable
协议使用一些普通的 HTTP 请求来检索一些数据。
问题是我发现我一次又一次地编写非常相似的代码(创建 URL 会话、解码数据、完成处理程序等)。
我正在考虑使用泛型或继承进行简化,但不确定如何初始化 'generic objects'。
当前代码:
var response = TypeAResponse()
if let url = URL(string: urlName) {
let session = URLSession.shared
var request = URLRequest(url: url)
let task = session.dataTask(with: request){ (data, response, error) in
if let data = data {
do {
response = try JSONDecoder().decode(TypeAResponse.self, from: data)
completion(true, TypeAResponse.items)
} catch let parseError {
print("parseError: \(parseError.localizedDescription)")
completion(false, TypeAResponse.items)
}
} else {
print("Error: Data not found")
completion(false, TypeAResponse.items)
}
}
task.resume()
} else {
print("Invalid URL")
completion(false, TypeAResponse.items)
}
但是 TypeAResponse 有不同的结构来表示 TypeBResponse - 它们共享的只是 'items' 属性。
可以做类似的事情
func get<T>(generalType: T, completion: GeneralRequest){
//but how would you initialise generalType?
//cannot do something like
var response = T()
}
作为参考,TypeAResponse 和 TypeBResponse 可能类似于:
struct TypeAResponse: Decodable {
items: [Track]?
}
struct Track: Decodable {
//for example
var name: String?
}
struct TypeBResponse: Decodable {
items: [Playlist]?
}
struct Playlist: Decodable {
var name, id, description: String?
}
API 回复:
请参阅 Spotify Web API 参考 - https://developer.spotify.com/documentation/web-api/reference/playlists/get-playlist/
(特别是“获取用户的播放列表”和“获取播放列表的曲目”)
您的字段名称必须完全匹配您正在解码的JSON字符串中的字段名称。希望以下简短示例有所帮助。根据您实际想要解码的程度,以下将能够解码来自 Spotify 的响应对象:
假设您的响应对象如下(取自Spotify's example):
{
"collaborative" : false,
"description" : "Having friends over for dinner? Here´s the perfect playlist.",
"external_urls" : {
"spotify" : "http://open.spotify.com/user/spotify/playlist/59ZbFPES4DQwEjBpWHzrtC"
},
"followers" : {
"href" : null,
"total" : 143350
},
"href" : "https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC",
"id" : "59ZbFPES4DQwEjBpWHzrtC",
"images" : [ {
"url" : "https://i.scdn.co/image/68b6a65573a55095e9c0c0c33a274b18e0422736"
} ],
"name" : "Dinner with Friends",
"owner" : {
"external_urls" : {
"spotify" : "http://open.spotify.com/user/spotify"
},
"href" : "https://api.spotify.com/v1/users/spotify",
"id" : "spotify",
"type" : "user",
"uri" : "spotify:user:spotify"
},
"public" : null,
"snapshot_id" : "bNLWdmhh+HDsbHzhckXeDC0uyKyg4FjPI/KEsKjAE526usnz2LxwgyBoMShVL+z+",
"tracks" : {
"href" : "https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks",
"items" : [ {
"added_at" : "2014-09-01T04:21:28Z",
"added_by" : {
"external_urls" : {
"spotify" : "http://open.spotify.com/user/spotify"
},
"href" : "https://api.spotify.com/v1/users/spotify",
"id" : "spotify",
"type" : "user",
"uri" : "spotify:user:spotify"
},
"is_local" : false,
"track" : {
"album" : {
"album_type" : "single",
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CH", "CL", "CO", "CR", "CY", "CZ", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "RO", "SE", "SG", "SI", "SK", "SV", "TR", "TW", "UY" ],
"external_urls" : {
"spotify" : "https://open.spotify.com/album/5GWoXPsTQylMuaZ84PC563"
},
"href" : "https://api.spotify.com/v1/albums/5GWoXPsTQylMuaZ84PC563",
"id" : "5GWoXPsTQylMuaZ84PC563",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/47421900e7534789603de84c03a40a826c058e45",
"width" : 640
}, {
"height" : 300,
"url" : "https://i.scdn.co/image/0d447b6faae870f890dc5780cc58d9afdbc36a1d",
"width" : 300
}, {
"height" : 64,
"url" : "https://i.scdn.co/image/d926b3e5f435ef3ac0874b1ff1571cf675b3ef3b",
"width" : 64
} ],
"name" : "I'm Not The Only One",
"type" : "album",
"uri" : "spotify:album:5GWoXPsTQylMuaZ84PC563"
},
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/2wY79sveU1sp5g7SokKOiI"
},
"href" : "https://api.spotify.com/v1/artists/2wY79sveU1sp5g7SokKOiI",
"id" : "2wY79sveU1sp5g7SokKOiI",
"name" : "Sam Smith",
"type" : "artist",
"uri" : "spotify:artist:2wY79sveU1sp5g7SokKOiI"
} ],
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CH", "CL", "CO", "CR", "CY", "CZ", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "RO", "SE", "SG", "SI", "SK", "SV", "TR", "TW", "UY" ],
"disc_number" : 1,
"duration_ms" : 204732,
"explicit" : false,
"external_ids" : {
"isrc" : "GBUM71403920"
},
"external_urls" : {
"spotify" : "https://open.spotify.com/track/4i9sYtSIlR80bxje5B3rUb"
},
"href" : "https://api.spotify.com/v1/tracks/4i9sYtSIlR80bxje5B3rUb",
"id" : "4i9sYtSIlR80bxje5B3rUb",
"name" : "I'm Not The Only One - Radio Edit",
"popularity" : 45,
"preview_url" : "https://p.scdn.co/mp3-preview/dd64cca26c69e93ea78f1fff2cc4889396bb6d2f",
"track_number" : 1,
"type" : "track",
"uri" : "spotify:track:4i9sYtSIlR80bxje5B3rUb"
}
}],
"limit" : 100,
"next" : "https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks?offset=100&limit=100",
"offset" : 0,
"previous" : null,
"total" : 105
},
"type" : "playlist",
"uri" : "spotify:user:spotify:playlist:59ZbFPES4DQwEjBpWHzrtC"
}
struct Playlist: Decodable {
var collaborative: Bool?
var description: String?
var external_urls: [String: String]?
var followers: FollowerData?
var href, id: String?
var images: [Image]?
var name: String?
var owner: User?
var `public`: String?
var snapshot_id: String?
var tracks: Track?
var type: String?
var uri: String?
}
struct FollowerData: Decodable {
var href: String?
var total: Int?
}
struct User: Decodable {
var external_urls: [String: String]?
var href, id, name, type, uri: String?
}
struct Track: Decodable {
var href: String?
var items: [TrackItem]?
var limit: Int?
var next: String?
var offset: Int?
var previous: String?
var total: Int?
}
struct TrackItem: Decodable {
var added_at: String?
var added_by: User?
var is_local: Bool?
var track: TrackDetails?
}
struct TrackDetails: Decodable {
var album: Album?
var artists: [User]?
var available_markets: [String]?
var disc_number: Int?
var duration_ms: Int?
var explicit: Bool?
var external_ids: [String: String]?
var external_urls: [String: String]?
var href, id, name: String?
var popularity: Int?
var preview_url: String?
var track_number: Int?
var type: String?
var uri: String?
}
struct Album: Decodable {
var album_type: String?
var available_markets: [String]?
var external_urls: [String: String]?
var href, id: String?
var images: [Image]?
}
struct Image: Decodable {
var width, height: Int?
var url: String?
}
let jsonString = "{ \"collaborative\" : false, \"description\" : \"Having friends over for dinner? Here´s the perfect playlist.\", \"external_urls\" : { \"spotify\" : \"http://open.spotify.com/user/spotify/playlist/59ZbFPES4DQwEjBpWHzrtC\" }, \"followers\" : { \"href\" : null, \"total\" : 143350 }, \"href\" : \"https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC\", \"id\" : \"59ZbFPES4DQwEjBpWHzrtC\", \"images\" : [ { \"url\" : \"https://i.scdn.co/image/68b6a65573a55095e9c0c0c33a274b18e0422736\" } ], \"name\" : \"Dinner with Friends\", \"owner\" : { \"external_urls\" : { \"spotify\" : \"http://open.spotify.com/user/spotify\" }, \"href\" : \"https://api.spotify.com/v1/users/spotify\", \"id\" : \"spotify\", \"type\" : \"user\", \"uri\" : \"spotify:user:spotify\" }, \"public\" : null, \"snapshot_id\" : \"bNLWdmhh+HDsbHzhckXeDC0uyKyg4FjPI/KEsKjAE526usnz2LxwgyBoMShVL+z+\", \"tracks\" : { \"href\" : \"https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks\", \"items\" : [ { \"added_at\" : \"2014-09-01T04:21:28Z\", \"added_by\" : { \"external_urls\" : { \"spotify\" : \"http://open.spotify.com/user/spotify\" }, \"href\" : \"https://api.spotify.com/v1/users/spotify\", \"id\" : \"spotify\", \"type\" : \"user\", \"uri\" : \"spotify:user:spotify\" }, \"is_local\" : false, \"track\" : { \"album\" : { \"album_type\" : \"single\", \"available_markets\" : [ \"AD\", \"AR\", \"AT\", \"AU\", \"BE\", \"BG\", \"BO\", \"BR\", \"CH\", \"CL\", \"CO\", \"CR\", \"CY\", \"CZ\", \"DK\", \"DO\", \"EC\", \"EE\", \"ES\", \"FI\", \"FR\", \"GB\", \"GR\", \"GT\", \"HK\", \"HN\", \"HU\", \"IE\", \"IS\", \"IT\", \"LI\", \"LT\", \"LU\", \"LV\", \"MC\", \"MT\", \"MY\", \"NI\", \"NL\", \"NO\", \"NZ\", \"PA\", \"PE\", \"PH\", \"PL\", \"PT\", \"PY\", \"RO\", \"SE\", \"SG\", \"SI\", \"SK\", \"SV\", \"TR\", \"TW\", \"UY\" ], \"external_urls\" : { \"spotify\" : \"https://open.spotify.com/album/5GWoXPsTQylMuaZ84PC563\" }, \"href\" : \"https://api.spotify.com/v1/albums/5GWoXPsTQylMuaZ84PC563\", \"id\" : \"5GWoXPsTQylMuaZ84PC563\", \"images\" : [ { \"height\" : 640, \"url\" : \"https://i.scdn.co/image/47421900e7534789603de84c03a40a826c058e45\", \"width\" : 640 }, { \"height\" : 300, \"url\" : \"https://i.scdn.co/image/0d447b6faae870f890dc5780cc58d9afdbc36a1d\", \"width\" : 300 }, { \"height\" : 64, \"url\" : \"https://i.scdn.co/image/d926b3e5f435ef3ac0874b1ff1571cf675b3ef3b\", \"width\" : 64 } ], \"name\" : \"I'm Not The Only One\", \"type\" : \"album\", \"uri\" : \"spotify:album:5GWoXPsTQylMuaZ84PC563\" }, \"artists\" : [ { \"external_urls\" : { \"spotify\" : \"https://open.spotify.com/artist/2wY79sveU1sp5g7SokKOiI\" }, \"href\" : \"https://api.spotify.com/v1/artists/2wY79sveU1sp5g7SokKOiI\", \"id\" : \"2wY79sveU1sp5g7SokKOiI\", \"name\" : \"Sam Smith\", \"type\" : \"artist\", \"uri\" : \"spotify:artist:2wY79sveU1sp5g7SokKOiI\" } ], \"available_markets\" : [ \"AD\", \"AR\", \"AT\", \"AU\", \"BE\", \"BG\", \"BO\", \"BR\", \"CH\", \"CL\", \"CO\", \"CR\", \"CY\", \"CZ\", \"DK\", \"DO\", \"EC\", \"EE\", \"ES\", \"FI\", \"FR\", \"GB\", \"GR\", \"GT\", \"HK\", \"HN\", \"HU\", \"IE\", \"IS\", \"IT\", \"LI\", \"LT\", \"LU\", \"LV\", \"MC\", \"MT\", \"MY\", \"NI\", \"NL\", \"NO\", \"NZ\", \"PA\", \"PE\", \"PH\", \"PL\", \"PT\", \"PY\", \"RO\", \"SE\", \"SG\", \"SI\", \"SK\", \"SV\", \"TR\", \"TW\", \"UY\" ], \"disc_number\" : 1, \"duration_ms\" : 204732, \"explicit\" : false, \"external_ids\" : { \"isrc\" : \"GBUM71403920\" }, \"external_urls\" : { \"spotify\" : \"https://open.spotify.com/track/4i9sYtSIlR80bxje5B3rUb\" }, \"href\" : \"https://api.spotify.com/v1/tracks/4i9sYtSIlR80bxje5B3rUb\", \"id\" : \"4i9sYtSIlR80bxje5B3rUb\", \"name\" : \"I'm Not The Only One - Radio Edit\", \"popularity\" : 45, \"preview_url\" : \"https://p.scdn.co/mp3-preview/dd64cca26c69e93ea78f1fff2cc4889396bb6d2f\", \"track_number\" : 1, \"type\" : \"track\", \"uri\" : \"spotify:track:4i9sYtSIlR80bxje5B3rUb\" } }], \"limit\" : 100, \"next\" : \"https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks?offset=100&limit=100\", \"offset\" : 0, \"previous\" : null, \"total\" : 105 }, \"type\" : \"playlist\", \"uri\" : \"spotify:user:spotify:playlist:59ZbFPES4DQwEjBpWHzrtC\"}"
if let data = jsonString.data(using: .utf8) {
let playlist = try? JSONDecoder().decode(Playlist.self, from: data)
print(playlist ?? "fail")
}
你在这里肯定是在一个很好的轨道上。从具体代码开始总是好的,然后看看如何使它更通用。对于您的具体情况,正如您所问 "how would you initialize generalType?" 这正是协议允许您做的。所以按照你的方法,你可以写类似的东西(我没有测试过这个;它可能有一些语法错误):
func get<T: Decodable>(generalType: T, completion: @escaping (Result<T, Error>) -> Void {
let task = session.dataTask(with: request){ (data, response, error) in
guard let data = data else {
let err = error ?? ... some default error ...
completion(.failure(err))
return
}
let result = Result {
// You know you can call `decode` with it because it's Decodable
try JSONDecoder().decode(T.self, from: data)
}
completion(result)
}
task.resume()
}
系列Start With A Protocol可能会有帮助(感谢提到它的人)。它对这个问题进行了更深入的探讨。但是对于简单的应用程序,这个功能可能就是您所需要的,在对您有用之前,您应该避免增加更多的复杂性。
因此,我正在尝试通过 Swift 的新 Decodable
协议使用一些普通的 HTTP 请求来检索一些数据。
问题是我发现我一次又一次地编写非常相似的代码(创建 URL 会话、解码数据、完成处理程序等)。
我正在考虑使用泛型或继承进行简化,但不确定如何初始化 'generic objects'。
当前代码:
var response = TypeAResponse()
if let url = URL(string: urlName) {
let session = URLSession.shared
var request = URLRequest(url: url)
let task = session.dataTask(with: request){ (data, response, error) in
if let data = data {
do {
response = try JSONDecoder().decode(TypeAResponse.self, from: data)
completion(true, TypeAResponse.items)
} catch let parseError {
print("parseError: \(parseError.localizedDescription)")
completion(false, TypeAResponse.items)
}
} else {
print("Error: Data not found")
completion(false, TypeAResponse.items)
}
}
task.resume()
} else {
print("Invalid URL")
completion(false, TypeAResponse.items)
}
但是 TypeAResponse 有不同的结构来表示 TypeBResponse - 它们共享的只是 'items' 属性。
可以做类似的事情
func get<T>(generalType: T, completion: GeneralRequest){
//but how would you initialise generalType?
//cannot do something like
var response = T()
}
作为参考,TypeAResponse 和 TypeBResponse 可能类似于:
struct TypeAResponse: Decodable {
items: [Track]?
}
struct Track: Decodable {
//for example
var name: String?
}
struct TypeBResponse: Decodable {
items: [Playlist]?
}
struct Playlist: Decodable {
var name, id, description: String?
}
API 回复:
请参阅 Spotify Web API 参考 - https://developer.spotify.com/documentation/web-api/reference/playlists/get-playlist/
(特别是“获取用户的播放列表”和“获取播放列表的曲目”)
您的字段名称必须完全匹配您正在解码的JSON字符串中的字段名称。希望以下简短示例有所帮助。根据您实际想要解码的程度,以下将能够解码来自 Spotify 的响应对象:
假设您的响应对象如下(取自Spotify's example):
{
"collaborative" : false,
"description" : "Having friends over for dinner? Here´s the perfect playlist.",
"external_urls" : {
"spotify" : "http://open.spotify.com/user/spotify/playlist/59ZbFPES4DQwEjBpWHzrtC"
},
"followers" : {
"href" : null,
"total" : 143350
},
"href" : "https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC",
"id" : "59ZbFPES4DQwEjBpWHzrtC",
"images" : [ {
"url" : "https://i.scdn.co/image/68b6a65573a55095e9c0c0c33a274b18e0422736"
} ],
"name" : "Dinner with Friends",
"owner" : {
"external_urls" : {
"spotify" : "http://open.spotify.com/user/spotify"
},
"href" : "https://api.spotify.com/v1/users/spotify",
"id" : "spotify",
"type" : "user",
"uri" : "spotify:user:spotify"
},
"public" : null,
"snapshot_id" : "bNLWdmhh+HDsbHzhckXeDC0uyKyg4FjPI/KEsKjAE526usnz2LxwgyBoMShVL+z+",
"tracks" : {
"href" : "https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks",
"items" : [ {
"added_at" : "2014-09-01T04:21:28Z",
"added_by" : {
"external_urls" : {
"spotify" : "http://open.spotify.com/user/spotify"
},
"href" : "https://api.spotify.com/v1/users/spotify",
"id" : "spotify",
"type" : "user",
"uri" : "spotify:user:spotify"
},
"is_local" : false,
"track" : {
"album" : {
"album_type" : "single",
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CH", "CL", "CO", "CR", "CY", "CZ", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "RO", "SE", "SG", "SI", "SK", "SV", "TR", "TW", "UY" ],
"external_urls" : {
"spotify" : "https://open.spotify.com/album/5GWoXPsTQylMuaZ84PC563"
},
"href" : "https://api.spotify.com/v1/albums/5GWoXPsTQylMuaZ84PC563",
"id" : "5GWoXPsTQylMuaZ84PC563",
"images" : [ {
"height" : 640,
"url" : "https://i.scdn.co/image/47421900e7534789603de84c03a40a826c058e45",
"width" : 640
}, {
"height" : 300,
"url" : "https://i.scdn.co/image/0d447b6faae870f890dc5780cc58d9afdbc36a1d",
"width" : 300
}, {
"height" : 64,
"url" : "https://i.scdn.co/image/d926b3e5f435ef3ac0874b1ff1571cf675b3ef3b",
"width" : 64
} ],
"name" : "I'm Not The Only One",
"type" : "album",
"uri" : "spotify:album:5GWoXPsTQylMuaZ84PC563"
},
"artists" : [ {
"external_urls" : {
"spotify" : "https://open.spotify.com/artist/2wY79sveU1sp5g7SokKOiI"
},
"href" : "https://api.spotify.com/v1/artists/2wY79sveU1sp5g7SokKOiI",
"id" : "2wY79sveU1sp5g7SokKOiI",
"name" : "Sam Smith",
"type" : "artist",
"uri" : "spotify:artist:2wY79sveU1sp5g7SokKOiI"
} ],
"available_markets" : [ "AD", "AR", "AT", "AU", "BE", "BG", "BO", "BR", "CH", "CL", "CO", "CR", "CY", "CZ", "DK", "DO", "EC", "EE", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MC", "MT", "MY", "NI", "NL", "NO", "NZ", "PA", "PE", "PH", "PL", "PT", "PY", "RO", "SE", "SG", "SI", "SK", "SV", "TR", "TW", "UY" ],
"disc_number" : 1,
"duration_ms" : 204732,
"explicit" : false,
"external_ids" : {
"isrc" : "GBUM71403920"
},
"external_urls" : {
"spotify" : "https://open.spotify.com/track/4i9sYtSIlR80bxje5B3rUb"
},
"href" : "https://api.spotify.com/v1/tracks/4i9sYtSIlR80bxje5B3rUb",
"id" : "4i9sYtSIlR80bxje5B3rUb",
"name" : "I'm Not The Only One - Radio Edit",
"popularity" : 45,
"preview_url" : "https://p.scdn.co/mp3-preview/dd64cca26c69e93ea78f1fff2cc4889396bb6d2f",
"track_number" : 1,
"type" : "track",
"uri" : "spotify:track:4i9sYtSIlR80bxje5B3rUb"
}
}],
"limit" : 100,
"next" : "https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks?offset=100&limit=100",
"offset" : 0,
"previous" : null,
"total" : 105
},
"type" : "playlist",
"uri" : "spotify:user:spotify:playlist:59ZbFPES4DQwEjBpWHzrtC"
}
struct Playlist: Decodable {
var collaborative: Bool?
var description: String?
var external_urls: [String: String]?
var followers: FollowerData?
var href, id: String?
var images: [Image]?
var name: String?
var owner: User?
var `public`: String?
var snapshot_id: String?
var tracks: Track?
var type: String?
var uri: String?
}
struct FollowerData: Decodable {
var href: String?
var total: Int?
}
struct User: Decodable {
var external_urls: [String: String]?
var href, id, name, type, uri: String?
}
struct Track: Decodable {
var href: String?
var items: [TrackItem]?
var limit: Int?
var next: String?
var offset: Int?
var previous: String?
var total: Int?
}
struct TrackItem: Decodable {
var added_at: String?
var added_by: User?
var is_local: Bool?
var track: TrackDetails?
}
struct TrackDetails: Decodable {
var album: Album?
var artists: [User]?
var available_markets: [String]?
var disc_number: Int?
var duration_ms: Int?
var explicit: Bool?
var external_ids: [String: String]?
var external_urls: [String: String]?
var href, id, name: String?
var popularity: Int?
var preview_url: String?
var track_number: Int?
var type: String?
var uri: String?
}
struct Album: Decodable {
var album_type: String?
var available_markets: [String]?
var external_urls: [String: String]?
var href, id: String?
var images: [Image]?
}
struct Image: Decodable {
var width, height: Int?
var url: String?
}
let jsonString = "{ \"collaborative\" : false, \"description\" : \"Having friends over for dinner? Here´s the perfect playlist.\", \"external_urls\" : { \"spotify\" : \"http://open.spotify.com/user/spotify/playlist/59ZbFPES4DQwEjBpWHzrtC\" }, \"followers\" : { \"href\" : null, \"total\" : 143350 }, \"href\" : \"https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC\", \"id\" : \"59ZbFPES4DQwEjBpWHzrtC\", \"images\" : [ { \"url\" : \"https://i.scdn.co/image/68b6a65573a55095e9c0c0c33a274b18e0422736\" } ], \"name\" : \"Dinner with Friends\", \"owner\" : { \"external_urls\" : { \"spotify\" : \"http://open.spotify.com/user/spotify\" }, \"href\" : \"https://api.spotify.com/v1/users/spotify\", \"id\" : \"spotify\", \"type\" : \"user\", \"uri\" : \"spotify:user:spotify\" }, \"public\" : null, \"snapshot_id\" : \"bNLWdmhh+HDsbHzhckXeDC0uyKyg4FjPI/KEsKjAE526usnz2LxwgyBoMShVL+z+\", \"tracks\" : { \"href\" : \"https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks\", \"items\" : [ { \"added_at\" : \"2014-09-01T04:21:28Z\", \"added_by\" : { \"external_urls\" : { \"spotify\" : \"http://open.spotify.com/user/spotify\" }, \"href\" : \"https://api.spotify.com/v1/users/spotify\", \"id\" : \"spotify\", \"type\" : \"user\", \"uri\" : \"spotify:user:spotify\" }, \"is_local\" : false, \"track\" : { \"album\" : { \"album_type\" : \"single\", \"available_markets\" : [ \"AD\", \"AR\", \"AT\", \"AU\", \"BE\", \"BG\", \"BO\", \"BR\", \"CH\", \"CL\", \"CO\", \"CR\", \"CY\", \"CZ\", \"DK\", \"DO\", \"EC\", \"EE\", \"ES\", \"FI\", \"FR\", \"GB\", \"GR\", \"GT\", \"HK\", \"HN\", \"HU\", \"IE\", \"IS\", \"IT\", \"LI\", \"LT\", \"LU\", \"LV\", \"MC\", \"MT\", \"MY\", \"NI\", \"NL\", \"NO\", \"NZ\", \"PA\", \"PE\", \"PH\", \"PL\", \"PT\", \"PY\", \"RO\", \"SE\", \"SG\", \"SI\", \"SK\", \"SV\", \"TR\", \"TW\", \"UY\" ], \"external_urls\" : { \"spotify\" : \"https://open.spotify.com/album/5GWoXPsTQylMuaZ84PC563\" }, \"href\" : \"https://api.spotify.com/v1/albums/5GWoXPsTQylMuaZ84PC563\", \"id\" : \"5GWoXPsTQylMuaZ84PC563\", \"images\" : [ { \"height\" : 640, \"url\" : \"https://i.scdn.co/image/47421900e7534789603de84c03a40a826c058e45\", \"width\" : 640 }, { \"height\" : 300, \"url\" : \"https://i.scdn.co/image/0d447b6faae870f890dc5780cc58d9afdbc36a1d\", \"width\" : 300 }, { \"height\" : 64, \"url\" : \"https://i.scdn.co/image/d926b3e5f435ef3ac0874b1ff1571cf675b3ef3b\", \"width\" : 64 } ], \"name\" : \"I'm Not The Only One\", \"type\" : \"album\", \"uri\" : \"spotify:album:5GWoXPsTQylMuaZ84PC563\" }, \"artists\" : [ { \"external_urls\" : { \"spotify\" : \"https://open.spotify.com/artist/2wY79sveU1sp5g7SokKOiI\" }, \"href\" : \"https://api.spotify.com/v1/artists/2wY79sveU1sp5g7SokKOiI\", \"id\" : \"2wY79sveU1sp5g7SokKOiI\", \"name\" : \"Sam Smith\", \"type\" : \"artist\", \"uri\" : \"spotify:artist:2wY79sveU1sp5g7SokKOiI\" } ], \"available_markets\" : [ \"AD\", \"AR\", \"AT\", \"AU\", \"BE\", \"BG\", \"BO\", \"BR\", \"CH\", \"CL\", \"CO\", \"CR\", \"CY\", \"CZ\", \"DK\", \"DO\", \"EC\", \"EE\", \"ES\", \"FI\", \"FR\", \"GB\", \"GR\", \"GT\", \"HK\", \"HN\", \"HU\", \"IE\", \"IS\", \"IT\", \"LI\", \"LT\", \"LU\", \"LV\", \"MC\", \"MT\", \"MY\", \"NI\", \"NL\", \"NO\", \"NZ\", \"PA\", \"PE\", \"PH\", \"PL\", \"PT\", \"PY\", \"RO\", \"SE\", \"SG\", \"SI\", \"SK\", \"SV\", \"TR\", \"TW\", \"UY\" ], \"disc_number\" : 1, \"duration_ms\" : 204732, \"explicit\" : false, \"external_ids\" : { \"isrc\" : \"GBUM71403920\" }, \"external_urls\" : { \"spotify\" : \"https://open.spotify.com/track/4i9sYtSIlR80bxje5B3rUb\" }, \"href\" : \"https://api.spotify.com/v1/tracks/4i9sYtSIlR80bxje5B3rUb\", \"id\" : \"4i9sYtSIlR80bxje5B3rUb\", \"name\" : \"I'm Not The Only One - Radio Edit\", \"popularity\" : 45, \"preview_url\" : \"https://p.scdn.co/mp3-preview/dd64cca26c69e93ea78f1fff2cc4889396bb6d2f\", \"track_number\" : 1, \"type\" : \"track\", \"uri\" : \"spotify:track:4i9sYtSIlR80bxje5B3rUb\" } }], \"limit\" : 100, \"next\" : \"https://api.spotify.com/v1/users/spotify/playlists/59ZbFPES4DQwEjBpWHzrtC/tracks?offset=100&limit=100\", \"offset\" : 0, \"previous\" : null, \"total\" : 105 }, \"type\" : \"playlist\", \"uri\" : \"spotify:user:spotify:playlist:59ZbFPES4DQwEjBpWHzrtC\"}"
if let data = jsonString.data(using: .utf8) {
let playlist = try? JSONDecoder().decode(Playlist.self, from: data)
print(playlist ?? "fail")
}
你在这里肯定是在一个很好的轨道上。从具体代码开始总是好的,然后看看如何使它更通用。对于您的具体情况,正如您所问 "how would you initialize generalType?" 这正是协议允许您做的。所以按照你的方法,你可以写类似的东西(我没有测试过这个;它可能有一些语法错误):
func get<T: Decodable>(generalType: T, completion: @escaping (Result<T, Error>) -> Void {
let task = session.dataTask(with: request){ (data, response, error) in
guard let data = data else {
let err = error ?? ... some default error ...
completion(.failure(err))
return
}
let result = Result {
// You know you can call `decode` with it because it's Decodable
try JSONDecoder().decode(T.self, from: data)
}
completion(result)
}
task.resume()
}
系列Start With A Protocol可能会有帮助(感谢提到它的人)。它对这个问题进行了更深入的探讨。但是对于简单的应用程序,这个功能可能就是您所需要的,在对您有用之前,您应该避免增加更多的复杂性。