Why have a Array Empty and Error in swift4? - arrays

I've spent a few hours trying to get a fetch to work in my film sheet. I need to open film's View of my colectionview Items. I could follow different guide and post but it always give me an empty array. I'm newbie, sorry for my question but I need your help.
Here's my code:
var taskArrayScheda : NewFilm?
override func viewDidLoad() {
super.viewDidLoad()
self.fetchData()
if(self.taskArrayScheda != nil) {
let schedaOk = taskArrayScheda
mostraDatiNellaScheda(schedaOk!)
} else { print("errore array vuoto") }
}
func mostraDatiNellaScheda(_ sched:NewFilm) {
// get title
titoloScheda.text = sched.titolo
}
func fetchData() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "NewFilm")
do {
taskArrayScheda = try context.fetch(NewAnime.fetchRequest())
πŸ’₯ERROR ::::: Cannot assign value of type '[Any]' to type 'NewFilm?'
} catch {
print(error)
}
}

The fetch() returns an array. But currently you assign the fetch() result to the single object var taskArrayScheda.
You'll need something like:
var taskArrayScheda: [NewFilm]?
Then you should do:
taskArrayScheda = try context.fetch(NewAnime.fetchRequest()) as? [NewFilm]
I assume here that NewAnime is a subclass of NewFilm, which seems to make sense looking at these two class names.

Related

Could not get JSONArray in variable Swift

So basically I want to make a TableList from my REST service. The REST service can be decoded by this code block:
func getAllParkeergarages(_ completion: #escaping ([Parkeergarage]) -> ()) {
if let url = URL(string: "http://localhost:8080/parkeergarages") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode([Parkeergarage].self, from: data)
print(res)
completion(res)
return
} catch let error {
print(error)
}
}
}.resume()
}
}
By using this codeblock I can print the whole JSON in my terminal:
getAllParkeergarages { (array) in
print(array)
}
To get the data in a TableView I need to have the data in a variable. But here is where I get stuck. I tried some different methodes like:
private var data: [Parkeergarage] = getAllParkeergarages { (array) in
return array
}
but is gives me an error: 'Cannot convert value of type '()' to specified type '[Parkeergarage]'. Can someone help me get the result of the function in the variable?
you should do
private var data: [Parkeergarage] = []
in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
getAllParkeergarages { (array) in
self.data = array
self.tableView.reloadData()
}
}
I cannot explain any more.

I want to add the results of a Fetch request from a Core Data entity into an Array

