如何使用 Swift 完成处理程序的结果?
How to use results from Swift completion handler?
我是 Swift 和 SwiftUI 的新手。
在我的 macOS SwiftUI 项目中,我试图验证 URL 是否可以访问,以便我可以有条件地呈现两个视图之一。一种加载图像 URL 的视图,另一种视图在 URL 无法访问时显示错误图像。
这是我的 URL 完成的扩展:
import Foundation
extension URL {
func isReachable(completion: @escaping (Bool) -> Void) {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
request.timeoutInterval = 1.0
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
completion(false)
}
return
}
if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
DispatchQueue.main.async {
completion(httpResp.statusCode == 200)
}
return
} else {
DispatchQueue.main.async {
completion(false)
}
return
}
}.resume()
}
}
在其他地方,我正在尝试在模型视图中使用它:
var imageURL: URL? {
if let url = self.book.image_url {
return URL(string: url)
} else {
return nil
}
}
var imageURLIsReachable: Bool {
if let url = self.imageURL {
url.isReachable { result in
return result // Error: Cannot convert value of type 'Bool' to closure result type 'Void'
}
} else {
return false
}
}
尽管 Xcode 显示此错误:
Cannot convert value of type 'Bool' to closure result type 'Void'
我做错了什么?
正如 Xcode 告诉您的那样,问题确实存在于行 return result
中。当您创建函数 func isReachable(completion: @escaping (Bool) -> Void)
时,您是在告诉 Xcode 您将输入 (Bool) -> Void
类型的内容,应该类似于 func someFunction(input: Bool) -> Void
.
但是当您使用闭包输入完成处理程序时,您输入的是 Bool -> Bool
类型的函数。删除行 return result
,或更改 func isReachable(completion:)
.
中的完成类型
编辑:
事实上,我不建议在计算 属性 中返回异步结果,那会导致一些其他问题。
我会将其更改为:
func isReachable(completion: @esacping (Bool) -> Void) {
...
}
func showResultView() {
guard let url = imageURL else {
// handling if the imageURL is nil
return
}
url.isReachable { result in
// do something with the result
if result {
// show viewController A
} else {
// show viewController B
}
}
}
// call showResultView anywhere you want, lets say you want to show it whenever the viewController appear
override func viewDidAppear() {
...
showResultView()
}
在阅读了此处的一些评论并做了更多 research/experimentation 后,我开始使用它。这是我更改的内容:
在 URL 扩展中,我保留了几乎相同的内容,因为我发现这样更易读。我确实将 timeoutInterval
推送到参数:
// Extensions/URL.swift
import Foundation
extension URL {
func isReachable(timeoutInterval: Double, completion: @escaping (Bool) -> Void) {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
request.timeoutInterval = timeoutInterval
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
completion(false)
}
return
}
if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
DispatchQueue.main.async {
completion(httpResp.statusCode == 200)
}
return
} else {
DispatchQueue.main.async {
completion(false)
}
return
}
}.resume()
}
}
我修改了我的 BookViewModel
使两个属性成为 @Published
并在那里使用了 URL 扩展:
// View Models/BookViewModel.swift
import Foundation
class BookViewModel: ObservableObject {
@Published var book: Book
@Published var imageURLIsReachable: Bool
@Published var imageURL: URL?
init(book: Book) {
self.book = book
self.imageURL = nil
self.imageURLIsReachable = false
if let url = book.image_url {
self.imageURL = URL(string: url)
self.imageURL!.isReachable(timeoutInterval: 1.0) { result in
self.imageURLIsReachable = result
}
}
}
// Rest of properties...
}
现在我的 BookThumbnailView
可以正确显示条件视图:
// Views/BookThumbnailView.swift
import SwiftUI
import Foundation
import KingfisherSwiftUI
struct BookThumbnailView: View {
@ObservedObject var viewModel: BookViewModel
private var book: Book {
viewModel.book
}
@ViewBuilder
var body: some View {
if let imageURL = self.viewModel.imageURL {
if self.viewModel.imageURLIsReachable {
KFImage(imageURL)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 70)
.cornerRadius(8)
} else {
ErrorBookThumbnailView()
}
} else {
DefaultBookThumbnailView()
}
}
}
哇,那真是一次学习经历。感谢所有发表评论并提供建议的人!
我是 Swift 和 SwiftUI 的新手。
在我的 macOS SwiftUI 项目中,我试图验证 URL 是否可以访问,以便我可以有条件地呈现两个视图之一。一种加载图像 URL 的视图,另一种视图在 URL 无法访问时显示错误图像。
这是我的 URL 完成的扩展:
import Foundation
extension URL {
func isReachable(completion: @escaping (Bool) -> Void) {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
request.timeoutInterval = 1.0
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
completion(false)
}
return
}
if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
DispatchQueue.main.async {
completion(httpResp.statusCode == 200)
}
return
} else {
DispatchQueue.main.async {
completion(false)
}
return
}
}.resume()
}
}
在其他地方,我正在尝试在模型视图中使用它:
var imageURL: URL? {
if let url = self.book.image_url {
return URL(string: url)
} else {
return nil
}
}
var imageURLIsReachable: Bool {
if let url = self.imageURL {
url.isReachable { result in
return result // Error: Cannot convert value of type 'Bool' to closure result type 'Void'
}
} else {
return false
}
}
尽管 Xcode 显示此错误:
Cannot convert value of type 'Bool' to closure result type 'Void'
我做错了什么?
正如 Xcode 告诉您的那样,问题确实存在于行 return result
中。当您创建函数 func isReachable(completion: @escaping (Bool) -> Void)
时,您是在告诉 Xcode 您将输入 (Bool) -> Void
类型的内容,应该类似于 func someFunction(input: Bool) -> Void
.
但是当您使用闭包输入完成处理程序时,您输入的是 Bool -> Bool
类型的函数。删除行 return result
,或更改 func isReachable(completion:)
.
编辑:
事实上,我不建议在计算 属性 中返回异步结果,那会导致一些其他问题。
我会将其更改为:
func isReachable(completion: @esacping (Bool) -> Void) {
...
}
func showResultView() {
guard let url = imageURL else {
// handling if the imageURL is nil
return
}
url.isReachable { result in
// do something with the result
if result {
// show viewController A
} else {
// show viewController B
}
}
}
// call showResultView anywhere you want, lets say you want to show it whenever the viewController appear
override func viewDidAppear() {
...
showResultView()
}
在阅读了此处的一些评论并做了更多 research/experimentation 后,我开始使用它。这是我更改的内容:
在 URL 扩展中,我保留了几乎相同的内容,因为我发现这样更易读。我确实将 timeoutInterval
推送到参数:
// Extensions/URL.swift
import Foundation
extension URL {
func isReachable(timeoutInterval: Double, completion: @escaping (Bool) -> Void) {
var request = URLRequest(url: self)
request.httpMethod = "HEAD"
request.timeoutInterval = timeoutInterval
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
completion(false)
}
return
}
if let httpResp: HTTPURLResponse = response as? HTTPURLResponse {
DispatchQueue.main.async {
completion(httpResp.statusCode == 200)
}
return
} else {
DispatchQueue.main.async {
completion(false)
}
return
}
}.resume()
}
}
我修改了我的 BookViewModel
使两个属性成为 @Published
并在那里使用了 URL 扩展:
// View Models/BookViewModel.swift
import Foundation
class BookViewModel: ObservableObject {
@Published var book: Book
@Published var imageURLIsReachable: Bool
@Published var imageURL: URL?
init(book: Book) {
self.book = book
self.imageURL = nil
self.imageURLIsReachable = false
if let url = book.image_url {
self.imageURL = URL(string: url)
self.imageURL!.isReachable(timeoutInterval: 1.0) { result in
self.imageURLIsReachable = result
}
}
}
// Rest of properties...
}
现在我的 BookThumbnailView
可以正确显示条件视图:
// Views/BookThumbnailView.swift
import SwiftUI
import Foundation
import KingfisherSwiftUI
struct BookThumbnailView: View {
@ObservedObject var viewModel: BookViewModel
private var book: Book {
viewModel.book
}
@ViewBuilder
var body: some View {
if let imageURL = self.viewModel.imageURL {
if self.viewModel.imageURLIsReachable {
KFImage(imageURL)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 70)
.cornerRadius(8)
} else {
ErrorBookThumbnailView()
}
} else {
DefaultBookThumbnailView()
}
}
}
哇,那真是一次学习经历。感谢所有发表评论并提供建议的人!