4

I'm using an API that gives me data in a neat format - How do I get this as a String? I can print it, but saving it as a string doesn't seem to work.

I specifically want to update a UI element using the data from the NSURLSession task.

    let url = NSURL(string: apiCall)
    let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
        //I want to replace this line below with something to save it to a string.
        println(NSString(data: data, encoding: NSUTF8StringEncoding))
    }

    task.resume()
Cesare
  • 9,139
  • 16
  • 78
  • 130
lgbrf
  • 174
  • 1
  • 2
  • 13
  • If you do the obvious (assign it to a string variable), what problem do you encounter? – Phillip Mills May 08 '15 at 15:58
  • When I print the string from inside the task it has the right data, but after the task it's empty. – lgbrf May 08 '15 at 16:09
  • See http://stackoverflow.com/questions/1351151/guess-encoding-when-creating-an-nsstring-from-nsdata/26740668#26740668 – HAS May 08 '15 at 16:13

4 Answers4

9

Swift 4

let data = String(bytes: data, encoding: String.Encoding.utf8)
Manee.O.H
  • 589
  • 8
  • 19
5

If your problem is that it is empty outside of the task, that is because it is going out of scope after the completion block ends. You need to save it somewhere that has a wider scope.

let url = NSURL(string: apiCall)
var dataString:String = ""
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
    //I want to replace this line below with something to save it to a string.
    dataString = String(NSString(data: data, encoding: NSUTF8StringEncoding))
    dispatch_async(dispatch_get_main_queue()) {
        // Update the UI on the main thread.
        self.textView.text = dataString
    });

}
task.resume()

now when you access dataString it will be set to the data from task. Be wary though, until task is completed, dataString won't be set, so you should really try to use it in the completion block.

Will M.
  • 1,864
  • 17
  • 28
  • So that's how I was assigning it when I tried, I didn't realise that it would not work until the task completed. That solves one problem, but now when I try and assign it directly it gives me some weird NSException. I added `self.textView.text = dataString` . I feel like there is probably something obviously wrong with doing that? – lgbrf May 08 '15 at 16:47
  • in the task block: `let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in var dataString:String = NSString(data: data, encoding: NSUTF8StringEncoding)! self.textView.text = (dataString) }` – lgbrf May 08 '15 at 16:51
  • If you are updating UI elements, you should make sure to do so on the main thread. What is your exception? – Will M. May 08 '15 at 16:54
  • `2015-05-09 02:53:41.830 Reap[99318:2892858] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Only run on the main thread!'` I'm assuming that means I can't update it inside the func, so, I'll go back to what I was trying before.. declaring the string before the task and changing it inside. But this leaves me with a blank variable (it seems to wipe it when the task finishes). Is there some way to hold the func from ending until the task does? – lgbrf May 08 '15 at 17:11
  • @lgbrf If you save the data as a variable and then call a function in the task block that updates your UI, does that solve your problem? – brimstone May 08 '15 at 17:46
  • I updated my answer to show you how to call something on the main thread in Swift. Your network completion block is (and should be) on a background thread, but you want to update the UI, so you call that specific bit of UI code on the main thread in the dispatch_async block – Will M. May 08 '15 at 18:47
  • I had to change it slighty (from my understanding objective C -> swift) `dispatch_async(dispatch_get_main_queue()) { self.textView.text = dataString }` But after that it worked! Thanks! – lgbrf May 09 '15 at 04:12
0

Swift uses String and not NSString.

But you can cast the NSString to a String like that:

var dataAsString:String = NSString(data: data, encoding: NSUTF8StringEncoding) as! String
Christian
  • 22,585
  • 9
  • 80
  • 106
0

Another Option:

let string = String(data: data, encoding: .utf8)

Edit: I mistakenly wrote that it would be a newer version, is just another.

Tob
  • 627
  • 6
  • 9
  • 1
    FYI - This isn't any newer than the "Swift 4" answer posted 5 years ago. This uses `init(data:encoding:)` versus `init(bytes:encoding:)`. Both options have been around a long time. Also, using the optionally shortened `.utf8` isn't any newer than using the optionally fully qualified `String.Encoding.utf8` shown in that "Swift 4" answer. – HangarRash Mar 26 '23 at 14:01