如果远程 GET 请求失败,如何定义回退情况?

How to define a fallback case if a remote GET request fails?

我最近开始 iOS 开发,目前正在为现有应用程序添加新功能。对于此功能,我需要从 Web 服务器获取 JSON 文件。但是,如果服务器无法访问(没有 internet/server unavailable/etc),则需要使用本地 JSON

在我当前的实现中,我尝试使用 do catch 块,但如果没有互联网连接,应用程序就会挂起而不是转到 catch 块。 JSON 解析和本地数据读取似乎工作正常,问题可能出在 GET 方法中,因为我试图将 return JSON 数据的回调定义为单独的变量,但我不确定这是否是正确的方法。

处理这种情况的最佳方法是什么?

 let url = URL(string: "https://jsontestlocation.com")  // test JSON

        do {
            // make a get request, get the result as a callback
            let _: () = getRemoteJson(requestUrl: url!, requestType: "GET") {
                remoteJson in
                performOnMainThread {
                    self.delegate.value?.didReceiveJson(.success(self.parseJson(jsonData: remoteJson!)!))
                }
            }

        }
        catch {
            let localFile = readLocalFile(forName: "local_json_file")
            let localJson = parseJson(jsonData: localFile!)
            if let localJson = localJson {
                self.delegate.value?.didReceiveJson(.success(localJson))
            }
        }

getRemoteJson() 实施:

private func getRemoteJson(requestUrl: URL, requestType: String, completion: @escaping (Data?) -> Void) {
        // Method which returns a JSON questionnaire from a remote API
        
        var request = URLRequest(url: requestUrl)  // create the request
        request.httpMethod = requestType
        
        // make the request
        let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
            
            // check if there is any error
            if let error = error {
                print("GET request error: \(error)")
            }
            
            // print the HTTP response
            if let response = response as? HTTPURLResponse {
                print("GET request status code: \(response.statusCode)")
            }
            
            guard let data = data else {return}  // return nil if no data
            
            completion(data)  // return
        }
        task.resume()  // resumes the task, if suspended
    }

parseJson() 实施:

private func parseJson(jsonData: Data) -> JsonType? {
        // Method definition
        do {
            let decodedData = try JSONDecoder().decode(JsonType.self, from: jsonData)
            return decodedData
            
        } catch {
            print(error)
        }
        
        return nil
    }

func NetworkCheck() -> 布尔 {

var isReachable = false

let reachability = Reachability()
print(reachability.status)

if reachability.isOnline {
    
    isReachable = true
    
    // True, when on wifi or on cellular network.
}
else
{
    // "Sorry! Internet Connection appears to be offline
}

return isReachable

}

在您的 API 请求之前调用 NetworkCheck()。如果它 returns 为假,请阅读您的本地 json 文件。如果为真,则进行远程 API 调用。

万一在远程 API 调用之后,任何使用 HTTP header 响应代码的失败检查。

如果让 httpStatus = 响应为? HTTPURLResponse, httpStatus.statusCode != 200 {

}

我认为您需要阻止请求在等待响应时挂起。该应用程序可能 运行 连接不佳并且能够获取部分但不是全部数据,在这种情况下您可能希望故障转移到本地 JSON.

我认为您可以粗略地使用现有的,但按照此处所述在 URLSession 上添加超时配置:

如果您不必使用具有可达性、错误处理、请求重试等的复杂逻辑,只需 return nil 在数据任务、HTTP 和无数据错误的情况下完成:

func getRemoteJson(requestUrl: URL, requestType: String, completion: @escaping (Data?) -> Void) {
    var request = URLRequest(url: requestUrl)
    request.httpMethod = requestType
    
    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
        // Task error
        guard  error == nil else {
            print("GET request error: \(error!)")
            completion(nil)
            return
        }
        
        // HTTP error
        guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
            print("GET request failed: \(response!.description)")
            completion(nil)
            return
        }
        
        // No data
        guard let data = data else {
            completion(nil)
            return
        }
        
        completion(data)
    }
    task.resume()
}

let url = URL(string: "https://jsontestlocation.com")!
getRemoteJson(requestUrl: url, requestType: "GET") { remoteJson in
    if let json = remoteJson {
        print(json)
        ...
    }
    else {
        print("Request failed")
        ...
    }
}