iOS:如何从 REST SwiftUi 访问和使用 API 响应数据

Posted

技术标签:

【中文标题】iOS:如何从 REST SwiftUi 访问和使用 API 响应数据【英文标题】:iOS: How to access and use API response data from REST SwiftUi 【发布时间】:2021-06-12 16:09:30 【问题描述】:

首先,我对 ios 开发和 Swift 非常陌生(从 php 来到这里 2 周 :))

我正在创建一个对我非常简单的 api 进行标注的应用程序。我正在使用下面的代码,但无法访问代码的“if let response”部分,也无法从 api 返回的数据中获取“Comments”的值。想知道是否有人可以提供帮助。

数据恢复正常

//Print out fine
print(str)

将打印我对"Comments":"success"的回复

所以只是想知道如何正确使用这些数据并检查是否成功等

谢谢


        func loadData() 
            guard let url = URL(string: "myapi.php") else 
                print("Your API end point is Invalid")
                return
            
            let request = URLRequest(url: url)
            print("hello1")
    
            URLSession.shared.dataTask(with: request)  data, response, error in
                if let data = data 
                    print("hello2")
                    let str = String(decoding: data, as: UTF8.self)

                    //Print out fine
                    print(str)
    
                    //  Cannot enter this if statement
                    if let response = try? JSONDecoder().decode([TaskEntry].self, from: data) 
                        DispatchQueue.main.async 
                            print("hello3")
    
                            print(response)
                        
                        return
                    
                
            .resume()
        
        
    struct TaskEntry: Codable 
        public var Comments: String
    

【问题讨论】:

不要尝试? , 使用 do 和 catch 得到确切的错误。 正如已经提到的catch errorprint 它。 DecodingErrors 非常具有描述性。顺便说一句,print(str) 清楚地表明根对象不是数组。与 PHP 不同,Swift 中的数组和字典是完全不同的类型。 正如 vadian 提到的,你应该有 TaskEntry.self 而不是 [TaskEntry].self。 @TusharSharma,vadian,感谢您的帮助,根据您的帮助设法使其正常工作 【参考方案1】:

根据您提供的描述,返回您的 API 的 JSON 如下:

 
    "Comments": "success"

是吗?

如果是,这部分代码

if let response = try? JSONDecoder().decode([TaskEntry].self, from: data)

标记您将从应用程序获取的数据解码为数组。你在这里为你的数据不正确的数据指定一个TaskEntry数组[TaskEntry].self,你需要用 try? JSONDecoder().decode(TaskEntry.self, from: data)解码json

如果你希望一个数组,你的 api 必须返回这样的东西

[
  
    "Comments": "success"
  
]

为了检查状态码是否正确,我提供了对您的代码进行解释的重构。

首先,Codable 协议用于通过 Encoders 和 Decoders 对数据进行解码和编码。在这种情况下,您正在使用 JSON,因此您使用 JSONDecoder 来解码数据。你只需要解码,所以使用 Decodable 协议就足够了。您不需要将 Comments 标记为 public,因为您在项目中使用 TaskEntry,因此默认情况下(未指定)它是内部的。此外,您不需要 var,因为您没有更改属性。 此外,作为标准的属性以小写字母开头,因此您可以使用 CodingKeys 来保持质量代码标准。

您可以在Apple Developer site查看它

struct TaskEntry: Decodable 
    let comments: String

    enum CodingKeys: String, CodingKey 
        case comments = "Comments"
    

然后,您应该考虑您的 Api 错误并对其进行定义。我举个例子:

enum NetworkErrors: Error 
    case responseIsNotHTTP
    case noDataProvided
    case unknown

您的加载函数,应该与外部类或构造TaskEntry 或错误进行通信。您可以使用带有 Result 的闭包。请查看链接Hacking with Swift。这有两个具有关联值的案例,失败和成功。

还需要将 UrlSessionTask 的响应转换为 HTTPURLResponse 以获取响应 http 状态代码。我用你的代码提供了一个例子。

func loadData(result: @escaping (Result<TaskEntry, Error>) -> Void) 
    guard let url = URL(string: "myapi.php") else 
        result(.failure(URLError(.badURL)))
        return
    

    let request = URLRequest(url: url) // your method is Get, if not you need to set the http method of the request
    URLSession.shared.dataTask(with: request)  data, response, error in

        guard let response = response as? HTTPURLResponse else 
            if let error = error 
                print(error.localizedDescription)
                result(.failure(error))

              else 
                result(.failure(NetworkErrors.responseIsNotHTTP))
            
            return
        

        switch response.statusCode 
            case 200...299:
                if let data = data 
                    do 
                        let taskEntry = try JSONDecoder().decode(TaskEntry.self, from: data)
                        result(.success(taskEntry))
                     catch 
                        result(.failure(error))
                    
                 else 
                    result(.failure(NetworkErrors.noDataProvided))
                

            default:
                if let error = error 
                    print(error.localizedDescription)
                    result(.failure(error))

                  else 
                    result(.failure(NetworkErrors.unknown))
                

        
    .resume()


此外,您可以使用 iOS 15 或 MacOS 12 中的新功能将此代码传递给 async/await 表单(async/await 这是 swift 5.5 的功能,但我使用的 urlsession 功能仅来自 ios 15和 macOS 12 以上):

@available(macOS 12.0, iOS 15.0, *)
func loadData() async throws -> Result<TaskEntry,Error> 
    guard let url = URL(string: "myapi.php") else 
        throw URLError(.badURL)
    

    do 
        let (data, urlResponse) = try await URLSession.shared.data(from: url, delegate: nil)

        guard let response = urlResponse as? HTTPURLResponse else 
            return .failure(NetworkErrors.responseIsNotHTTP)
        

        switch response.statusCode 
            case 200...299:
                do 
                    let taskEntry = try JSONDecoder().decode(TaskEntry.self, from: data)
                    return .success(taskEntry)
                 catch 
                    return .failure(error)
                

            default:
                return .failure(NetworkErrors.unknown)

        
     catch 
        return .failure(error)
    

如果它澄清你,请投票。

祝你有美好的一天:)

【讨论】:

以上是关于iOS:如何从 REST SwiftUi 访问和使用 API 响应数据的主要内容,如果未能解决你的问题,请参考以下文章

从 iOS / Objc 访问 JSON/Rest 服务器的方法?

无法从 SwiftUI 框架中的资产目录访问的图像

如何从 SwiftUI 中的操作内部访问按钮的 titleLabel?

如何仅使用 SwiftUI 从@main App 访问 NSWindow?

如何从 UI 测试用例类访问 SwiftUI 视图的属性?

SwiftUI:如何更新 NavigationView 详细信息屏幕?