在尝试解析Alamofire结果之前处理无网络连接错误

19

如果在Alamofire中没有网络连接时出现错误,该如何处理?我尝试检查数据是否为nil,但它不起作用。

以下是我如何使用Alamofire

Alamofire.request(.POST, REGISTER_URL, parameters: parameters, encoding: .JSON, headers: getAuthenticationHeader()).response { (request, response, data, errorType) -> Void in

    let resultObject: APIResults = APIResults(JSONDecoder(data!));
    let responseCode: Int = Int(resultObject.code!)!;// THIS CRASHES WHEN THERE IS NO INTERNET CONNECTION

    if (responseCode == 200) {
        available = true;
    }

    finished = true;

}

您可以检查 errorType 来了解是否存在错误。 - Rajat
我怎样才能知道错误类型是什么? - JayVDiyk
1
你应该始终检查数据是否存在,直接访问可能会在数据为空值时崩溃。永远不要对那些不需要始终非空的可选类型使用强制解包。 - Shripada
1
正如我在问题中所写的那样,即使我检查数据是否不为nil,它仍然会通过@Shripada。 - JayVDiyk
1
你应该使用Reachability来可靠地检查连接性-https://developer.apple.com/library/ios/samplecode/Reachability/Introduction/Intro.html - Shripada
显示剩余2条评论
7个回答

37

Swift 3解决方案

假设您有一个Error实例,您可以执行以下操作:

if let err = error as? URLError, err.code  == URLError.Code.notConnectedToInternet
{
    // No internet
}
else
{
    // Other errors
}

您可以将error直接转换为URLError。这是可行的,因为URLError实现了Error协议。这里是苹果文档的一段引用:

URLError:描述URL错误域中的错误。

一旦您拥有URLError实例,您可以简单地比较其code属性,该属性是一个URLError.Code枚举,与任何相关的枚举值进行比较(在我们的示例中为URLError.Code.notConnectedToInternet)。


有时候,Alamofire 在没有任何错误代码的情况下返回 Error。这种情况只会在我没有运行本地主机时发生。我忘记了错误代码,但它与尽管互联网已经连接但无法连接到服务器有关,我希望能够捕获这个错误。 - rocky raccoon

12

这对我在Swift2.x中有效。

Alamofire.request(.POST, url).responseJSON { response in
    switch response.result {
        case .Success(let json):
            // internet works.  
        case .Failure(let error):

            if let err = error as? NSURLError where err == .NotConnectedToInternet {
                // no internet connection
            } else {
                // other failures
            }
    }
}

5

我同意@Shripada的观点。首先,您应该使用Reachability检查连接性。

这里有一个Swift库:https://github.com/ashleymills/Reachability.swift

此外,您还可以使用Alamofire的验证方法之一:

Alamofire.request(.POST, REGISTER_URL, parameters: parameters, encoding: .JSON, headers: getAuthenticationHeader()).validate(statusCode: 200 ..< 300).response { (request, response, data, error) -> Void in
    if error != nil {
        println("Server responded with: \(response.statusCode)")
        return
    }

    // Handle your response data here
}

我得到了“使用未解决的标识符'getAuthenticationHeader'” 的错误信息。 - Luda
请删除此方法调用。它是针对该问题特定的。 - ergoon
1
可达性是一个问题,除非您的应用程序需要不断连接,出于某种原因,否则您不需要它。我通常不需要它。用户只有在请求数据时才需要连接,大多数应用程序的其余时间都可以使用缓存数据。 - HotFudgeSunday

5

检查互联网连接的另一种方式

import SystemConfiguration

func connectedToNetwork() -> Bool {

        var zeroAddress = sockaddr_in()    

       zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))

        zeroAddress.sin_family = sa_family_t(AF_INET)

      guard let defaultRouteReachability = withUnsafePointer(&zeroAddress, {
            SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
        }) else {
            return false
        }
        var flags : SCNetworkReachabilityFlags = []

        if SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) == false {

            return false

        }

        let isReachable = flags.contains(.Reachable)

        let needsConnection = flags.contains(.ConnectionRequired)

        return (isReachable && !needsConnection)

    }

let hasInternet = connectedToNetwork()

4

3

细节

  • Xcode 10.2.1 (10E1001), Swift 5

准备

编辑Info.plist中的NSAppTransportSecurity

代码:

<key>NSAppTransportSecurity</key>
    <dict>
    <key>NSAllowsArbitraryLoads</key><true/>
</dict>

示例1 (需要使用Alamofire库)

使用网络可达性

