Knowing when to use a Double, String or Int when casting an Array from an API? - xcode

I am trying to cast multiple Arrays from a weather API. I was able to get the 5 day highs using a Double, then I was able to match them with the days of the week using a String.
Now I am trying to pull the the 5 days lows and the weather Icons associated to the day and no matter what I use Double, String or Int there is no data coming back.
Here is a part of my code that is working.
var temperatureArray: Array<Double> = Array()
var dateArray: Array<String> = Array()
var iconArray: Array<String> = Array()
var dayNumber = 0
var readingNumber = 0
if let jsonObj = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSDictionary {
if let mainArray = jsonObj!.value(forKey: "list") as? NSArray {
for dict in mainArray {
if let mainDictionary = (dict as! NSDictionary).value(forKey: "main") as? NSDictionary {
if let temperature = mainDictionary.value(forKey: "temp_max") as? Double {
if readingNumber == 0 {
temperatureArray.append(temperature)
} else if temperature > temperatureArray[dayNumber] {
temperatureArray[dayNumber] = temperature
}
} else {
print("Error: unable to find temperature in dictionary")
}
if let date = (dict as! NSDictionary).value(forKey: "dt_txt") as? String {
if readingNumber == 0 {
dateArray.append(date)
}
}
} else {
print("Error: unable to find main in dictionary")
}
if let icon = (dict as! NSDictionary).value(forKey: "icon") as? String {
if readingNumber == 0 {
iconArray.append(icon)
}
}
readingNumber += 1
if readingNumber == 8 {
readingNumber = 0
dayNumber += 1
}
}
I get no build errors, but cannot draw data for the icon array.

#Vadian, thank you!! I took me a while, but we were actually trying to break though 2 Array Strings to get to the "icon" key!! this is how I did it.
if let mainArray = jsonObj!.value(forKey: "list") as? NSArray {
for dict in mainArray {
if let weatherArray = (dict as AnyObject).value(forKey: "weather") as? NSArray {
if let weatherDictionary = weatherArray[0] as? NSDictionary {
if let icon = weatherDictionary.value(forKey: "icon") as? String {

Related

swift 3 core data fetch results

i have a core data modal with the entity "Users"
There are two attributes "firstName" and "secondName"
this is actually my code to fetch the results:
var content:[String] = []
func RequestData() {
let appdelegate = NSApplication.shared().delegate as! AppDelegate
let context = appdelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Kunden")
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
if results.count > 0 {
for result in results as! [NSManagedObject] {
if let firstname = result.value(forKey: "firstname") as? String {
content.append(firstname)
}
if let secondname = result.value(forKey: "secondname") as? String {
content.append(secondname)
}
}
}
tbl_kunden.reloadData()
} catch {
print("ERROR")
}
}
this works fine.
Problem is. I dont belive that
var content:[String] = []
is the correctly variable to take the results.
Because the output of print(content) is:
["Max", "Mustermann", "Max2", "Mustermann2", "Max3", "Mustermann3"]
Which variable type will be the best for this situation?
i think the result should be something like this:
+ID
++Max
++Mustermann
+ID
++Max2
++Mustermann2
+ID
++Max3
++Mustermann3
You should create custom User objects that are subclasses of NSManagedObject.
https://developer.apple.com/reference/coredata/nsmanagedobject
But if you don't need custom logic, you could change your variable to a Int:[String] Dictionary:
var content:[Int: [String]] = [:]
And append the data like this:
if let id = result.value(forKey: "firstname") as? Int {
let firstName = result.value(forKey: "firstname") as? String ?? "Unknown"
let secondName = result.value(forKey: "secondname") as? String ?? "Unknown"
let user = [id: [firstName, lastName]]
print("Fetched user: \(user.debugDescription)")
}

fatal : Array index out of range

Every child has a pick up point location consists of latitude and longitude that i fetch from sqlite database. I have childIdArray which is array of childID's. I want to calculate the estimate time(ETA) from current location of their respective drivers location i fetch from getCurrentLocationOfRespectiveDrivers() method with pickUp point of children that i fetch from sqlite database in form of array of latitude and longitude.
but i get error
fatal error: Array index out of range
in getCurrentLocationOfRespectiveDrivers() method.
Any help would be highly appreciated.. thanks in advance
var etaArray : [String] = []
var estimatedTime : String?
var latitudeArray : [Double] = []
var longitudeArray : [Double] = []
override func viewDidLoad() {
super.viewDidLoad()
let rs = ModelManager.sharedInstance.fetchingPickUpAnnotationsArray(ChildDetailsVC.parentID)
latitudeArray = rs.latPickUp
longitudeArray = rs.longPickUp
print("pickUpLatitudeArray = \(latitudeArray)")
print("pickUpLongitudeArray = \(longitudeArray)")
let result = ModelManager.sharedInstance.fetchingChildren( ChildDetailsVC.parentID)
self.childNameArray = result.childNames
self.childImageArray = result.childImages
self.childIDArray = result.chidIDs
self.childDriverArray = result.childDrivers
getCurrentLocationOfRespectiveDrivers()
for (var i = 0; i < childIDArray.count; i++)
{
etaArray.append("")
}
}
func getCurrentLocationOfRespectiveDrivers()
{
for( var i = 0 ; i < latitudeArray.count ; i++)
{
let url:NSURL = NSURL(string:"http://development.ssntpl.com/gogo_app/api.php?action=getDriverCurrentLocation")!
let request = NSMutableURLRequest(URL:url)
request.HTTPMethod = "POST"
let post:NSString = "child_id=\(childIDArray)"
request.HTTPBody = post.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil
{
print("error is \(error)")
return;
}
do
{
let myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if let parseJSON = myJSON
{
let Status = parseJSON["status"] as! Int
let Code = parseJSON["code"] as! Int
if (Status == 1 && Code == 200)
{
let Result = parseJSON["result"] as! NSArray
self.etaArray.removeAll()
//here i get the crash fatal error: Array index out of range
let latitudePickUp = self.latitudeArray[i]
let longitudePickUp = self.longitudeArray[i]
let CoordinatePickUp = CLLocation(latitude: latitudePickUp, longitude: longitudePickUp)
for res in Result
{
self.estimatedTime = ""
if (res["exists"] as! Int == 1)
{
let lastLatitude = res["latitude"] as! CLLocationDegrees
let lastLongitude = res["longitude"] as! CLLocationDegrees
let Location : CLLocation = CLLocation(latitude: lastLatitude, longitude: lastLongitude)
let meters:CLLocationDistance = Location.distanceFromLocation(CoordinatePickUp)
print(meters)
let ID = res["child_id"] as! String
let time = round(meters/40000 * 60)
self.estimatedTime = String(time)
self.etaArray.append(self.estimatedTime!)
}
else
{
self.etaArray.append("")
}
}
}
else
{
}
}
}
catch
{
print(error)
}
}
//executing the task
task.resume()
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}

Could not cast value of type '__NSCFNumber' (0x10e59f3c0) to 'NSString' (0x10dba7ad8) swift 3

I've been tying to figure out this one for a few days now... Here is my original code:
if let ratingDic = dictionary["rating"] as? [String: Any],
let ratingId = ratingDic["id"] {
searchResult.ratingID = ratingId as! String
}
Here is my api:
"amount" = "50.00";
"rating" = {
"name" = "Platinum";
"id" = 5
I'm pretty sure I need to use "valueForKey" so here is my updated code:
if let ratingDic = dictionary["rating"] as? [String: Any],
//let ratingId = ratingDic["id"] {
let ratingId = [ratingDic.valueForKey("id")!] {
searchResult.ratingID = ratingId as! Number
}
However now I receive the "Value for type String:Any has no member "valueForKey"
Never mind, I was forgetting to change the actual variable to a "Double" or number
var ratingID = 0.0
instead of
var ratingID = ""
and the code now reads
if let ratingDic = dictionary["rating"] as? [String: Any],
let ratingId = ratingDic["id"] {
searchResult.ratingID = ratingId as! Double
}

How to wait for a background function to finish before calling another one in swift?

I will try to explain briefly what the situation is.
I am building a quiz app and I wanted it to work mainly using the internet, but also to work for a while when the user is disconnected. The older code I was using was made only with synchronous queries, it was taking more time than what I expected. So i decided to reformulate it.
The situation I projected is the following:
When my user selects a subject the app will synchronously get 1 question for each subsubject in order to be ready for my user click and to be faster.
After getting this first question, the app would then get another 4 (or how much is needed to complete 5) for each of the subsubjects asynchronously, while the user is occupied answering the first question that was presented to him.
Finally, the app would save the objects in the local datastore, so that the user can answer 5 questions for each subsubject when he is not connected.
Here is my code:
func getQuestionsRemotelyandSave (subsubject:String?, subject:String?, arrayOfSubsubjects:[String]?) {
self.getFromUserDefaults()
if Reachability.isConnectedToNetwork() == true {
if self.needsToGetQuestions == true {
let user = PFUser.currentUser()
var query = PFQuery(className: "Questions")
query.whereKey("answeredBy", equalTo: user)
query.whereKey("grade", equalTo: user["grade"])
if subsubject != nil && subject == nil {
query.whereKey("subsubject", equalTo: subsubject)
query.limit = 2
let array = query.findObjects()
for item in array {
let object = item as PFObject
var QuestionToPin = PFObject(className: "Questions")
QuestionToPin["Question"] = object["Question"] as String
QuestionToPin["rightAnswer"] = object ["rightAnswer"] as String
QuestionToPin["wrongAnswer1"] = object ["wrongAnswer1"] as String
QuestionToPin["wrongAnswer2"] = object ["wrongAnswer2"] as String
QuestionToPin["wrongAnswer3"] = object ["wrongAnswer3"] as String
QuestionToPin["grade"] = object["grade"] as String
QuestionToPin["subject"] = object ["subject"] as String
QuestionToPin["subsubject"] = object ["subsubject"] as String
QuestionToPin["feedback"] = object ["feedback"] as? String
QuestionToPin["shortFeedback1"] = object ["shortFeedback1"] as? String
QuestionToPin["shortFeedback2"] = object ["shortFeedback2"] as? String
QuestionToPin["shortFeedback3"] = object ["shortFeedback3"] as? String
QuestionToPin.pin()
}
query.limit = 3
query.findObjectsInBackgroundWithBlock({ (objects:[AnyObject]!, error:NSError!) -> Void in
if error == nil {
let questions = objects as [PFObject]
var QuestionsToPin = [PFObject]()
for object in questions {
var QuestionToPin = PFObject(className: "Questions")
QuestionToPin["Question"] = object["Question"] as String
QuestionToPin["rightAnswer"] = object ["rightAnswer"] as String
QuestionToPin["wrongAnswer1"] = object ["wrongAnswer1"] as String
QuestionToPin["wrongAnswer2"] = object ["wrongAnswer2"] as String
QuestionToPin["wrongAnswer3"] = object ["wrongAnswer3"] as String
QuestionToPin["grade"] = object["grade"] as String
QuestionToPin["subject"] = object ["subject"] as String
QuestionToPin["subsubject"] = object ["subsubject"] as String
QuestionToPin["feedback"] = object ["feedback"] as? String
QuestionToPin["shortFeedback1"] = object ["shortFeedback1"] as? String
QuestionToPin["shortFeedback2"] = object ["shortFeedback2"] as? String
QuestionToPin["shortFeedback3"] = object ["shortFeedback3"] as? String
QuestionToPin.pinInBackground()
}
}
})
}
if subsubject == nil && subject != nil {
var firstTime = true
var semaphore = dispatch_semaphore_create(0)
query.limit = 1
for item in arrayOfSubsubjects! {
if item != "Todas as matérias" {
var query3 = PFQuery(className: "Questions")
query3.fromLocalDatastore()
query3.whereKey("subsubject", equalTo: item)
let count = query3.countObjects()
if count == 0 {
query.whereKey("subsubject", equalTo: item)
let array:NSArray = query.findObjects()
for item in array {
let object = item as PFObject
var QuestionToPin = PFObject(className: "Questions")
QuestionToPin["Question"] = object["Question"] as String
QuestionToPin["rightAnswer"] = object ["rightAnswer"] as String
QuestionToPin["wrongAnswer1"] = object ["wrongAnswer1"] as String
QuestionToPin["wrongAnswer2"] = object ["wrongAnswer2"] as String
QuestionToPin["wrongAnswer3"] = object ["wrongAnswer3"] as String
QuestionToPin["grade"] = object["grade"] as String
QuestionToPin["subject"] = object ["subject"] as String
QuestionToPin["subsubject"] = object ["subsubject"] as String
QuestionToPin["feedback"] = object ["feedback"] as? String
QuestionToPin["shortFeedback1"] = object ["shortFeedback1"] as? String
QuestionToPin["shortFeedback2"] = object ["shortFeedback2"] as? String
QuestionToPin["shortFeedback3"] = object ["shortFeedback3"] as? String
QuestionToPin.pin()
}
}
if count > 0 {
var limit = 5 - count
if limit < 0 {
limit = 0
}
query.limit = limit
if firstTime == false {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}
query.findObjectsInBackgroundWithBlock({ (objects:[AnyObject]!, error:NSError?) -> Void in
if error == nil {
let questions = objects as [PFObject]
for object in questions {
var QuestionToPin = PFObject(className: "Questions")
QuestionToPin["Question"] = object["Question"] as String
QuestionToPin["rightAnswer"] = object ["rightAnswer"] as String
QuestionToPin["wrongAnswer1"] = object ["wrongAnswer1"] as String
QuestionToPin["wrongAnswer2"] = object ["wrongAnswer2"] as String
QuestionToPin["wrongAnswer3"] = object ["wrongAnswer3"] as String
QuestionToPin["grade"] = object["grade"] as String
QuestionToPin["subject"] = object ["subject"] as String
QuestionToPin["subsubject"] = object ["subsubject"] as String
QuestionToPin["feedback"] = object ["feedback"] as? String
QuestionToPin["shortFeedback1"] = object ["shortFeedback1"] as? String
QuestionToPin["shortFeedback2"] = object ["shortFeedback2"] as? String
QuestionToPin["shortFeedback3"] = object ["shortFeedback3"] as? String
QuestionToPin.pinInBackground()
}
dispatch_semaphore_signal(semaphore)
firstTime = false
}
})
}
}
}
}
}
}
}
The problem I'm facing is that I cant request multiple asynchronous functions from Parse. What I thought was maybe waiting for an asynchronous function to finish before the other one starts, but I wanted it to happen without the user having to wait. Is there a way of doing it?
Thank you.

Replacing NSNulls inside NSDictionary

this question is similar to Replace occurrences of NSNull in nested NSDictionary
In swift I am getting an error (I believe because of NSNull's) I don't really care if the NSNull becomes an empty string or a nil. I am just wanting to get the code to work.
I have a large data structure coming in from JSON as an NSDictionary. Then I am saving that to a temporary file. Here is the code:
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as! NSDictionary
let json = JSON(jsonResult)
if (json["errors"].array?.count > 0) {
println("could not load stuff")
} else {
println("retrieving the stuff")
let file = "file.txt"
let file_path = NSTemporaryDirectory() + file
let dict:NSDictionary = jsonResult
let readDict:NSDictionary? = NSDictionary(contentsOfFile: file_path)
if dict.writeToFile(file_path, atomically: true) {
let readDict:NSDictionary? = NSDictionary(contentsOfFile: file_path)
//--- need to handle the NSNull junk here
if let dict = readDict {
println("Temp file created, here are the contents: \(dict)")
} else {
println("!!!Failed to READ the dictionary data back from disk.")
}
}
else {
println("!!!Failed to WRITE the dictionary to disk.")
}
}
Here's an example of what jsonResult looks like
things = (
{
"one" = one;
two = "<null>";
"three" = three;
"four" = "<null>";
"five" = five;
"six" = "six-6";
seven = 7;
eight = eight;
nine = "<null>";
ten = "<null>";
eleven = "<null>";
"twelve" = "<null>";
},
UPDATE:
Problem: I have a very large amount of JSON (roughly 600kb as text) Within that JSON data there are nulls. The problem I was having is that when you NSJSONSerialization as NSDictionary, it converts the nulls into NSNull objects (it looks funky if you print this out because they appear as a string, this is wrong.
Solution: You need to have a "pruned" or "sanitized" dictionary. What this means is to throw out the key and value entirely. To do this, I added a sanitized_dict function. Here is the function:
func sanitize_dict(obj: AnyObject) -> AnyObject {
if obj.isKindOfClass(NSDictionary) {
var saveableObject = obj as! NSMutableDictionary
for (key, value) in obj as! NSDictionary {
if (value.isKindOfClass(NSNull)) {
//println("Removing NSNull for: \(key)")
saveableObject.removeObjectForKey(key)
}
if (value.isKindOfClass(NSArray)) {
let tmpArray: (AnyObject) = sanitize_dict(value as! NSArray)
saveableObject.setValue(tmpArray, forKey: key as! String)
}
if (value.isKindOfClass(NSDictionary)) {
let tmpDict: (AnyObject) = sanitize_dict(value as! NSDictionary)
saveableObject.setValue(tmpDict, forKey: key as! String)
}
}
return saveableObject
//--- because arrays are handled differently,
//--- you basically need to do the same thing, but for an array
//--- if the object is an array
} else if obj.isKindOfClass(NSArray) {
var saveableObject = [AnyObject]()
for (index, ele) in enumerate(obj as! NSArray) {
if (ele.isKindOfClass(NSNull)) {
// simply don't add element to saveableobject and we're good
}
if (ele.isKindOfClass(NSArray)) {
let tmpArray: (AnyObject) = sanitize_dict(ele as! NSArray)
saveableObject.append(tmpArray)
}
if (ele.isKindOfClass(NSDictionary)) {
let tmpDict: (AnyObject) = sanitize_dict(ele as! NSDictionary)
saveableObject.append(tmpDict)
}
}
return saveableObject
}
// this shouldn't happen, but makes code work
var saveableObject = [AnyObject]()
return saveableObject
}
So your other code needs to call that function, it should look something like this:
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as! NSDictionary
//--- right now jsonResult is not actual json, and actually has the NSNulls
//--- get that result into the sanitized function above
let saveableDict: (AnyObject) = self.sanitize_dict(jsonResult)
let json = JSON(saveableDict)
if (json["errors"].array?.count > 0) {
println("!!!Failed to load.")
} else {
println("Load json successful. Attempting to save file...")
//--- set up basic structure for reading/writing temp file
let file = "file.txt"
let file_path = NSTemporaryDirectory() + file
let dict:NSDictionary = jsonResult
let readDict:NSDictionary? = NSDictionary(contentsOfFile: file_path)
if dict.writeToFile(file_path, atomically: true) {
let readDict:NSDictionary? = NSDictionary(contentsOfFile: file_path)
if let dict = readDict {
println("Temp file created, here are the contents: \(dict)")
} else {
println("!!!Failed to READ the dictionary data back from disk.")
}
}
else {
println("!!!Failed to WRITE the dictionary to disk.")
}
}
Hope this helps somebody out there with a big JSON dataset and nulls. It is all about that sanitize function!

Resources