Обработка ошибок + Замыкания

swift

#1

###Пример первый.

Имеем сложный метод генерации случайных чисел, который в случае неудачи генерирует ошибку:

class Generator {
    enum GeneratorError: Error {
        case invalidRandom
    }
    class func superRandom() throws -> Int {
        let result = arc4random()
        guard result <= UInt32(Int32.max) else { throw GeneratorError.invalidRandom }
        return Int(result)
    }
}

Поскольку метод сложный и ресурсоёмкий, хотелось бы выполнить его один раз и результат записать в переменную, как ленивое свойство (lazy в данном случае не подойдет) на помощь приходят замыкания:

var result: () throws -> Int = {
    var result: Int!
    return {
        if result == nil { result = try Generator.superRandom() }
        return result
    }
}()

Отработает он как положено один раз и есть возможность отловить ошибку или передать её дальше:

do {
    var result = try self.result()
    print(result)
    result = try self.result()
    print(result)
    result = try self.result()
    print(result)
} catch {
    print(error)
}

###Пример второй.

Имеем метод загрузки данные из интернета, обычно выглядет так:

func load(url: String, comletion: @escaping (_ data: Data, _ error: Error?) -> ()) {
    URLSession.shared.dataTask(with: URL(string: url)!) { data, _, error in
        comletion(data ?? Data(), error)
    }.resume()
}

И используется так, что конечно хорошо но совсем не круто:

load(url: "http://swiftbook.ru") { data, error in
    guard error != nil else {
        let alert = UIAlertController(title: "Error", message: error!.localizedDescription, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
        self.present(alert, animated: true, completion: nil)
        return
    }
    self.data = data
}

Самое в нем ужасное “error != nil” когда есть “do catch”, по этому воспользуемся силой замыканий и немного усложним его:

func superLoad(url: String, completion: @escaping (_ data: () throws -> Data) -> ()) {
    guard let url = URL(string: url) else {
        completion({ throw NetworkError.invalidURL })
        return
    }
    URLSession.shared.dataTask(with: url) { data, _, error in
        completion({
            guard error == nil else { throw error! }
            guard let data = data else { throw NetworkError.invalidData }
            return data
        })
    }.resume()
}

Теперь вызов выглядет очень круто и понятно))):

superLoad(url: "http://swiftbook.ru") { data in
    do {
        self.data = try data()
    } catch {
        let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
        alert.addAction(UIAlertAction(title: "OK", style: .cancel, handler: nil))
        self.present(alert, animated: true, completion: nil)
    }
}