I am using the following code to retrieve rows from an entity in my Core Data DB. I am able to successfully fetch the data. I can also access the rows of the entity and add it to an array. However when I try using the array 'outside' the do { } enclosure, I am only able to read the last array item value. Please assist me.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest = NSFetchRequest<T01_test_results>(entityName: "T01_test_results")
//3
do {
let tests = try managedContext.fetch(fetchRequest)
for test in tests {
testItem.max_number = test.c01_max_number!
testItem.results = test.c01_results!
testItem.test_date = test.c01_test_date!
testItem.timesTable = test.c01_timesTable!
print("In Loop -- \(testItem.timesTable)")
testItem.total_correct = test.c01_total_correct!
testItem.total_questions = test.c01_total_questions!
testArray.append(testItem)
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
print("Single element -> \(testArray[3].timesTable)")
}
Problem seems to be solved. I was using a Class based object to store the retrieved values. Changed this to a struct based type and seems to be working fine!

Assigning String to Array is Returning Nil - Swift

When web scraping my forum in swift I can run the following code to print in console the titles of each forum topic:
enum HTMLError: Error {
case badInnerHTML
}
class DiscussionBoardResponse {
init(_ innerHTML: Any?) throws {
guard let htmlString = innerHTML as? String else { throw HTMLError.badInnerHTML }
let doc = try SwiftSoup.parse(htmlString)
let titles = try doc.getElementsByClass("Forum_NormalBold").array()
var nestedLoginController: LoginController!
for i in 0..<titles.count {
let title = try titles[i].text()
print(title)
print("")
}
}
}
Now above I need to assign what is in the title to an array outside of the class. So I created a variable (which could be wrong and maybe that is the issue):
var discussionTitles = [String?]()
My viewController is named LoginController. Now I tried adding this to the DiscussionBoardResponse to assign titles to the variable:
class DiscussionBoardResponse {
//let discussionTopics: [DiscussionTopicSingle]
init(_ innerHTML: Any?) throws {
guard let htmlString = innerHTML as? String else { throw HTMLError.badInnerHTML }
let doc = try SwiftSoup.parse(htmlString)
let titles = try doc.getElementsByClass("Forum_NormalBold").array()
var nestedLoginController: LoginController!
for i in 0..<titles.count {
let title = try titles[i].text()
//tried converting title into a string in case it was't
//guard let titleString = title as? String else { return }
nestedLoginController?.discussionTitles[i] = title
print(nestedLoginController?.discussionTitles[i])
print("")
}
}
}
Now when I print the nestedLoginController?.discussionTitles[I] It only prints out nil for every title. What am I doing wrong?
UPDATE: Im now rethinking a little on how to do this because the other way is not working, so inside where I click the button which takes me to DiscussionBoardResponse looks like this:
do {
let discussionResponse = try DiscussionBoardResponse(innerLoginHTML)
print("Got Response")
} catch{}
I attempt to send over the variable discussionTitles
let discussionResponse = try DiscussionBoardResponse(innerLoginHTML, test: self.discussionTitles)
and in the DiscussionResponse I have now:
init(_ innerHTML: Any?, test: [String] = []) throws {
guard let htmlString = innerHTML as? String else { throw HTMLError.badInnerHTML }
let doc = try SwiftSoup.parse(htmlString)
let titles = try doc.getElementsByClass("Forum_NormalBold").array()
//var nestedLoginDelete: [String] = []
for i in 0..<titles.count {
let title = try titles[i].text()
test.append(title)
}
}
}
and I now get the error: Cannot use mutating member on immutable value: 'test' is a 'let' constant. Im not sure even if this worked is im actually manipulating the variable discussionTitles or if im just manipulating some instance of it? Either way what am I doing wrong?

Fetched data from Parse not saving in Array - Swift 2.0

I am working with Parse and would like to download images for offline use. I understand that this is not possible with Local Datastore so I have decided to add them to Core Data.
I have successfully downloaded the PFFiles and put them in to an Array. I am then trying to create an Array for the NSData, but the Array count is always 0 when I use the code below
class DealsDownloadViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
var trailID = [Int]()
var trailStep = [Int]()
var dealNumber = [Int]()
var imageFile = [PFFile]()
var imagesArray = [UIImage]()
var imageDataArray = [NSData]()
var number = 0
override func viewDidLoad() {
super.viewDidLoad()
let appDel: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = appDel.managedObjectContext
let dealsQuery = PFQuery(className: ("Deals"))
dealsQuery.orderByAscending("TrailId")
dealsQuery.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let objects = objects {
for object in objects {
self.trailID.append(object["TrailID"] as! Int)
self.trailStep.append(object["TrailStep"] as! Int)
self.dealNumber.append(object["dealNumber"] as! Int)
self.imageFile.append(object["dealImage"] as! PFFile!)
}
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
self.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
} else {print("error here")}
})
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
}
} else {print("problem making arrays")}
}
}
If I move the Print statement up, I just get it printing every iteration of the loop.
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
self.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
} else {print("error here")}
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
})
}
} else {print("problem making arrays")}
}
}
In this case I can see that the data is added to both the imagesArray and imageDataArray.
This seems like such a simple issue but I am going crazy over it. What am I doing wrong, and is this the most efficient way of adding this data to Core Data? Am I overlooking something obvious?
I am new to programming so please do point out any mistakes I have made, and I am especially new as a questioner to stackoverflow (you have been indispensable while learning) so please let me know if you need any information that I have missed.
Thanks for your help.
Update 1
I have tried editing the code as explained in the comments and I am still getting the same result. I have moved the Print statement around on this code and it is still giving me the same results as above.
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
weak var aBlockSelf = self
let image = UIImage(data: imageData!)
aBlockSelf!.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
}
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
})
}
} else {print("problem making arrays")}
}
}
Am I missing something very simple? Thanks again for your help.
Update 2
This is the same code with (I think) the print statements moved outside of the For Loop. This is giving me counts of 9,9,9,0,0 from the print statements, whereas I think I should be expecting 9,9,9,9,9.
for file in self.imageFile {
let dealImage = file
dealImage.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
weak var aBlockSelf = self
let image = UIImage(data: imageData!)
aBlockSelf!.imageDataArray.append(imageData!)
self.imagesArray.append(image!)
}
})
}
print(self.trailID.count)
print(self.trailStep.count)
print(self.dealNumber.count)
print(self.imageDataArray.count)
print(self.imagesArray.count)
} else {print("problem making arrays")}
}
}
There is no issue here!
You are being deceived by the way asynchronous block works. Asynchronous block gets queued up to get executed some point later in next run loop.
Your first print statement is just after you pass the code to block which is yet to be executed. Which is why you do see your image array empty.

