在将其分配给 Swift 中的变量之前等待网络请求的结果
Waiting for the result of a network request before assigning it to a variable in Swift
我想这个问题已经出现过很多次了,但我无法理解所有 GCD 和完成处理的内容。非常感谢提供一些指向有用文章的链接的快速修复。
我有一个函数可以从网络请求中获取和处理数据:
func getTracklist(album id: String) -> String {
//create a GET request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
guard let data = data else { return }
let decoder = JSONDecoder()
if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
tracks = jsonTracks.items!
}
//append the tracklist string
for track in tracks {
combinedTracks += "\(track.id)%2C"
}
}
task.resume()
return String(combinedTracks.dropLast(3))
}
返回值在另一个函数中使用:
formattedAlbum.trackList = self.getTracklist(album: album.id!)
您可能已经猜到了,第一个函数未能及时获取数据,因此将一个空字符串分配给了曲目列表。
使用 Alexey 的回答:
for album in self.albums {
let formattedAlbum = AlbumFormatted(context: self.persistenceManager.context)
formattedAlbum.albumName = album.name
self.getTracklist(album: album.id!) { (data) in
formattedAlbum.trackList = data
}
self.formattedAlbums.append(formattedAlbum)
print(formattedAlbum)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {
print(self.formattedAlbums[1])
}
func getTracklist(album id: String, completion: @escaping (String?)->()) {
//URLSession stuff
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
let decoder = JSONDecoder()
if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
tracks = jsonTracks.items!
for track in tracks {
combinedTracks += "\(track.id),"
}
completion(String(combinedTracks.dropLast()))
}
}
task.resume()
}
打印:
(entity: AlbumFormatted; id: 0x6000018fb400 <x-coredata:///AlbumFormatted/tFD0D9588-2820-451B-9A00-99175026ED043>; data: {
albumName = "Everyday Life";
trackList = "6Tb7Zfo4PcSiS4TqQ4NnTh,1e8D1BCD2afT56Km7UahpB,45PqOIkZ9PdCjsCJQYzx9G,1cXXhzPnbrXjNQYbLdUJdy,3pcPPhPAiurm2Ior11SHrz,7jib2tJjQ82kTIZZATMvAK,0ZlVUhjO8c0bOx1D2Btznf,0UvUivL70eDwhTWBd8S38I,6VzRvCbolqcUswaS";
})
我看到了使用闭包的解决方案:
只需在您的 ViewController 中测试以供参考。
override func viewDidLoad() {
super.viewDidLoad()
getTracklist { data in
print(data)
}
}
func getTracklist(completion: @escaping (Data?)->()) {
let request = URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos/1")!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// process errors here
completion(data) // here you return your decoded data (I omit your json, as we don't have a model)
}
task.resume()
}
UPD
阅读您的评论后:
for album in self.albums {
let formattedAlbum = AlbumFormatted(context: self.persistenceManager.context)
formattedAlbum.albumName = album.name
self.getTracklist(album: album.id!) { data in
print(data)
formattedAlbum.trackList = data
}
print(formattedAlbum.trackList) // will return you nil
// here you will not have results in formattedAlbum.trackList, because getTracklist method processes the data asynchronously
}
检查 this, that and this 个 SO 线程。
这是处理回调的一种合法方式:
func getTrackList(album id: String, completion: @escaping(String)->()){
//create a GET request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
guard let data = data else { return }
let decoder = JSONDecoder()
if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
tracks = jsonTracks.items!
}
//append the tracklist string
for track in tracks {
combinedTracks += "\(track.id)%2C"
}
completion(combinedTracks)
}
task.resume()
}
调用此函数时:
getTrackList(album: album.id) { [weak self] combinedTrack in
guard let self = self else {return}
self.formattedAlbum.trackList = combinedTrack
}
注意:如果请求失败或收到空 response/string
,您可能还想处理这些情况
我想这个问题已经出现过很多次了,但我无法理解所有 GCD 和完成处理的内容。非常感谢提供一些指向有用文章的链接的快速修复。
我有一个函数可以从网络请求中获取和处理数据:
func getTracklist(album id: String) -> String {
//create a GET request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
guard let data = data else { return }
let decoder = JSONDecoder()
if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
tracks = jsonTracks.items!
}
//append the tracklist string
for track in tracks {
combinedTracks += "\(track.id)%2C"
}
}
task.resume()
return String(combinedTracks.dropLast(3))
}
返回值在另一个函数中使用:
formattedAlbum.trackList = self.getTracklist(album: album.id!)
您可能已经猜到了,第一个函数未能及时获取数据,因此将一个空字符串分配给了曲目列表。
使用 Alexey 的回答:
for album in self.albums {
let formattedAlbum = AlbumFormatted(context: self.persistenceManager.context)
formattedAlbum.albumName = album.name
self.getTracklist(album: album.id!) { (data) in
formattedAlbum.trackList = data
}
self.formattedAlbums.append(formattedAlbum)
print(formattedAlbum)
}
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(5)) {
print(self.formattedAlbums[1])
}
func getTracklist(album id: String, completion: @escaping (String?)->()) {
//URLSession stuff
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
let decoder = JSONDecoder()
if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
tracks = jsonTracks.items!
for track in tracks {
combinedTracks += "\(track.id),"
}
completion(String(combinedTracks.dropLast()))
}
}
task.resume()
}
打印:
(entity: AlbumFormatted; id: 0x6000018fb400 <x-coredata:///AlbumFormatted/tFD0D9588-2820-451B-9A00-99175026ED043>; data: {
albumName = "Everyday Life";
trackList = "6Tb7Zfo4PcSiS4TqQ4NnTh,1e8D1BCD2afT56Km7UahpB,45PqOIkZ9PdCjsCJQYzx9G,1cXXhzPnbrXjNQYbLdUJdy,3pcPPhPAiurm2Ior11SHrz,7jib2tJjQ82kTIZZATMvAK,0ZlVUhjO8c0bOx1D2Btznf,0UvUivL70eDwhTWBd8S38I,6VzRvCbolqcUswaS";
})
我看到了使用闭包的解决方案: 只需在您的 ViewController 中测试以供参考。
override func viewDidLoad() {
super.viewDidLoad()
getTracklist { data in
print(data)
}
}
func getTracklist(completion: @escaping (Data?)->()) {
let request = URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/todos/1")!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// process errors here
completion(data) // here you return your decoded data (I omit your json, as we don't have a model)
}
task.resume()
}
UPD 阅读您的评论后:
for album in self.albums {
let formattedAlbum = AlbumFormatted(context: self.persistenceManager.context)
formattedAlbum.albumName = album.name
self.getTracklist(album: album.id!) { data in
print(data)
formattedAlbum.trackList = data
}
print(formattedAlbum.trackList) // will return you nil
// here you will not have results in formattedAlbum.trackList, because getTracklist method processes the data asynchronously
}
检查 this, that and this 个 SO 线程。
这是处理回调的一种合法方式:
func getTrackList(album id: String, completion: @escaping(String)->()){
//create a GET request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) -> Void in
guard let data = data else { return }
let decoder = JSONDecoder()
if let jsonTracks = try? decoder.decode(TrackRoot.self, from: data) {
tracks = jsonTracks.items!
}
//append the tracklist string
for track in tracks {
combinedTracks += "\(track.id)%2C"
}
completion(combinedTracks)
}
task.resume()
}
调用此函数时:
getTrackList(album: album.id) { [weak self] combinedTrack in
guard let self = self else {return}
self.formattedAlbum.trackList = combinedTrack
}
注意:如果请求失败或收到空 response/string
,您可能还想处理这些情况