Ahm*_*Els 19 api struct closures swift
大家好,我正在尝试制作一个简单且可重用的 Swift 网络层
也许这不是在视图中循环返回数据的最佳方法,但是在我尝试获取返回的 Api 数据以在 SwiftUI 视图中循环它之后,我收到错误:
Escaping closure captures mutating 'self' parameter
Run Code Online (Sandbox Code Playgroud)
不知道我在这节课中遗漏了什么地方或什么
这是文件的图片
内容视图.swift
struct ContentView: View {
var emptyDataArr: [CollectionItem] = []
init() {
ServiceLayer.request(router: Router.getSources) { (result: Result<[String : [CollectionItem]], Error>) in
switch result {
case .success(let data):
print(data)
self.emptyDataArr = data["custom_collections"]!
case .failure:
print(result)
}
}
}
var body: some View {
VStack (alignment: .leading) {
Text("No thing yet")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Run Code Online (Sandbox Code Playgroud)
服务层类 ServiceLayer.swift
class ServiceLayer {
// 1.
class func request<T: Codable>(router: Router, completion: @escaping (Result<[String: [T]], Error>) -> ()) {
// 2.
var components = URLComponents()
components.scheme = router.scheme
components.host = router.host
components.path = router.path
components.queryItems = router.parameters
// 3.
guard let url = components.url else { return }
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = router.method
// 4.
let session = URLSession(configuration: .default)
let dataTask = session.dataTask(with: urlRequest) { data, response, error in
// 5.
guard error == nil else {
completion(.failure(error!))
print(error?.localizedDescription)
return
}
guard response != nil else {
return
}
guard let data = data else {
return
}
print(data)
// 6.
let responseObject = try! JSONDecoder().decode([String: [T]].self, from: data)
// 7.
DispatchQueue.main.async {
// 8.
completion(.success(responseObject))
}
}
dataTask.resume()
}
}
Run Code Online (Sandbox Code Playgroud)
Rob*_*ier 23
问题在于它ContentView是一个结构体,这意味着它是一个值类型。你不能把它传递给一个闭包并改变它。如果你这样做了,什么都不会改变,因为闭包会有它自己独立的结构副本。
你的问题是你混合了你的视图和模型。一个给定的 View 可以有很多很多的副本(每次它传递给一个函数时,都会制作一个副本)。您不会希望这些副本中的每一个都发起请求。而是将这个请求逻辑移动到一个 Model 对象中,让 View 观察它。
我今天遇到了同样的问题,找到了这个帖子,然后我按照@Rob Napier的建议,最后做了一个工作示例。
希望以下代码可以提供帮助(注意:它不会直接回答您的问题,但我认为作为示例会有所帮助):
import Combine
import SwiftUI
import PlaygroundSupport
let url = URL(string: "https://source.unsplash.com/random")!
// performs a network request to fetch a random image from Unsplash’s public API
func imagePub() -> AnyPublisher<Image?, Never> {
URLSession.shared
.dataTaskPublisher(for: url)
.map { data, _ in Image(uiImage: UIImage(data: data)!)}
.print("image")
.replaceError(with: nil)
.eraseToAnyPublisher()
}
class ViewModel: ObservableObject {
// model
@Published var image: Image?
// simulate user taps on a button
let taps = PassthroughSubject<Void, Never>()
var subscriptions = Set<AnyCancellable>()
init() {
taps
// ?? map the tap to a new network request
.map { _ in imagePub() }
// ?? accept only the latest tap
.switchToLatest()
.assign(to: \.image, on: self)
.store(in: &subscriptions)
}
func getImage() {
taps.send()
}
}
struct ContentView: View {
// view model
@ObservedObject var viewModel = ViewModel()
var body: some View {
VStack {
viewModel.image?
.resizable().scaledToFit().frame(height: 400).border(Color.black)
Button(action: {
self.viewModel.getImage()
}, label: {
Text("Tap")
.padding().foregroundColor(.white)
.background(Color.pink).cornerRadius(12)
})
}.padding().background(Color.gray)
}
}
PlaygroundPage.current.setLiveView(ContentView())
Run Code Online (Sandbox Code Playgroud)
在点击按钮之前,ContentView看起来像这样:
点击按钮(并等待几秒钟)后,它看起来像这样:
所以我知道它正在工作^_^