Swift Array: [AnyObject] vs. [xxxxxxxxClass] and the method "append"

Here's my code. You don't need to look at all of it. I added comments where I'm confused:
class ProductData: NSObject {
var title = ""
var icon = ""
private init(dict: NSDictionary){
title = dict["title"] as! String
icon = dict["icon"] as! String
super.init()
}
class func getTheData(fromJSONPath JSONPath: String) -> [ProductData] {
let JSONData = NSData(contentsOfFile: JSONPath)!
var JSONArray = [[String : AnyObject]]()
do {
JSONArray = try NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions.MutableContainers) as! [Dictionary]
} catch { print("error")}
-----------------------------------------------------------------------------------------
//↓↓↓↓↓↓↓↓↓ different: data = "[AnyObject]()" or "[ProductData]()" ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
var data = [AnyObject]()
// var data = [ProductData]()
for d in JSONArray {
data.append(ProductData(dict: d))
}
return data as! [ProductData]
// return data
//↑↑↑↑↑↑↑↑↑ and here: return "data as! [ProductData]" or "data" ↑↑↑↑↑↑↑↑↑↑↑↑↑↑
}
}
I use "var data = [ProductData](), retun data" first. There's no error or warning, but when I run my app, and run to the code data.append(ProductData(dict: d)), it crashes with the error: thread 1:exc_bad_access(code=1,address=0x10). What?!
I found a way to fix it: if I use var datas = [AnyObject]() and return datas as! [ProductData], it works very well.
I am so confused:
Why does [AnyObject] make the code OK?
When I use [ProductData], why does the code: data.append(ProductData(dict: d)) crash?
What is the different between [AnyObject] and [ProductData]?
Your original version works for me (screenshot) (only slightly modified for testing with my data). You shouldn't have to do this dance, something else is causing trouble.
I suggest cleaning up your class a bit and take advantage of Swift 2 using guard, map and error. It will be easier to debug and will work more efficiently anyway.
Here's an example. The only difference is that I'm using NSURL to access the data in my case and I've removed the icon value, but it's easy to change it back to your case.
class ProductData: NSObject {
var title = ""
private init(dict: [String : AnyObject]){
if let t = dict["title"] as? String { self.title = t }
super.init()
}
class func getTheData(fromJSONPath JSONPath: String) -> [ProductData] {
do {
// safely unwrap and typecast the values else return empty array
guard let url = NSURL(string: JSONPath),
let JSONData = NSData(contentsOfURL: url),
let JSONArray = try NSJSONSerialization.JSONObjectWithData(JSONData, options: [])
as? [[String : AnyObject]] else { return [] }
return JSONArray.map() { ProductData(dict: $0) }
} catch {
// this `error` variable is created by the `catch` mechanism
print(error)
// return empty array if unkown failure
return []
}
}
}
let test = ProductData.getTheData(fromJSONPath: "http://localhost:5678/file/test.json")
Note: I'm sure you know it but just in case for the readers, NSData(contentsOf... is a synchronous function, so it will block the main thread (unless executed from a background thread). It's better practice to use asynchronous functions when possible.

Resources