import UIKit
import Alamofire

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(frame: .init(x: 80, y: 80, width: 100, height: 40))
        button.setTitle("Check", for: .normal)
        button.addTarget(self, action: #selector(checkInternetConnection), for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        view.addSubview(button)
    }

    @objc func checkInternetConnection() {
        guard let networkReachabilityManager = NetworkReachabilityManager(host: "http://google.com") else { return }
        networkReachabilityManager.listener = { [weak self] status in
            let alert = UIAlertController(title: "Network Status ", message: "\(status)", preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] _ in
                alert?.dismiss(animated: true, completion: nil)
            }))
            self?.present(alert, animated: true, completion: nil)
        }
        networkReachabilityManager.startListening()
    }
}

示例 2(需要使用Alamofire库)

import UIKit
import Alamofire

class ViewController: UIViewController {

    private lazy var networkManager = NetworkManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(frame: .init(x: 80, y: 80, width: 100, height: 40))
        button.setTitle("Check", for: .normal)
        button.addTarget(self, action: #selector(checkInternetConnection), for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        view.addSubview(button)
    }

    @objc func checkInternetConnection() {
        let urlString = "http://dummy.restapiexample.com/api/v1/employees"
        networkManager.sessionManager.request(urlString).validate().response { response in
            print("\(response.data as Any)")
        }
    }
}


class NetworkManager {
    lazy var sessionManager: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpCookieStorage = nil
        configuration.httpCookieAcceptPolicy = HTTPCookie.AcceptPolicy.never
        let manager = SessionManager(configuration: configuration)
        manager.retrier = self
        return manager
    }()
}

extension NetworkManager: RequestRetrier {
    func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
        let error = error as NSError
        switch error.code {
            case -1009:
                DispatchQueue.main.async {
                    let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
                    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] _ in
                        alert?.dismiss(animated: true, completion: nil)
                    }))
                    UIApplication.topMostViewController?.present(alert, animated: true, completion: nil)
                }
            default: break
        }
        print("-- Error code: \(error.code)")
        print("-- Error descriptiom: \(error.localizedDescription)")
    }
}


// https://dev59.com/_V8d5IYBdhLWcg3wkS7Y#52932487
extension UIViewController {
    var topMostViewController: UIViewController {

        if let presented = self.presentedViewController {
            return presented.topMostViewController
        }

        if let navigation = self as? UINavigationController {
            return navigation.visibleViewController?.topMostViewController ?? navigation
        }

        if let tab = self as? UITabBarController {
            return tab.selectedViewController?.topMostViewController ?? tab
        }
        return self
    }
}

extension UIApplication {
    class var topMostViewController : UIViewController? {
        return UIApplication.shared.keyWindow?.rootViewController?.topMostViewController
    }
}

示例3(不使用第三方库)

更多信息:如何在Swift中使用SCNetworkReachability

import UIKit
import SystemConfiguration

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(frame: .init(x: 80, y: 80, width: 100, height: 40))
        button.setTitle("Check", for: .normal)
        button.addTarget(self, action: #selector(checkInternetConnection), for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        view.addSubview(button)
    }

    @objc func checkInternetConnection() {
        print("-- \(URLSession.connectedToNetwork())")
    }
}

extension URLSession {
    class func connectedToNetwork() -> Bool {
        var zeroAddress = sockaddr()
        zeroAddress.sa_len = UInt8(MemoryLayout<sockaddr>.size)
        zeroAddress.sa_family = sa_family_t(AF_INET)
        guard let networkReachability = SCNetworkReachabilityCreateWithAddress(nil, &zeroAddress) else { return false }
        var flags = SCNetworkReachabilityFlags()
        SCNetworkReachabilitySetDispatchQueue(networkReachability, DispatchQueue.global(qos: .default))
        if SCNetworkReachabilityGetFlags(networkReachability, &flags) == false { return false }
        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)
        return isReachable && !needsConnection
    }
}

样例4 (需要使用Reachability.swift库)

import UIKit
import Reachability

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let button = UIButton(frame: .init(x: 80, y: 80, width: 100, height: 40))
        button.setTitle("Check", for: .normal)
        button.addTarget(self, action: #selector(checkInternetConnection), for: .touchUpInside)
        button.setTitleColor(.blue, for: .normal)
        view.addSubview(button)
    }

    @objc func checkInternetConnection() {
        guard let reachability = Reachability(hostname: "google.com", queueQoS: .utility) else { return }
        try? reachability.startNotifier()
        print("-- \(reachability.connection as Any)")
    }
}

2

它在Swift 5中工作正常

.catchError { (error: Error) in
    if let err = error as? URLError, err.code  == URLError.Code.notConnectedToInternet {
        // No internet
    } else {
        // Other errors
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接