Обратитесь к переменной из блока do в catch - PullRequest
1 голос
/ 20 марта 2019

Мне нужно получить доступ к переменной внутри оператора do. Будет ли он вести себя как оператор if - else в том смысле, что вы не используете переменные вне оператора if?

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {
    override func loadView() {

        do {
            //let url = URL?("https://www.hackingwithswift.com")
            let TECIONEXContent = try String(contentsOf: URL("https://www.hackingwithswift.com"))

        } catch { print("error")}

            //I need to access TECIONEXContent variable outside the do statement

            // Error: Use of unresolved identifier 'TECIONEXContent'
            var TECGrid = TECIONEXContent.components(separatedBy: "\n")
    }
}

Ошибка в последней строке, «неразрешенный идентификатор».

Ответы [ 3 ]

2 голосов
/ 20 марта 2019

Будет ли он вести себя как оператор if - else в том смысле, что вы не используете переменные вне оператора if?

Да. Но так же, как оператор if - else, вы можете определить переменную перед do - catch:

например. в выражении if - else:

let foo: String

if bar > 1 {
    foo = "bigger than one"
} else {
    foo = "one or smaller"
}

Или, в вашем случае:

let url = URL(string: "https://www.hackingwithswift.com")!

let contents: String

do {
    contents = try String(contentsOf: url)
} catch { 
    print(error)
    return
}

let grid = contents.components(separatedBy: "\n")

Или вы ничего не делаете с сообщением об ошибке, вы можете полностью исключить do - catch:

guard let contents = try? String(contentsOf: url) else {
    print("error")
    return
}

let grid = contents.components(separatedBy: "\n")

Честно говоря, все вышесказанное, в любом случае, использование String(contentsOf:), вероятно, не лучший шаблон, потому что он выполняет синхронный сетевой запрос, который может привести к тому, что процесс «сторожевого таймера» ОС убьет ваше приложение бесцеремонно, если основной поток заблокирован; и даже если этого не произойдет, пользователь не сможет заморозить приложение во время выполнения сетевого запроса. Обычно мы используем URLSession:

let url = URL(string: "https://www.hackingwithswift.com")!

URLSession.shared.dataTask(with: url) { data, response, error in
    guard
        let data = data,
        let httpResponse = response as? HTTPURLResponse,
        let string = String(data: data, encoding: .utf8) else {
            print(error ?? "Unknown error")
            return
    }

    guard 200 ..< 300 ~= httpResponse.statusCode else {
        print("Expected 2xx response, but got \(httpResponse.statusCode)")
        return
    }

    let grid = string.components(separatedBy: "\n")

    DispatchQueue.main.async {
        // use `grid` here
    }
}.resume()

Не связано, но:

  • Соглашение состоит в том, чтобы начинать имена переменных со строчных букв.

  • Вы реализовали loadView. Это редко случается, и вместо этого мы внедряем viewDidLoad, поэтому обязательно вызываем super.viewDidLoad().

  • Если вы делаете это на детской площадке, вы, очевидно, также установите needsIndefiniteExecution, если вы еще этого не сделали.

Таким образом:

import UIKit
import PlaygroundSupport

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        performRequest()
    }

    func performRequest() {
        let url = URL(string: "https://www.hackingwithswift.com")!

        URLSession.shared.dataTask(with: url) { data, response, error in
            guard
                let data = data,
                let httpResponse = response as? HTTPURLResponse,
                let string = String(data: data, encoding: .utf8) else {
                    print(error ?? "Unknown error")
                    return
            }

            guard 200 ..< 300 ~= httpResponse.statusCode else {
                print("Expected 2xx response, but got \(httpResponse.statusCode)")
                return
            }

            let grid = string.components(separatedBy: "\n")

            DispatchQueue.main.async {
                print(grid)
                // use `grid` here
            }
        }.resume()
    }
}

PlaygroundPage.current.liveView = ViewController()
PlaygroundPage.current.needsIndefiniteExecution = true
1 голос
/ 20 марта 2019

В вашем коде четыре (!) Очень плохие практики.

  1. Никогда загрузка данных синхронно с API, например String(contentsOf с удаленного URL,Используйте асинхронный API, такой как URLSession.
  2. Никогда не печатать бессмысленную литеральную строку в блоке catch.Напечатайте экземпляр error.
  3. В соответствии с соглашением об именах имена переменных должны быть lowerCamelCased .
  4. В блоке do - catch всегда помещать все хороший код в области do.Это решит вашу проблему.

    do {
        let tecionexContent = try String(contentsOf: URL("https://www.hackingwithswift.com")!)
        let tecGrid = tecionexContent.components(separatedBy: "\n")
    } catch { print(error) }
    

    рекомендуется

    URLSession.shared.dataTask(with: URL("https://www.hackingwithswift.com")!) { data, _ , error in 
       if let error = error { print(error); return }
       let tecionexContent = String(data: data!, encoding: .utf8)!
       let tecGrid = tecionexContent.components(separatedBy: "\n")
    
    }.resume()
    
0 голосов
/ 20 марта 2019

Проблема

Проблема с вашим кодом заключается в том, что вы определяете переменную внутри блока / замыкания, которое является локальной областью действия.Ваша переменная должна находиться в области видимости, где оба блока могут ее видеть.Это означает, что в блоке catch переменная не существует.В частности, вы пытались ссылаться на строковую переменную, которую вы назвали TECIONEXContent, вне пределов замыкания, в котором она была создана.

В некоторых общих пунктах стиля: пожалуйста, придерживайтесь соглашения о быстром именовании переменных (т. Е. Случай верблюда)) тогда как ваши классы и т. д. должны быть написаны заглавными буквами.(Для запуска приведенного ниже кода на игровой площадке я использовал произвольное имя функции, но вы можете использовать его из своих методов жизненного цикла).

Базовый демонстрационный код игровой площадки

import UIKit
import PlaygroundSupport

class MyViewController : UIViewController {

    private var tecGrid: [String]? {
        didSet {
            // Update some UI Here (insuring your on the main thread)
            print(self.tecGrid)
        }
    }

     func test() {
        do {
             var texionicContent = try String(contentsOf:URL(string: "https://www.hackingwithswift.com")!)
             tecGrid = texionicContent.components(separatedBy: "\n")
        }
        catch let error {
            print("Catch the error")
        }
    }
}


let play = MyViewController(nibName: nil, bundle: nil)
play.test()
...