简体   繁体   English

在Swift 3中使用的函数外部传递变量

[英]Passing variable outside of a function for use in Swift 3

I'm new to Swift, and I want to 1) run a function that extracts a value from a JSON array (this part works) and 2) pass that variable into another function which will play that URL in my audio player. 我是Swift的新手,我想1)运行一个从JSON数组中提取值的函数(此部分有效),然后2)将该变量传递到另一个函数中,该函数将在我的音频播放器中播放该URL。

My issue: I can't access that string stored in a variable outside the first function. 我的问题:我无法访问存储在第一个函数之外的变量中的字符串。 Luckily, there's a bunch of questions on this ( example ), and they say to establish a global variable outside the function and update it. 幸运的是,对此( 示例 )有很多问题,他们说要在函数外部建立全局变量并对其进行更新。 I have tried this like so: 我已经这样尝试过了:

var audio = ""

override func viewDidLoad() {
    super.viewDidLoad()

        let url = URL(string: "http://www.example.json")
            URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
                guard let data = data, error == nil else { return }

                let json: Any?
                do{
                    json = try JSONSerialization.jsonObject(with: data, options: [])
                }
                catch{
                    return
                }
                guard let data_list = json as? [[String:Any]] else {
                    return
                }
                // here's the important part
                if let foo = data_list.first(where: {$0["episode"] as? String == "Special Episode Name"}) {
                    // do something with foo

                    self.audio = (foo["audio"] as? String)!

                } else {
                    // item could not be found
                }

            }).resume()

    print(audio) // no errors but doesn't return anything
  1. I have confirmed the JSON extraction is working -- if I move that print(audio) inside the function, it returns the value. 我已经确认JSON提取正常-如果我在函数内移动print(audio) ,它将返回该值。 I just can't use it elsewhere. 我只是不能在其他地方使用它。

  2. I originally tried it without the self. 我最初尝试时没有self. but returned an error. 但返回了错误。

Is there a better way to store this string in a variable so I can use it in another function? 是否有更好的方法将此字符串存储在变量中,以便可以在其他函数中使用它?

EDIT: Trying new approach based on Oleg's first answer. 编辑:基于奥列格的第一个答案尝试新方法。 This makes sense to me based on how I understand didSet to work, but it's still causing a thread error with the play button elsewhere. 根据我对didSet的工作方式的了解,这对我来说didSet ,但是仍然会导致其他位置的播放按钮出现线程错误。

  var audiotest = ""{
        didSet{
            // use audio, start player
            if let audioUrl = URL(string: audiotest) {

                let documentsDirectoryURL =  FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

                let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)

                //let url = Bundle.main.url(forResource: destinationUrl, withExtension: "mp3")!

                do {
                    audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)

                } catch let error {
                    print(error.localizedDescription)
                }
            } // end player
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

            let url = URL(string: "http://www.example.com/example.json")
                URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
                    guard let data = data, error == nil else { return }

                    let json: Any?
                    do{
                        json = try JSONSerialization.jsonObject(with: data, options: [])
                    }
                    catch{
                        return
                    }

                    guard let data_list = json as? [[String:Any]] else {
                        return
                    }

                    if let foo = data_list.first(where: {$0["episode"] as? String == "Houston Preview"}) {
                        // do something with foo

                        self.audiotest = (foo["audio"] as? String)!


                    } else {
                        // item could not be found
                    }

                    print(self.audiotest)


                }).resume()

The request for the data is asynchronous so the code that is inside the completionHandler block happens some time later (depending on the server or the timeout) , that's why if you try to print outside the completionHandler actually the print func happens before you get the data. 数据请求是异步的,因此,completeHandler块内的代码会在一段时间后发生(取决于服务器或超时),这就是为什么如果您尝试在completeHandler外部进行打印,则实际上打印功能发生在获取数据之前。

There are couple of solution: 1. Add property observer to your audio property and start playing when it is set: 有几种解决方案:1.将属性观察器添加到您的音频属性,并在设置后开始播放:

var audio = “”{
     didSet{
        // use audio, start player 
    }

}

2. Wrapping the request with a method that one of its parameters is a completion closure: 2.用一种方法将请求包装为一个参数,该参数之一是完成闭包:

// the request
func fetchAudio(completion:(String)->()){
       // make request and call completion with the string inside the completionHandler block i.e. completion(audio)
}

// Usage
fetchAudio{ audioString in
     // dispatch  to main queue and use audioString
}

Try this code. 试试这个代码。 No need to take global variable if it is not being used in multiple function. 如果未在多个函数中使用全局变量,则无需采用全局变量。 you can return fetched URL in completion handler. 您可以在完成处理程序中返回获取的URL。

    func getAudioUrl(completionHandler:@escaping ((_ url:String?) -> Void)) {

        let url = URL(string: "http://www.example.json")
        URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
            guard let data = data, error == nil else { return }

            let json: Any?
            do{
                json = try JSONSerialization.jsonObject(with: data, options: [])
            }
            catch{
                return
            }
            guard let data_list = json as? [[String:Any]] else {
                return
            }
            // here's the important part
            if let foo = data_list.first(where: {$0["episode"] as? String == "Special Episode Name"}) {
                // do something with foo

                let audio = (foo["audio"] as? String)!
                completionHandler(audio)

            } else {
                // item could not be found
                completionHandler(nil)
            }

        }).resume()
    }

    func useAudioURL() {

        self.getAudioUrl { (url) in

            if let strUrl = url {

                // perform your dependant operation
                print(strUrl)
            }else {

                //url is nil
            }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM