我目前正在开发一个应用程序,我被一个涉及完成处理程序和async/await的问题卡住了。
我正在尝试上传多个文件到fire storage,并在一个简单的自定义结构中返回文件名和url:
struct PicData: Codable, Equatable {
let id: String
let link: URL
}
我用这个功能上传一张图片。我将完成返回从“Void”改为我的结构体,但这两个都给我的下一个函数带来了问题。
private func uploadPicture(picture: UIImage, completion: @escaping (PicData) -> PicData) async throws {
guard let data = picture.jpegData(compressionQuality: 1.0) else {
throw StorageError(message: "No data found in picture")
}
let fileName = UUID().uuidString + ".jpg"
let picRef = storage.child("photos").child(someId).child(fileName)
picRef.putData(data, metadata: nil) { (metadata, error) in
if let error = error {
print("Error while uploading file: ", error)
return
}
picRef.downloadURL(completion: { (url, error) in
if let theUrl = url {
completion(PicData(id:fileName, link:theUrl))
}
})
}
}
我使用下面的函数来上传多张图片。这是我从视图模型调用的主要上传功能-
func uploadPictures(_ pics: [UIImage], completion: @escaping ([PicData])->()) async throws {
try await withThrowingTaskGroup(of: PicData.self, body: { group in
for (pic) in pics {
group.addTask {
//let combo =
try await self.uploadPicture(picture: pic, completion: { (files) in
return files
})
//return combo
}
}
var fileNames: [PicData] = []
for try await fileName in group{
fileNames.append(PicData(id: fileName.id, link: fileName.link))
}
completion(fileNames)
})
}
在我的视图模型中,我试图在调用另一个函数之前等待数据:
func createNew(data: data, controller: UIViewController) async {
self.isLoading = true
Task{
do {
try await storageRepository.uploadPictures(data.pictures, completion: { pics in
Task {
do {
try await self.firestoreRepository.create(data: data, pictures: pics)
DispatchQueue.main.async {
self.isLoading = false // when this is published next view is shown.
}
}
}
})
} catch {
return
}
}
}
我花了很多时间试图搜索和弄清楚事情,我了解async/await和完成处理程序。我已经用不同的方法重写了我的函数多次,图像确实成功上传,但我的其他函数不会等待。我知道线程并没有等待响应,但是很难修复它。
其中一个主要的错误,我一直运行到其他比它不等待,是当我试图从一个函数的值-
// from function: uploadPictures
Cannot convert value of type '()' to closure result type 'PicData'
(That问题虽然可能是更好的小康作为它自己的问题)
我也试过使用信号量,尽管我的理解非常有限--
actor Datas {
var combos: [PicData] = []
func append(combo: PicData) {
combos.append(combo)
}
}
func runSema(pics: [UIImage]) async -> [PicData] {
let combo = Datas()
let queue = DispatchQueue(label: "com.app.myQueue", attributes: .concurrent)
let semaphore = DispatchSemaphore(value: pics.count)
for pic in pics {
queue.async {
semaphore.wait()
Task {
do {
let com = try await self.uploadPicture(picture: pic)
print(com)
await combo.append(combo: com)
} catch {
}
}
semaphore.signal()
}
}
return await combo.combos
}
private func uploadPicture(picture: UIImage) async throws -> PicData {
let semaphore = DispatchSemaphore(value: 1)
guard let data = picture.jpegData(compressionQuality: 1.0) else {
throw StorageError(message: "No data found in picture")
}
let fileName = UUID().uuidString + ".jpg"
let picRef = storage.child("users").child(userId!).child(fileName)
let metadata = StorageMetadata()
metadata.contentType = "image/jpg"
DispatchQueue.global().async {
semaphore.wait()
picRef.putData(data, metadata: metadata) { (metadata, error) in
if let error = error {
print("Error while uploading file: ", error)
// return
}
picRef.downloadURL { (url, error) in
guard let theUrls = url else { return }
semaphore.signal()
}
}
}
let theUrl = try await picRef.downloadURL() // trying to get url outside of closure
return(PicData(id: fileName, link: theUrl))
}
在我的模型中
func createNew(data: data, controller: UIViewController) {
self.isLoading = true
Task{
do {
let combos = await storageRepository.runSema(pics: profileData.pictures)
try await self.firestoreRepository.create(data: data, pictures: combos)
DispatchQueue.main.async {
self.isLoading = false // when this is published next view is shown.
}
} catch {
publishError(message: error.localizedDescription)
return
}
}
}
我的文件上传,但我不能让它等待,无论我已经尝试。我需要将文件上传到存储,然后将我的结构数据保存到Firestore DB。
我还尝试使用putDataAsync与代码我发现在另一个SO答案,但不断得到这个错误-
类型“StorageReference”的值没有成员“putDataAsync”
func someUpload(someImagesData: [Data]) async throws -> [String] {
// your path
let storageRef = storage.child("users").child(userId!).child("fileName")
return try await withThrowingTaskGroup(of: String.self) { group in
// the urlStrings you are eventually returning
var urlStrings = [String]()
// just using index for testing purposes so each image doesnt rewrites itself
// of course you can also use a hash or a custom id instead of the index
// looping over each image data and the index
for (index, data) in someImagesData.enumerated() {
// adding the method of storing our image and retriving the urlString to the task group
group.addTask(priority: .background) {
let _ = try await storageRef.putDataAsync(data)
return try await storageRef.downloadURL().absoluteString
}
}
for try await uploadedPhotoString in group {
// for each task in the group, add the newly uploaded urlSting to our array...
urlStrings.append(uploadedPhotoString)
}
// ... and lastly returning it
return urlStrings
}
}
在这一点上,我给予了,任何想法,我在这方面的错误?
1条答案
按热度按时间uubf1zoe1#
感谢@loremipsum的评论,它引导我朝着正确的方向去解决它。
首先,我尝试通过
File -> Packages -> Update..
更新我的包,清理构建,重置包缓存,解析版本,关闭Xcode,重建等。但什么都没用我所要做的就是删除Firebase iOS SDK,然后添加一个全新的安装。这样做之后,出现了很多原本不存在的错误,之前没有造成任何问题。例如强制解包和guard let语句,我还必须在一些firebase调用中添加try await。我现在可以使用
putDataAsync
。使用@loremipsum提供的链接,我能够修改我的代码并使其工作-
然后在我的视图模型中,我可以使用try await调用,这次它实际上是在等待。