I am trying to follow this tutorial and after some minor trial and error runs I have ran into a problem that I don't quite understand. I am receiving this error(?) exc_bad_instruction. I have read that it happens usually when you try to unwrap a nil or the nil isn't valid?
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
var cell:iGameTableViewCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? iGameTableViewCell
if(cell == nil) {
cell = NSBundle.mainBundle().loadNibNamed("iGameTableViewCell", owner: self, options: nil)[0] as? iGameTableViewCell
}
if let pfObject = object {
cell?.gameNameLabel?.text = pfObject["name"] as? String
var votes:Int? = pfObject["votes"] as? Int
if votes == nil {
votes = 0
}
cell?.gameVotesLabel?.text = "\(votes!) votes"
var credit:String? = pfObject["author"] as? String
if credit != nil {
cell?.gameCreditLabel?.text = "\(credit!)"
}
cell?.gameImageView?.image = nil
if var urlString:String? = pfObject["url"] as? String {
var url:NSURL? = NSURL(string: urlString!)
if var url:NSURL? = NSURL(string: urlString!) {
var error:NSError?
var request:NSURLRequest = NSURLRequest(URL: url!, cachePolicy: NSURLRequestCachePolicy.ReturnCacheDataElseLoad, timeoutInterval: 5.0)
NSOperationQueue.mainQueue().cancelAllOperations()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: {
(response:NSURLResponse!, imageData:NSData!, error:NSError!) -> Void in
cell?.gameImageView?.image = UIImage(data: imageData)
})
}
}
}
return cell
}
See these two lines:
var url:NSURL? = NSURL(string: urlString!)
if var url:NSURL? = NSURL(string: urlString!) {
First, you can't want them both; they both do the same thing in
different ways.
Second, the way the first line operates is dangerous. Delete it.
Third, delete the exclamation mark from urlString! and the type
declaration NSURL?.
Now you will just have this:
if var url = NSURL(string: urlString) {
And that is safe and is how such unwrapping should look.
EDIT: Just to clarify: This is a terribly self-defeating thing to do:
if var urlString:String? = pfObject["url"] as? String
Here's why. The if var ... = or if let ... = construct unwraps the Optional on the right side of the equal sign. That is its purpose: to unwrap the Optional, safely. But by adding the :String? declaration, you wrap it right back up again in an Optional, thus defeating the entire purpose of this construct! You want to say this:
if var urlString = pfObject["url"] as? String
Now urlString, if it is anything at all, is an unwrapped String, which is what you're after.
Related
To parse json i have following function
func single_news(userid: Int) {
var request = URLRequest(url: URL(string: news_url)!)
request.httpMethod = "POST"
//Pass your parameter here
let postString = "userid=\(userid)"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print("error=(error)")
return
}
let json: Any?
do
{
json = try JSONSerialization.jsonObject(with: data, options: [])
print("abcnews")
//here is your JSON
print(json)
let jsonValue : NSDictionary = json as! NSDictionary
self.results = jsonValue.object(forKey: "data") as! [[String:String]]
self.DiscoveryNewsTableView.delegate = self
self.DiscoveryNewsTableView.dataSource = self
self.DiscoveryNewsTableView.reloadData()
// let _ = getData.shared.getDataForTableView(dict: json)
}
catch
{
return
}
guard let server_response = json as? NSDictionary else
{
return
}
}
task.resume()
}
To get data the class is created
class getData: NSObject {
var descriptionn : String = ""
var image : String = ""
// static let shared = getData()
func getDataForTableView(results: [[String:String]], index : Int){
var productArray = [String:String]()
productArray = results[index]
descriptionn = productArray["description"]!
image = productArray["images"]!
}
}
To display data in table view
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "discoveryNewscell") as! DiscoveryNewsTableViewCell
// if results.count > 0{
classObject.getDataForTableView(results: results, index: indexPath.row)
cell.sneakerImageView.image=filteredsneakernews[indexPath.row].image
print("abc image"+classObject.image)
cell.newsTitle.text = classObject.descriptionn
// }
return cell
}
How to display the image .Image(classObject.image) in string format how to display image view on table view ?you can download the code from this link .https://drive.google.com/file/d/1bVQsuSQINSa6YRwZe2QwEjPpU_m7S3b8/view?usp=sharing
You're wanting to display an image but you only have the URL to that image and not the image itself so you'll need to download it, then display it. I have a class I use a lot that allows you to simply call one line to download AND cache the image so you'll be able to do something like this:
classObject.getDataForTableView(results: results, index: indexPath.row)
let image_url = filteredsneakernews[indexPath.row].image
cell.sneakerImageView.loadImageUsingCacheWithUrlString(urlString: image_url!)
To do this, you'll have to copy the class below and inside your cell class, you’ll want to change the imageView type from a standard UIImageView to a CustomImageView for example:
let imageView: CustomImageView!
//
import UIKit
let imageCache = NSCache<NSString, UIImage>()
class CustomImageView: UIImageView {
var imageUrlString: String?
func loadImageUsingCacheWithUrlString(urlString: String) {
imageUrlString = urlString
if let cachedImage = imageCache.object(forKey: urlString as NSString) {
self.image = cachedImage
return
}
self.image = nil
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil { return }
DispatchQueue.main.async {
if let downloadedImage = UIImage(data: data!) {
if self.imageUrlString == urlString {
if self.imageUrlString != "" {
self.image = downloadedImage
} else {
self.image = nil
}
}
imageCache.setObject(downloadedImage, forKey: urlString as NSString)
}
}
}).resume()
}
}
After upgrading to iOS10 users started complaining about crashes of my app.
I am testing it with iOS10 on the simulator and indeed the app crashes with a message saying "Could not cast value of type '__NSArrayI' to 'NSMutableArray'". Here's my code, please help:
import Foundation
protocol getAllListsModel: class {
func listsDownloadingComplete(downloadedLists: [ContactsList])
}
class ListsDownloader: NSObject, NSURLSessionDataDelegate{
//properties
weak var delegate: getAllListsModel!
var data : NSMutableData = NSMutableData()
func downloadLists() {
let urlPath: String = "http://..."
let url: NSURL = NSURL(string: urlPath)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration() //defaultSessionConfiguration()
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data);
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
self.parseJSON()
print("Lists downloaded")
}
}
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
do{
try jsonResult = NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
var downloadedLists: [ContactsList] = []
for i in 0...jsonResult.count-1 {
jsonElement = jsonResult[i] as! NSDictionary
let tempContactsList = ContactsList()
//the following insures none of the JsonElement values are nil through optional binding
let id = jsonElement["id"] as? String
let name = jsonElement["name"] as? String
let pin = jsonElement["pin"] as? String
let lastUpdated = jsonElement["created"] as? String
let listAdminDeviceID = jsonElement["admin"] as? String
tempContactsList.id = id
tempContactsList.name = name
tempContactsList.pin = pin
tempContactsList.lastUpdated = lastUpdated
tempContactsList.listAdmin = listAdminDeviceID
downloadedLists.append(tempContactsList)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.listsDownloadingComplete(downloadedLists)
})
}
}
Even in iOS 9, there was no guarantee NSJSONSerialization.JSONObjectWithData(_:options:) would return mutable object or not. You should have specified NSJSONReadingOptions.MutableContainers.
And in your code, you are not modifying jsonResult, which means you have no need to declare it as NSMutableArray. Just replace NSMutableArray to NSArray, and then you have no need to specify NSJSONReadingOptions.MutableContainers.
But as vadian is suggesting, you better use Swift types rather than NSArray or NSDictionary. This code should work both in iOS 9 and 10.
func parseJSON() {
var jsonResult: [[String: AnyObject]] = [] //<- use Swift type
do{
try jsonResult = NSJSONSerialization.JSONObjectWithData(self.data, options: []) as! [[String: AnyObject]] //<- convert to Swift type, no need to specify options
} catch let error as NSError {
print(error)
}
var downloadedLists: [ContactsList] = []
for jsonElement in jsonResult { //<- your for-in usage can be simplified
let tempContactsList = ContactsList()
//the following insures none of the JsonElement values are nil through optional binding
let id = jsonElement["id"] as? String
let name = jsonElement["name"] as? String
let pin = jsonElement["pin"] as? String
let lastUpdated = jsonElement["created"] as? String
let listAdminDeviceID = jsonElement["admin"] as? String
tempContactsList.id = id
tempContactsList.name = name
tempContactsList.pin = pin
tempContactsList.lastUpdated = lastUpdated
tempContactsList.listAdmin = listAdminDeviceID
downloadedLists.append(tempContactsList)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.listsDownloadingComplete(downloadedLists)
})
}
Try this and check it on iOS 10 devices.
(The as! conversion would cause some weird crashes when your server is malfunctioning, but that would be another issue, so I keep it there.)
I am retrieving posts in a data handler from a DB for a news feed and I run a php script and echo the JSON encoded data back to my application, at which point the data is parsed and stored in a model of a "post", there is a protocol that is used in the view controller to get the data once it has been downloaded. My problem is that I am getting the notorious "Unexpectedly found nil when unwrapping optional value" error when I pass the NSMutableArray of "post" objects to the function "itemsDownloaded" which is function of the protocol. I checked all the values being parsed and they exist, and I also checked the count of the array to make sure it has values. The exception is occurring on the line self.delegate.itemsDownloaded(posts)
The code to handle the data is this :
import Foundation
protocol PostDataHandlerProtocol: class {
func itemsDownloaded(items: NSArray)
}
class PostDataHandler: NSObject, NSURLSessionDataDelegate {
weak var delegate: PostDataHandlerProtocol!
var data : NSMutableData = NSMutableData()
//The path to the php script to be executed
let urlPath: String = "www.something.com/myphpscript.php"
func downloadItems() {
let url: NSURL = NSURL(string: urlPath)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data);
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
print("Data downloaded")
self.parseJSON()
}
}
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
do{
jsonResult = try NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
let posts: NSMutableArray = NSMutableArray()
for(var i = 0; i < jsonResult.count; i++)
{
jsonElement = jsonResult[i] as! NSDictionary
let post = PostModel()
//The following insures none of the JsonElement values are nil through optional binding
let username = jsonElement["username"] as? String
let imagePath = jsonElement["user_imagePath"] as? String
let postID = (jsonElement["post_id"] as! NSString).integerValue
let postRep = (jsonElement["post_rep"] as! NSString).integerValue
let postType = jsonElement["post_type"] as? String
let postDate = jsonElement["post_date"] as? String
let comment = jsonElement["comment"] as? String
post.username = username
post.imagePath = imagePath
post.postID = postID
post.postRep = postRep
post.postType = postType
post.postDate = postDate
post.comment = comment
posts.addObject(post)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.itemsDownloaded(posts)
})
}
}
In my view controller, I create a new data handler let postHandler = PostDataHandler() and then once the view has loaded I call postHandler.downloadItems() and in the view controller declaration I conformed to the protocol and implemented itemsDownloaded:
func itemsDownloaded(items: NSArray) {
allPosts = items as! [PostModel]
indicator.stopAnimating()
self.tableVIew.reloadData()
}
Does anyone know why this is happening? I tried to look at the numerous postings regarding this error as I'm aware it's quite common, but couldn't find anything that helped me. Many of the postings say there should be a check to ensure it's not nil, the problem is I didn't think NSMutableArray can be optional, and also I checked all the values and they don't appear to be nil, so those answers did not help me. In the thread exceptions it says something related to a closure, which I think could be causing the issue, I'm just not exactly sure what or how.
None of the code that you showed ever set your PostDataHandler's delegate property to anything, so it is reasonable to suppose that it is still nil, which perfectly explains why you crash at runtime when you try to access it as if it were an actual object.
I am creating a new NSManagedObject called "translation". Within the translation I need to create two additional NSManagedObjects called "phrase". Sometimes one of the phrase assignments will throw an error, but when I inspect the values they all look like they were created just fine. What gives???
Creating A Translation Object:
func getOrCreateTranslation(package: Package?, data: NSDictionary) -> Translation {
let translationId = data["id"] as! NSNumber
if let translation = self.getTranslation(translationId) {
return translation
} else {
let context = LocalDataStorage().context
let translation = NSEntityDescription.insertNewObjectForEntityForName("Translation", inManagedObjectContext: context) as! Translation
translation.id = translationId
let fromPhrase = data["from_phrase"]! as! NSDictionary
let toPhrase = data["to_phrase"]! as! NSDictionary
let pm = PhraseManager()
//*******
// *SOMETIMES* ONE OF THESE LINES FAIL WITH BAD_EXC_ACCESS code=1
translation.fromPhrase = pm.getOrCreatePhrase(fromPhrase)
translation.toPhrase = pm.getOrCreatePhrase(toPhrase)
//******
if package != nil {
package!.addTranslationObject(translation)
}
return translation
}
}
Creating A Phrase Object:
func getOrCreatePhrase(data: NSDictionary) -> Phrase {
// check if phrase exists
let phraseId = data["id"] as! NSNumber
if let phrase = self.getPhrase(phraseId) {
return phrase
} else {
let context = localDataStorage.context
let lm = LanguageManager()
let phrase = NSEntityDescription.insertNewObjectForEntityForName("Phrase", inManagedObjectContext: context) as! Phrase
phrase.id = phraseId
phrase.text = data["text"] as! String
phrase.audioUrl = data["audio_url"] as? String
let code = data["language"]!["language_code"] as! String
phrase.language = lm.getLanguageFromCode(code)
return phrase
}
}
Call Made to API:
func getPackageTranslations(package: Package, completion: ([Translation])-> Void) {
let currentLanguage: Language = LanguageManager().getCurrentLanguage()!
let urlString = baseAPIString + "/groups/\(package.id!)/translations/?language_code=\(currentLanguage.code)"
let session = NSURLSession.sharedSession()
let serachUrl = NSURL(string: urlString)
let task = session.dataTaskWithURL(serachUrl!) {
(data, response, error) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
let jsonData: NSDictionary!
do {
jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSDictionary
} catch _ {
jsonData = NSDictionary()
}
let groupTranslationsData = jsonData["group_translations"] as! [NSDictionary]
var translations = [Translation]()
let context = LocalDataStorage().context
for groupTranslation in groupTranslationsData {
let translationData = groupTranslation["translation"] as! NSDictionary
let translation = TranslationManager().getOrCreateTranslation(package, data: translationData)
if translation.packages?.containsObject(package) == false {
//package.addTranslationObject(translation!)
//translation!.addPackageObject(package)
}
translations.append(translation)
}
do {
try context.save()
} catch {
print("There was a problem saving translation ")
}
dispatch_async(dispatch_get_main_queue(), {
completion(translations)
})
}
}
task.resume()
}
CoreData Context Class:
class LocalDataStorage {
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext!
init() {
context = appDelegate.managedObjectContext
}
}
This issue occurs when you create a NSManagedObjectContext with a concurrency pattern that it should be interacted on and you perform actions on the it on a thread different from the concurrency pattern specified during its initialization.
The completion block of NSURLSession.dataTaskWithURL is run on another thread, so you must dispatch to the type of thread specified in the context creation to perform any operation successfully on it.
If the concurrency type of your context is MainQueueConcurrencyType which is used in most cases, you must perform the context save method on the main queue.
dispatch_async(dispatch_get_main_queue()) {
do {
try context.save()
} catch {
print("There was a problem saving translation ")
}
completion(translations)
}
I am currently working with Swift 2.0 and created a new "Master-Detail Application" in X-Code. I need that when the user clicks the BarButtomItem, a new View appears requesting a text field that will do a search in the internet.
The code to open the ViewController is as follows:
func insertNewObject(sender: AnyObject) {
let mainStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let vc : ISBNRequest = mainStoryboard.instantiateViewControllerWithIdentifier("myISBN") as! ISBNRequest
presentViewController(vc, animated: true, completion: nil)
}
Once the user enters the text, and the search is finished, I need to add the results into to the TableView. To do this, Im calling an update function from the view Controller:
Function in ViewController:
#IBAction func buscarLibro(sender: AnyObject) {
print ("aqui estamos")
let mainStoryboard = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let mvc :MasterViewController = mainStoryboard.instantiateViewControllerWithIdentifier("myMVC") as! MasterViewController
var titulo : String = String()
var autores : [String] = [String]()
var portada : UIImage = UIImage()
let isbnTxt = ISBN.text!
let urls = "https://openlibrary.org/api/books?jscmd=data&format=json&bibkeys=ISBN"+isbnTxt
let url = NSURL(string: urls)
let datos:NSData? = NSData(contentsOfURL: url!)
if (datos == nil) {
//No hacer nada
print("datos nil")
} else {
//let texto = NSString(data:datos!, encoding: NSUTF8StringEncoding)
//self.textView.text = texto! as String
print("empieza busqueda")
do {
let json = try NSJSONSerialization.JSONObjectWithData(datos!, options: NSJSONReadingOptions.MutableLeaves)
let dico1 = json as! NSDictionary
print(dico1.allKeys)
var tmp = dico1["ISBN"+isbnTxt]
if (tmp != nil && tmp is NSDictionary) {
let dico2 = tmp as! NSDictionary
tmp = dico2["authors"]
if (tmp != nil && tmp is NSArray) {
let dico3 = tmp as! NSArray
titulo = (dico2["title"] as! NSString as String)
for id in dico3 {
print(id["name"])
autores.append(id["name"] as! NSString as String)
}
let cover = dico2["cover"]
if (cover != nil && cover is NSDictionary) {
let covers = cover as! NSDictionary
let url = NSURL(string: covers["medium"] as! NSString as String)
if let data = NSData(contentsOfURL: url!) {
portada = UIImage(data: data)!
}
}
print("LIBRO:" + titulo)
let libro : Libro = Libro(nombre : titulo,autores: autores, portada: portada)
mvc.libros.append(libro)
for x in mvc.libros {
print (x.nombre)
}
mvc.actualiza(libro)
}
}
} catch _ {
}
}
}
The mvc.actualiza function does the following:
func actualiza (libro:Libro) {
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context)
// If appropriate, configure the new managed object.
// Normally you should use accessor methods, but using KVC here avoids the need to add a custom class to the template.
newManagedObject.setValue(libro as? AnyObject, forKey: "timeStamp")
// Save the context.
do {
try context.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
//print("Unresolved error \(error), \(error.userInfo)")
abort()
}
}
When executing the first line, in crashes here:
let entity = NSEntityDescription.entityForName("Event", inManagedObjectContext: self.managedObjectContext!)
And I am getting the following error message:
fatal error: unexpectedly found nil while unwrapping an Optional value
What am I missing to include in the transition from one view controller to another one?
That looks like self.managedObjectContext has not been initialised. You could try putting this before the faulting line to check:
print("managedObjectContext: \(self.managedObjectContext)")