例如,我有一个SwitUI ContentView。第一次创建项目时出现的那个。

import SwiftUI

struct ContentView: View {
   var manager = TestManager()
   var body: some View {
    ZStack{
        Color(.green)
            .edgesIgnoringSafeArea(.all)
        VStack {
            Text("Test Text")

            Button(action:{}) {
                Text("Get number 2")
                    .font(.title)
                    .foregroundColor(.white)
                .padding()
                .overlay(RoundedRectangle(cornerRadius: 30)
                .stroke(Color.white, lineWidth: 5))
                }
           }
       }
   }
}

我有一个可以处理Api调用的TestManager。我为具有两个功能的类做了一个委托。
protocol TestManagerDelegate {
    func didCorrectlyComplete(_ testName: TestManager, model: TestModel)
    func didFailWithError(_ error: Error)
}

struct TestManager {

    var delegate: TestManagerDelegate?
    let urlString = "http://numbersapi.com/2/trivia?json"

    func Get(){
        if let url = URL(string: urlString){

            let session = URLSession(configuration: .default)

            let task = session.dataTask(with: url) { (data, response, error) in
                if error != nil{
                    self.delegate?.didFailWithError(error!)
                    return
                }

                if let safeData = data{
                    if let parsedData = self.parseJson(safeData){
                        self.delegate?.didCorrectlyComplete(self, model: parsedData)
                    }
                }
            }
            task.resume()
        }
    }

   func parseJson(_ jsonData: Data) -> TestModel?{
       let decoder = JSONDecoder()
       do {
           let decodedData = try decoder.decode(TestModel.self,  from: jsonData)
           let mes = decodedData.message
           let model = TestModel(message: mes)
           return model

       } catch {
           delegate?.didFailWithError(error)
           return nil
       }
     }

  }

这是testModel数据类。只抓取了Json的文字返回。
struct TestModel :Decodable{
    let text: String
}

如何将TestManager连接到视图并让视图像在情节提要板上一样处理委托?

最佳答案

关于TestModel

Decodable协议(在您的上下文中)假设您使用所有通过JSON获得的属性来创建模型结构。请求http://numbersapi.com/2/trivia?json时,您将获得类似以下内容的信息:

{
 "text": "2 is the number of stars in a binary star system (a stellar system consisting of two stars orbiting around their center of mass).",
 "number": 2,
 "found": true,
 "type": "trivia"
}

这意味着您的模型应如下所示:

struct TestModel: Decodable {
    let text: String
    let number: Int
    let found: Bool
    let type: String
}

关于代表

在SwiftUI中,这种方法无法实现。相反,开发人员需要调整Combine框架的功能:属性包装@ObservedObject@PublishedObservableObject协议。
您想将逻辑放入某种结构中。坏消息是,(当前)ObservableObjectAnyObject协议(即Class-Only Protocol)。您需要将TestManager重写为以下类:

class TestManager: ObservableObject {
   // ...
}

只有这样,您才能使用@ObservedObject属性包装器在CurrentView中使用它:

struct ContentView: View {
    @ObservedObject var manager = TestManager()
    // ...
}

关于TestManager

现在,您的逻辑就这样排除了delegate,并且您需要使用TestModel将数据传递到CustomView。您可以通过使用@Published属性包装器添加新属性来修改TestManager:

class TestManager: ObservableObject {
    let urlString = "http://numbersapi.com/2/trivia?json"
    // 1
    @Published var model: TestModel?

    func get(){
        if let url = URL(string: urlString){
            let session = URLSession(configuration: .default)
            let task = session.dataTask(with: url) { [weak self] (data, response, error) in
                // 2
                DispatchQueue.main.async {
                    if let safeData = data {
                        if let parsedData = self?.parseJson(safeData) {
                            // 3
                            self?.model = parsedData
                        }
                    }
                }
            }
            task.resume()
        }
    }

    private func parseJson(_ jsonData: Data) -> TestModel? {
        let decoder = JSONDecoder()
        do {
            let decodedData = try decoder.decode(TestModel.self, from: jsonData)
            return decodedData
        } catch {
            return nil
        }
    }
}
  • 为了能够从外部访问模型,在您的情况下为ContentView
  • DispatchQueue.main.async{ }用于异步任务,因为Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
  • 只需使用您的解析模型。

  • 然后在ContentView中使用您的TestManager像这样:

    struct ContentView: View {
        @ObservedObject var manager = TestManager()
        var body: some View {
            ZStack{
                Color(.green)
                    .edgesIgnoringSafeArea(.all)
                VStack {
                    Text("Trivia is: \(self.manager.model?.text ?? "Unknown")")
                    Button(action:{ self.manager.get() }) {
                        Text("Get number 2")
                            .font(.title)
                            .foregroundColor(.white)
                            .padding()
                            .overlay(RoundedRectangle(cornerRadius: 30)
                                .stroke(Color.white, lineWidth: 5))
                    }
                }
            }
        }
    }
    

    关于HTTP

    您可以使用链接http://numbersapi.com/2/trivia?json,它是not allowed by Apple,请改用https,或者将App Transport Security Settings参数设置为Allow Arbitrary Loads into your Info.PlistYES键添加。但是请这样做very carefully,因为http链接根本无法工作。

    进一步的步骤

    您可以根据上述描述自行实现错误处理。

    完整代码(复制并粘贴):

    import SwiftUI
    
    struct ContentView: View {
        @ObservedObject var manager = TestManager()
        var body: some View {
            ZStack{
                Color(.green)
                    .edgesIgnoringSafeArea(.all)
                VStack {
                    Text("Trivia is: \(self.manager.model?.text ?? "Unknown")")
                    Button(action:{ self.manager.get() }) {
                        Text("Get number 2")
                            .font(.title)
                            .foregroundColor(.white)
                            .padding()
                            .overlay(RoundedRectangle(cornerRadius: 30)
                                .stroke(Color.white, lineWidth: 5))
                    }
                }
            }
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    
    class TestManager: ObservableObject {
        let urlString = "http://numbersapi.com/2/trivia?json"
        @Published var model: TestModel?
    
        func get(){
            if let url = URL(string: urlString){
                let session = URLSession(configuration: .default)
                let task = session.dataTask(with: url) { [weak self] (data, response, error) in
                    DispatchQueue.main.async {
                        if let safeData = data {
                            if let parsedData = self?.parseJson(safeData) {
                                self?.model = parsedData
                            }
                        }
                    }
                }
                task.resume()
            }
        }
    
        private func parseJson(_ jsonData: Data) -> TestModel? {
            let decoder = JSONDecoder()
            do {
                let decodedData = try decoder.decode(TestModel.self, from: jsonData)
                return decodedData
            } catch {
                return nil
            }
        }
    }
    
    struct TestModel: Decodable {
        let text: String
        let number: Int
        let found: Bool
        let type: String
    }
    

    10-08 16:22