I am new to iOS development. I am using Swift and I have very little experience with Objective-C, so some of the other possibly related answers are tricky to understand. I am trying to understand how to use NSURLSession to get some data from a JSON file on the Web. I found some useful information about getting a file from a URL, but like this other StackOverflow user (NSURLSessionDataTask dataTaskWithURL completion handler not getting called), I heard that NSURLConnection was not the current way to get data, so I'm trying to use NSURLSession.
When I am getting my JSON from the bundle, I am using this extension to
Dictionary (I am pretty sure I got this code from a tutorial):
static func loadJSONFromBundle(filename: String) -> Dictionary<String, AnyObject>? {
let path = NSBundle.mainBundle().pathForResource(filename, ofType: ".json")
if !path {
println("Could not find level file: \(filename)")
return nil
}
var error: NSError?
let data: NSData? = NSData(contentsOfFile: path, options: NSDataReadingOptions(),
error: &error)
if !data {
println("Could not load level file: \(filename), error: \(error!)")
return nil
}
let dictionary: AnyObject! = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions(), error: &error)
if !dictionary {
println("Level file '\(filename)' is not valid JSON: \(error!)")
return nil
}
return dictionary as? Dictionary<String, AnyObject>
}
I'd like to do something similar for getting a dictionary from a JSON file that is on the web because I don't anticipate wanting to include all of my JSON files in the bundle. So far, I have this:
static func loadJSONFromWeb(urlstring: String) -> Dictionary<String, AnyObject>? {
let url = NSURL(string: urlstring)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config, delegate: nil, delegateQueue: NSOperationQueue())
var error: NSError?
//I think I am using the completionHandler incorrectly. I'd like to access the data from the download
let task = session.downloadTaskWithRequest(NSURLRequest(URL: url), {(url, response, error) in println("The response is: \(response)")
})
task.resume()
//Isn't this contentsOfURL thing supposed to go with the connection stuff rather than the session stuff?
//How can I do this with a session? How can I create and use a completionHandler? This way seems clunky.
let data: NSData? = NSData(contentsOfURL: url)
if !data {
println("Could not load data from file: \(url), error: \(error!)")
return nil
}
println("The data is: \(data)")
let dictionary: AnyObject! = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions(), error: &error)
if !dictionary {
println("The file at '\(url)' is not valid JSON, error: \(error!)")
return nil
}
return dictionary as? Dictionary<String, AnyObject>
}
I think that my actual question that most needs answering is this: Where
is the data? I don't think I am using sessions and tasks correctly. I feel like I'm
starting a session to connect to a specific URL and using resume() to
start the download task I want to make happen, but I don't know how to
get the data from that JSON file.
If I need to use a completionHandler and a request in a way similar to what I found here:
(popViewControllerAnimated work slow inside NSURLSessionDataTask) can someone please explain how the 'data' in the completionHandler relates to the data in the fie I am trying to read/download? I am a bit baffled by the completionHandler and how to use it properly.
I looked at the documentation for NSData as well, but I didn't see anything that helped me understand how to get data from my session (or how to initialize an instance of NSData given my session). As far as I can tell form the documentation for NSURLDownloadTask, the task itself is not how I can access the data. It looks like the data comes from the session and task through the completionHandler.
EDIT:
I also looked at the documentation for NSURLSessionDownloadDelegate, but I could really use an example in Swift with some explanation about how to use the delegate. This led me to the URL Loading System Programming Guide. I'm guessing the benefits of using a session must be huge if the documentation is this complicated. I'm going to keep looking for more information on the URL Loading System.
I found this answer helpful (but I'm so new I can't upvote anything yet): https://stackoverflow.com/a/22177659/3842582 It helped me see that I am probably going to need to learn to use a delegate.
I also looked at the URL Loading System Programming Guide. I think what I really need is help with a completionHandler. How can I get my data? Or, am I already doing it correctly using NSData(contentsOfURL: url) (because I don't think I am).
Thank you in advance for any help you can offer!
First, let data: NSData? = NSData(contentsOfURL: url) will return your JSON synchronously. Did you try that and get this working simply? That way you can get started with the rest of your processing while figuring out NSURLSession.
If you're going to use NSURLSession, or a lot of other things in iOS, you need to learn delegates. Fortunately, they're easy. In terms of syntax you just add it to your class declaration like you were inheriting from it. What that does is say that you are going to implement at least the required functions for the delegate. These are callback functions which are generally pretty well documented. It is quite straightforward once you understand it.
If this is not a "heavyweight" project that really needs NSURLSession, you should look at this Swift library. Besides being a really nice way to deal with JSON there is a synchronous call to directly load the JSON from a url. https://github.com/dankogai/swift-json/
Why is NSURLConnection not the correct way to get data? You just should be careful with synchronous requests. Here is an example of how to get data from an url.
func synchronousExampleRequest() -> NSDictionary {
//creating the request
let url: NSURL! = NSURL(string: "http://exampledomain/apiexample.json")
var request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
var error: NSError?
var response: NSURLResponse?
let data = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error: &error)
error = nil
let resultDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
return resultDictionary
}
Related
I have this JSON
{
"chatUsers":"[
{"id":"5","sender_id":"6","receiver_id":"1","content":"hi","datetime":"2016-11-19 00:00:00"},
{"id":"4","sender_id":"1","receiver_id":"2","content":"hello","datetime":"2016-11-11 00:00:00"},
{"id":"2","sender_id":"1","receiver_id":"3","content":"how are you","datetime":"2016-11-04 00:00:00"}
]",
"chatsCount":3
}
now I have this code to get data from the url :
let StringUrl = NSURL(string: url) as NSURL!
let Data = NSData(contentsOfURL: StringUrl) as NSData!
let ReadableData = JSON(data: Data)
let result = ReadableData["chatUsers"][0]["id"].string! as String // this should gives 5
but it always gives this error :
fatal error: unexpectedly found nil while unwrapping an Optional value
any idea why ?
There's quite a bit wrong with the example you provided above, but for the sake of getting you moving in the right direction, let me give you a some advice and a quick solution to your issue.
First off, don't start your variables with an uppercase character. Its not a set in stone rule or anything, but it's better practice to use camelCase as it will make your code more readable and avoid confusion.
Secondly, you are doing lots of force casting and force unwrapping of optional values which will make your application prone to crashes due to the same error you are getting now. I would take a look at the following for some better guidance: https://stackoverflow.com/a/25195633/4660602
Third, when posting on SO, please make sure you clearly give your question context as some users may believe you are asking a question about what optionals are and why you are getting the fatal error: unexpectedly found nil while unwrapping an Optional value error.
With that said, in the future please make sure to take a look at the SwiftyJSON docs as they pretty clearly demonstrate how to use the library. The reason your code is not working is because you are handling the JSON incorrectly. Here is an updated example:
let StringUrl = NSURL(string: url) as NSURL!
let Data = NSData(contentsOfURL: StringUrl) as NSData!
let ReadableData = JSON(data: Data)
let chatUsers = ReadableData["chatUsers"].arrayValue
let result = chatUsers[0]["id"].stringValue
or if you need to iterate through all the users:
let StringUrl = NSURL(string: url) as NSURL!
let Data = NSData(contentsOfURL: StringUrl) as NSData!
let ReadableData = JSON(data: Data)
for chatUser in ReadableData["chatUsers"]{
print(chatUser.1["id"].stringValue)
}
Please note I did not update your variable identifiers to the correct convention for the sake of not confusing you, but you should really follow the correct convention as I mentioned in my first point.
Good luck.
EDIT: Forgot to mention that your JSON is incorrectly formatted. Please fix your JSON data to the correct JSON format so that SwiftyJSON can correctly interpret it. You are receiving index out of range because SwiftyJSON does NOT know chatUsers is an array because the square brackets are wrapped in double quotes when they should not be. So
"chatUsers":"[
{"id":"5","sender_id":"6","receiver_id":"1","content":"hi","datetime":"2016-11-19 00:00:00"},
{"id":"4","sender_id":"1","receiver_id":"2","content":"hello","datetime":"2016-11-11 00:00:00"},
{"id":"2","sender_id":"1","receiver_id":"3","content":"how are you","datetime":"2016-11-04 00:00:00"}
]"
should be
"chatUsers": [
{"id":"5","sender_id":"6","receiver_id":"1","content":"hi","datetime":"2016-11-19 00:00:00"},
{"id":"4","sender_id":"1","receiver_id":"2","content":"hello","datetime":"2016-11-11 00:00:00"},
{"id":"2","sender_id":"1","receiver_id":"3","content":"how are you","datetime":"2016-11-04 00:00:00"}
]
I'm sorry if this may seem that I haven't read or updated my information on Swift 2.2 but I actually already have and I've gone through the updated method but still am receiving errors(I didn't state them here because thats not my issue; my issue is writing the JSONObjectWithData in its updated syntax form)
I took this function from an older swift book and am trying to get it to Parse the data into JSON. I used the updated syntax for the method JSONObjectWithData() but wasn't able to piece the syntax pieces together. My problem isn't the compiler screaming at me with errors because I know that I was rewriting the JSONObjectWithData method wrong, heres the old syntax that I tried to rewrite but couldn't do so successfully.
I still haven't wrapped my head around the concept of parsing data into JSON even after studying the chapter and reading the Apple documentation, let alone try to rewrite the JSONObjectWithData method correctly. I searched a similar answer to this and could not figure out how to write this book method correctly in the updated syntax and have it run without errors. I've been stuck on this for 2 days.
func parseJson(data: NSData) {
var error: NSError?
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: .allZeros, error: &error)
if error == nil {
if let unwrappedJson: AnyObject = json {
parseSongs(json: unwrappedJson)
}
}
}
Have you tried this block of code:
let json: AnyObject? = try! NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
this code was function correctly but when i tried on swift 2, i start having this errors, does anyone know how to fix them? i already add the NSAppTransportSecurity to info list.
The app may be able to download a json array with images from server, but i'm not really sure how to do it on swift 2
I tried a lot of tutorials about swift 2 but didn't work
Try this....Use do catch instead of if let
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(dataObject, options:.MutableContainers) as? NSArray
self.images = jsonResult
} catch let error as NSError {
print(error)
}
I want to code an app which shows you the data of a specific HTML-Page.
How can read the text e.g. in a div and turn that text into a String? How can I search something on that page or look in the row of a table?
You need mind what is the step you need to complete this task.
First need load html page data
After find what do you need with parser.
In the first step I found this code
let url = NSURL(string: "http://www.stackoverflow.com")
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) {(data, response, error) in
println(NSString(data: data, encoding: NSUTF8StringEncoding))
}
task.resume()
Example from this link How to make an HTTP request in Swift?
Look this example, you do a request and return a data, this data need be String. After received data you need parse to findo what do you want.
Second step you need tools to execute, the simple way is do with string methods like slipt. If you need something more smart I recommend Regular Expression
Simple example for split (swift 1.2)
var fullName = "First Last"
var fullNameArr = split(fullName) {$0 == " "}
var firstName: String = fullNameArr[0]
var lastName: String? = fullNameArr.count > 1 ? fullNameArr[1] : nil
example (swift 2.0)
let fullName = "First Last"
let fullNameArr = split(fullName.characters){$0 == " "}.map(String.init)
fullNameArr[0] // First
fullNameArr[1] // Last
Examples from Swift: Split a String into an array
This link is good about Regular Expression, mind it is a powerfull tool to parse, with this you can search anything in your html.
NSRegularExpression Tutorial: Getting Started
I hope helped you with this explanation.
Updated syntax (swift 5) with a few modifications to get html data:
let url = NSURL(string: "https://www.stackoverflow.com")
let task = URLSession.shared.dataTask(with: url! as URL) {(data, response, error) in
if data != nil { // otherwise will crash for if url is empty
let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)!
print("url data:",dataString)
} else {print("url data not found")}
}
task.resume()
I am coding a hangman game and am loading the possible words into my app using json text files. I tried to follow the examples of others on this website but I am getting errors from Xcode.
I tried the following code based on another answer:
import Foundation
var error: NSError?
let jsonData: NSData = /* get your json data */
let jsonDict = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: &error) as NSDictionary
But I got an errors on line 4 with jsonDict that said "call can throw but is not marked with try, and the error is not handled" and "Type JSONReadingOptions does not conform to protocol NilLiteralConvertible".
Here is the JSON File I would like to parse:
{
“wordList” : {
“difficulty” : “Easy”
“list” : [
“fireplace”,
“apple”,
“january”,
“tooth”,
“cookies”,
“mysterious”,
“essential”,
“magenta",
“darling”,
“pterodactyl”
]}}
I would like to be able to go into my list array and get values. Thank you very much for any help!
In Swift 2 you need to use the new error handling API instead of passing a reference to an NSError:
do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions(rawValue: 0)) as? NSDictionary
if let jsonDict = jsonDict {
// work with dictionary here
} else {
// more error handling
}
} catch let error as NSError {
// error handling
}
You also can't pass nil as value to the options parameter, you need to pass a value of type NSJSONReadingOptions.
That said, the most common approach for parsing JSON in Swift is currently using third party libraries, such as Argo because they can save you a lot of code that is necessary to validate and safely cast the content of your JSON data to the correct Swift types.