This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 5 years ago.
i am working an app in which data is downloaded from server in JSON format. but if Image value is nill then app crashed. i also set a default picture but cursor did not enter in else statement. Kindly guide me how i do my task correctly. Here is my code
func downloadUsersData(){
let email = UserDefaults.standard.value(forKey: "userEmail")
var urlString = "http://nexusvision.net/zeroone/selectuserbasic.php"
urlString.append("?")
urlString.append("id=\(email!)")
print("This is URL : \(urlString)")
let url = URL(string: urlString)
var request = URLRequest(url: url!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil{
print(error?.localizedDescription)
}
let data = data
if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode != 200 { // check for http errors
print("statusCode should be 200, but is \(httpStatus.statusCode)")
print("response = \(response)")
}
let responseString = String(data: data!, encoding: .utf8)
print("all Countary responseString = \(responseString)")
let responseData = responseString
do {
let jsonValue = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! NSDictionary
print("This is Json : \(jsonValue.value(forKey: "product"))")
if let profile = jsonValue.value(forKey: "product") as? NSArray
{
for profileData in profile{
if let dict = profileData as? NSDictionary{
if let firstName = dict.value(forKey: "firstname"){
print("Name is : \(firstName)")
self.defaults.set(firstName, forKey: "firstName")
}
if let lastName = dict.value(forKey: "lastname"){
print("Last Name : \(lastName)")
self.defaults.set(lastName, forKey: "lastName")
}
if let imageData = dict.value(forKey: "picture") {
print("This is Image Json: \(imageData)")
let convertToImage = imageData
let decodedData : NSData = NSData(base64Encoded: convertToImage as! String, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
let decodedimage: UIImage = UIImage(data: decodedData as Data)!
print("Thats Decoded : \(decodedimage)")
self.profilePicImageShow.image = decodedimage
}
else{
self.profilePicImageShow.image = UIImage(named: "Empty")
}
}
}
}
}
catch let error as NSError {
print(error)
}
}.resume()
}
Technically, key "picture" is not nil, its empty string according to the JSON response. So when you execute the code, it will go into the if statement and finally crash at
let decodedimage: UIImage = UIImage(data: decodedData as Data)! as decodedData is nil.
Answer:
Modify the your if statement as below
if let imageData = dict.value(forKey: "picture"), (imageData as! String) != "" {
This should fix your crash problem.
Replace
if let imageData = dict.value(forKey: "picture") {
print("This is Image Json: \(imageData)")
let convertToImage = imageData
let decodedData : NSData = NSData(base64Encoded: convertToImage as! String, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters)!
let decodedimage: UIImage = UIImage(data: decodedData as Data)!
print("Thats Decoded : \(decodedimage)")
self.profilePicImageShow.image = decodedimage
} else {
self.profilePicImageShow.image = UIImage(named: "Empty")
}
with
let imageData = dict["picture"] as? NSData ?? UIImageJPEGRepresentation(UIImage(named: "Empty")!, 1.0)
self.profilePicImageShow.image = UIImage(data: imageData!)
Also, make sure that the key "picture" actually contains nil
You could add guard statements as follow:
guard let decodedData = NSData(base64Encoded: convertToImage as! String, options: NSData.Base64DecodingOptions.ignoreUnknownCharacters) else {
return
}
guard let decodedimage: UIImage = UIImage(data: decodedData as Data) else {
return
}
Since you are forcing an unwrap, if it fails to create an image from the data, your app will crash.
As a side note, you could use this and this, those are really great libs to work with HTTP Requests.
In my case I'll get URL from JSON and then download image with URL; So if url is nil then I'll add dummy image else I'll download Image from URL. Here is My Stuff :
var news = respone.result.value as! Dictionary<String,Any>
if let result:NSArray = news["stories"] as? NSArray
{
for i in 0..<result.count
{
let str = (((result[i] as! NSDictionary).value(forKey: "ximg") as! NSDictionary).value(forKey: "ipath") as? String)
if str == nil
{
self.news_url.append("nil")
}
else
{
self.news_url.append(str!)
}
}
}
self.tableview.reloadData()
Related
When Running my http request I get returned the data in the following way.
I am requesting as follows
do {
if let file = URL(string: "https://myhttprequest....") {
let data = try Data(contentsOf: file)
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let object = json as? [String: Any] {
// json is a dictionary
print(object)
} else {
print("JSON is invalid")
}
} else {
print("no file")
}
} catch {
print(error.localizedDescription)
}
The http request shows like this for example
{"created":[{"id":"-LVEAdIk2KwDmxBj25pK","caption":"Cool watch bro ","creationDate":1546442937.5934439,"imageHeight":1000,"imageUrl":"https://firebasestorage.googleapis.com/..."}],"reshared":[{"id":"-LVEAdIk2KwDmxBj25pK","caption":"Cool watch bro ","creationDate":1546442937.5934439,"imageHeight":1000,"imageUrl":"https://firebasestorage.googleapis.com/..."}]}
I want to be able to put the value of object["created"] and object["reshared"] together to have one array of two dictionaries [[caption:"", creationDate:""...],[caption:"", creationDate:""...]]
I have tried by accessing them individually like object["created"] but its not of type dictionary and I cant seem to figure out how to get it to be one.
UPDATE: So I am now doing the following
guard let uid = Auth.auth().currentUser?.uid else { return }
let url = URL(string: "https://us-central1-flashtrend-bdcd3.cloudfunctions.net/getFeed/\(uid)")!
let task = URLSession.shared.dataTask(with: url) {(data, response, error) in
guard let firstData = data else { return }
let jsonStr = String(data: firstData, encoding: .utf8)!
guard let data = jsonStr.data(using: .utf8) else {
return
}
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return
}
guard let created = json["created"], let reshared = json["reshared"] else {
return
}
let result = [created, reshared]
print(result)
} catch {
print(error.localizedDescription)
}
}
task.resume()
But when i print it looks weird
[<__NSSingleObjectArrayI 0x600002de2290>(
{
caption = "Cool watch bro ";
creationDate = "1546442937.593444";
id = "-LVEAdIk2KwDmxBj25pK";
imageHeight = 1000;
imageUrl = "https://firebasestorage.googleapis.com/v0/b/flashtrend-bdcd3.appspot.com/o/posts%2FE346E4B7-31D8-4E9E-89F2-DA7C426C0537?alt=media&token=4936ce58-64bb-4d5a-b913-c3b87705614f";
imageWidth = 750;
swipes = 0;
userid = U9097gARoXOus96vT1uBHAcNPs03;
views = 1;
}
)
, <__NSArray0 0x600002df40c0>(
)
]
I have changed the http request result to json string, sample code for you as follow, works fine on my xcode:
let jsonStr = "{\"created\":{\"caption\":\"Cool watch bro \",\"creationDate\":\"1546442937.593444\",\"id\":\"-LVEAdIk2KwDmxBj25pK\",\"imageHeight\":1000,\"imageUrl\":\"https://firebasestorage.googleapis.com/v0/b/flashtrend-bdcd3.appspot.com/o/posts/E346E4B7-31D8-4E9E-89F2-DA7C426C0537?alt:media&token:4936ce58-64bb-4d5a-b913-c3b87705614f\",\"imageWidth\":750,\"swipes\":0,\"userid\":\"U9097gARoXOus96vT1uBHAcNPs03\",\"views\":1},\"reshared\":{\"caption \":\"Cool watch bro\",\"creationDate \":\"1546442937.593444\",\"id \":\"-LVEAdIk2KwDmxBj25pK \",\"imageHeight\":1000,\"imageUrl\":\"https://firebasestorage.googleapis.com/v0/b/flashtrend-bdcd3.appspot.com/o/posts/E346E4B7-31D8-4E9E-89F2-DA7C426C0537?alt:media&token:4936ce58-64bb-4d5a-b913-c3b87705614f\",\"imageWidth\":750,\"swipes\":0,\"userid\":\"U9097gARoXOus96vT1uBHAcNPs03\",\"views\":1}}"
guard let data = jsonStr.data(using: .utf8) else {
return
}
do {
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
return
}
guard let created = json["created"], let reshared = json["reshared"] else {
return
}
let result = [created, reshared]
print(result)
} catch {
print(error.localizedDescription)
}
let defaultConfiguration = URLSessionConfiguration.default
let operationQueue = OperationQueue.main
let defaultSession = URLSession(configuration: defaultConfiguration, delegate: self, delegateQueue: operationQueue)
if let url = URL(string: "https://newsapi.org/v1/articles?source=abc-news-au&sortBy=top&apiKey=47d2ce48babd47b1bc391b426b89ca23")
{
(defaultSession.dataTask(with: url) { (data, response, error) in
if error != nil{
return
}
do {
let resultJson = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:AnyObject]
if var dataDictionary = resultJson {
// dataDictionary["access_token"] as AnyObject
self.dataArray = dataDictionary["articles"] as! [Any]
var dataDictionary22 = self.dataArray[0] as! [String: Any] as [String : AnyObject]
let url = URL(string:
"\(String(describing: dataDictionary22["urlToImage"]!))")
print("url -> \(String(describing: url!))")
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
guard let data = data, error == nil else {
return
}
self.imageView.image = UIImage(data: data)
}
task.resume()
}
} catch {
print("Error -> \(error)")
}
}).resume()
}
i am trying to get news updates from open api through nsurlsession and it has dictionary->array->dictionary->at key "urlToImage"
but iam getting url like http://www.abc.net.au/news/image/8968140-1x1-700x700.jpg but not getting image file in data it was empty can any one minimige the code lenth and solve my problem
Using this piece of code, you can parse that specific URL response successfully, I have tested it in a Playground.
This: "\(String(describing: dataDictionary22["urlToImage"]!))" is not the way get a String from an AnyObject, you should use conditional casting.
if let url = URL(string: "https://newsapi.org/v1/articles?source=abc-news-au&sortBy=top&apiKey=47d2ce48babd47b1bc391b426b89ca23"){
URLSession.shared.dataTask(with: url, completionHandler: { data, response, error in
guard error == nil, let data = data else {
print(error!);return
}
guard let resultJson = (try? JSONSerialization.jsonObject(with: data)) as? [String:Any] else {
return
}
print(resultJson)
guard let articles = resultJson["articles"] as? [[String:Any]], let firstArticle = articles.first else { return }
guard let imageUrlString = firstArticle["urlToImage"] as? String, let imageUrl = URL(string: imageUrlString) else { return }
URLSession.shared.dataTask(with: imageUrl, completionHandler: { data, response, error in
guard error == nil, let data = data else {
print(error!);return
}
let image = UIImage(data: data)
DispatchQueue.main.async {
self.imageView.image = image
}
}).resume()
}).resume()
}
If you want to get all article pictures (in your question you were only parsing the first one), just change guard let articles = resultJson["articles"] as? [[String:Any]], let firstArticle = articles.first else { return } to the following:
for article in articles {
guard let imageUrlString = article["urlToImage"] as? String, let imageUrl = URL(string: imageUrlString) else { return }
URLSession.shared.dataTask(with: imageUrl, completionHandler: { data, response, error in
guard error == nil, let data = data else {
print(error!);return
}
let image = UIImage(data: data)
//use the image
}).resume()
}
Have been researching on the parsing for quite a bit. With plethora of information avilable for JSON nothing seems to explain how to do in a sensible way to extract information with swift 3.
This is what got so far
func getBookDetails() {
let scriptUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546" .
let myurl = URL(string:scriptUrl)
let request = NSMutableURLRequest(url: myurl!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myurl! ) { (data, response, error) in
if error != nil{
print("THIS ERROR",error!)
return
} else{
if let mydata = data{
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let dictonary = myJson["items"] as AnyObject? {
print("the DICTONARY",dictonary) // ----> OUTPUT
if let dictonaryAA = dictonary["accessInfo"] as AnyObject? {
print("the accessInfo",dictonaryAA)
}
}
} catch{
print("this is the in CATCH")
}
} //data
}
}
task.resume()
}
}
OUTPUT :
the DICTONARY (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
=============
RELEVANT DATA as in https://www.googleapis.com/books/v1/volumes?
q=isbn:9781451648546"
==========================
title = "Steve Jobs";
};
}
)
Just need to parse through the json data to get the name, author and title of the book with reference to isbn.
Know there should be a better way to do things that is easily understandable to someone new into the language
You can parse the api in two ways
Using URLSession:
let rawDataStr: NSString = "data={\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"
self.parsePostAPIWithParam(apiName: "get_posts", paramStr: rawDataStr){ ResDictionary in
// let statusVal = ResDictionary["status"] as? String
self.postsDict = (ResDictionary["posts"] as! NSArray!) as! [Any]
print("\n posts count:",self.postsDict.count)
}
func parsePostAPIWithParam(apiName:NSString, paramStr:NSString,callback: #escaping ((NSDictionary) -> ())) {
var convertedJsonDictResponse:NSDictionary!
let dataStr: NSString = paramStr
let postData = NSMutableData(data: dataStr.data(using: String.Encoding.utf8.rawValue)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://13.12..205.248/get_posts/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = nil
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse as Any)
do{
if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
convertedJsonDictResponse = convertedJsonIntoDict.object(forKey: apiName) as? NSDictionary
// callback for response
callback(convertedJsonDictResponse)
}
} catch let error as NSError {
print(error)
}
}
Using Alamofire
func AlamofirePOSTRequest() {
let urlString = "http://13.12..205.../get_posts/"
let para = ["data": "{\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"]
Alamofire.request(urlString, method: .post, parameters: para , headers: nil).responseJSON {
response in
switch response.result {
case .success:
print("response: ",response)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["posts"].arrayObject {
self.postsDict = resData as! [[String:AnyObject]]
}
print("\n \n alomafire swiftyJsonVar: ",swiftyJsonVar)
break
case .failure(let error):
print(error)
}
}
}
})
dataTask.resume()
}
First of all, all JSON types are value types in Swift 3 so the most unspecified type is Any, not AnyObject.
Second of all, there are only two collection types in the JSON type set, dictionary ([String:Any]) and array ([Any], but in most cases [[String:Any]]). It's never just Any nor AnyObject.
Third of all, the given JSON does not contain a key name.
For convenience let's use a type alias for a JSON dictionary:
typealias JSONDictionary = [String:Any]
The root object is a dictionary, in the dictionary there is an array of dictionaries for key items. And pass no options, .mutableContainers is nonsense in Swift.
guard let myJson = try JSONSerialization.jsonObject(with: mydata) as? JSONDictionary,
let items = myJson["items"] as? [JSONDictionary] else { return }
Iterate through the array and extract the values for title and authors which is an array by the way. Both values are in another dictionary for key volumeInfo.
for item in items {
if let volumeInfo = item["volumeInfo"] as? JSONDictionary {
let title = volumeInfo["title"] as? String
let authors = volumeInfo["authors"] as? [String]
print(title ?? "no title", authors ?? "no authors")
The ISBN information is in an array for key industryIdentifiers
if let industryIdentifiers = volumeInfo["industryIdentifiers"] as? [JSONDictionary] {
for identifier in industryIdentifiers {
let type = identifier["type"] as! String
let isbn = identifier["identifier"] as! String
print(type, isbn)
}
}
}
}
You are doing wrong in this line
if let dictonaryAA = dictonary["accessInfo"] as AnyObject?
because dictonary here is an array not dictionary. It is array of dictionaries. So as to get first object from that array first use dictonary[0], then use accessInfo key from this.
I am attaching the code for your do block
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let array = myJson["items"] as AnyObject? {
print("the array",array) // ----> OUTPUT
let dict = array.object(at: 0) as AnyObject//Master Json
let accessInf = dict.object(forKey: "accessInfo") //Your access info json
print("the accessInfo",accessInf)
}
}
Hope this helps you.
From the below code it doesn't show any error but it gets run time appDelegate error and its reason is Terminating app due to uncaught exception 'NSInvalidArgumentException'. Please, tell what I want to do to get rid of this...
var urlstring: String!
urlstring = "\(signInAPIUrl)rooms/room_type"
urlstring = urlstring.replacingOccurrences(of: "Optional(", with: "")
urlstring = urlstring.replacingOccurrences(of: ")", with: "")
urlstring = urlstring.addingPercentEncoding( withAllowedCharacters: .urlQueryAllowed)!
print(urlstring)
self.callSiginGBAPI(url: "\(urlstring!)")
}
func callSiginGBAPI(url : String){
print("url: \(url)")
Alamofire.request(url).responseJSON { (response) in
self.parseDataGB(JSONData: response.data!)
print("Response:\(response)")
}
}
func parseDataGB(JSONData : Data){
do{
let readableJSon = try JSONSerialization.jsonObject(with: JSONData, options: .mutableContainers) as! jsonSTD
print(" !!! \(readableJSon[0])")
let value = readableJSon[0] as AnyObject
if let final = value.object(forKey: "id")
{
print(final)
let first_name:String = value.object(forKey: "id") as! String
let last_name:String = value.object(forKey: "type") as! String
let list_type:String = value.object(forKey: "list_type") as! String
print(first_name)
print(last_name)
print(list_type)
} else{
}
}
catch{
print(error)
}
}
Use the following extension to convert data to JSON object:
extension Data {
func JSONObject() -> AnyObject? {
do {
let content = try JSONSerialization.jsonObject(with: self as Data, options: JSONSerialization.ReadingOptions.allowFragments)
return content as AnyObject?
} catch _ as NSError {
return nil
}
}
var string: String {
return String(data: self as Data, encoding: String.Encoding.utf8) ?? "Error: Not able to get string from the data."
}
}
in response
let info = response.data?.JSONObject()
After i convert my project to swift 3 i had run time error when i try to parsing JSON object to NSArray this my code
let receipt: Data! = try? Data(contentsOf: receiptURL)
if receipt == nil {//NSData(contentsOfURL: receiptURL, options: nil, error: nil)!
// validateReceipt(false)
return
}
let base64Data: String = receipt.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0)) as String
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last?.path//
do{
let payload: Dictionary = ["receipt-data" : base64Data as String, "password": sharedSecret as String ]
let receiptData = try JSONSerialization.data(withJSONObject: payload, options: JSONSerialization.WritingOptions.prettyPrinted)
let request = NSMutableURLRequest(url: URL(string: serverURL)!, cachePolicy: NSURLRequest.CachePolicy.useProtocolCachePolicy, timeoutInterval: 10) // ur website
request.httpMethod = "POST"
request.httpBody = receiptData as! Data?
var response: URLResponse?
let resultData = try NSURLConnection.sendSynchronousRequest(request as URLRequest, returning: &response)
do{
let json = try JSONSerialization.jsonObject(with: resultData, options: .mutableLeaves) as? NSDictionary
let statusCode = (json?.object(forKey: "status") as! NSNumber).intValue
switch(statusCode){
case 21000, 21002, 21003, 21004, 21006:
return
// break
case 21005:
//Server is not available: Save recipt data and try again later
return
case 21007:
validateReceipt(true)
return
case 21008:
validateReceipt(false)
return
default:
break
}
//print("**********************************")
//print("json :: ", json )
//print("**********************************")
if let parseJSON = json {
let latest_receipt_info:NSArray = (parseJSON.object(forKey: "receipt") as AnyObject).object(forKey: "in_app") as! NSArray
//var ms = 0
//parse the json reponse and add the objects into array
for i in 0 ..< latest_receipt_info.count {
let object:[String: String] = latest_receipt_info.object(at: i) as! [String : String]
let strMS = object["expires_date_ms"]
let prodId = object["product_id"]
if(prodId == iAPItems["LifeTime"] || prodId == iAPItems["Remove_Ads"]){
latestObjectsForEachProduct[prodId!] = object
}else{
if let oldTransaction = latestObjectsForEachProduct[prodId!] {
let strOldMS = oldTransaction["expires_date_ms"]
print("oldTransaction :: ",prodId, " :: ",strOldMS)
if( strMS!.toDouble() > strOldMS!.toDouble() ){
//latestObject = object
latestObjectsForEachProduct[prodId!] = object
//ms = str!.toInt()
}
}else{
latestObjectsForEachProduct[prodId!] = object
}
}
}
the errors appears on this line
let latest_receipt_info:NSArray = (parseJSON.object(forKey: "receipt") as AnyObject).object(forKey: "in_app") as! NSArray
i dont know what should i do , if any one can help
Note that on swift 2 its work
Try this code
let json = try JSONSerialization.jsonObject(with: resultData, options: .mutableLeaves) as? NSDictionary
let statusCode = (json?.object(forKey: "status") as! NSNumber).intValue
to replace this code
let json = try JSONSerialization.jsonObject(with: resultData, options: .mutableLeaves) as? [String:AnyObject]
let statusCode = (json?["status"] as! NSNumber).intValue
let latest_receipt_info:NSArray = (parseJSON.object(forKey: "receipt") as AnyObject).object(forKey: "in_app") as! NSArray
To replace this code
let latest_receipt_info:NSArray = parseJSON["receipt"]!["in_app"] as! NSArray