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.
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 I have confirmed the JSON extraction is working -- if I move that
print(audio)inside the function, it returns the value. I just can't use it elsewhere.I originally tried it without the
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.
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()
print(audio)inside your blockViewDidLoad. Maybe I am missing something, but if possible, I need to be able to print outside the block to make it work (as I want to pass it as a URL in another function that will let the user download the audio from the link). Does that make more sense?dataTaskworks asynchronously. pass it as a URL in another function inside the completion block.audioafter you get response from API. For that you need to use method which takes parameter of completionHandler or Block