Here I won't have any connection for both the pages and here I need to save the model class globally and to use anywhere in all pages till app was in use and after it may can clear the data having in array but I can able to access anywhere in all pages in app and I tried using to save in UserDefaults it crashed. Can anyone help me how to implement this?
var sortModel = [Sort]()
for (_, value) in sortJson as! [String: Any] {
self.sortModel.append(Sort.init(dict: value as! [String : Any]))
}
UserDefaults.standard.set(self.sortModel, forKey: "sorts")
Get your Sort struct to conform to Codable like:
struct Sort: Codable {
//...
}
then you can quickly get away with:
//Encode Sort to json data
if let jsonData = try? JSONEncoder().encode(sortModel) {
print("To Save:", jsonData)
//Save as data
UserDefaults.standard.set(jsonData,
forKey: "sorts")
}
//Read data for "sorts" key and decode to array of "Sort" structs
if let data = UserDefaults.standard.data(forKey: "sorts"),
let sorts = try? JSONDecoder().decode([Sort].self,
from: data) {
print("Retrieved:", sorts)
}
Basically, we make a json out of the array and save it as a data object.
We then can get it back as data and recreate the sort struct array.
NOTE: This may not work if the struct has nested within itself some types that prevent it from getting encoded as a json.
In this case, read:
http://swiftandpainless.com/nscoding-and-swift-structs
Related
I have a UICollectionView with multiple sections. Thus my data source for the collection view is a nested array of type [[DayData?]]. There is an instance of DayData for every cell and each inside array corresponds with each section. In DayData.swift, DayData has one property, a String and conforms to Codable. The problem I am struggling with is how to properly encode and decode this nested array.
static func saveToFile(days: [[DayData?]]) {
let propertyListEncoder = PropertyListEncoder()
let encodedDays = try? propertyListEncoder.encode(days)
try? encodedDays?.write(to: archiveURL,
options: .noFileProtection)
}
static func loadFromFile() -> [[DayData?]]? {
let propertyListDecoder = PropertyListDecoder()
if let retrievedDayData = try? Data(contentsOf: archiveURL),
let decodedDays = try?
propertyListDecoder.decode(Array<Array<DayData>>.self, from:
retrievedDayData) {
return decodedDays
}
return nil
}
Right now I call saveToFile(days:) after the String property of an element DayData in the nested array is reassigned like this:
dayDataArray[indexPath!.section][indexPath!.item]?.moodColor =
When I call loadFromFile(), it returns nil, so my data isn't saving. The program builds successfully.
I would really appreciate any help. I'm new to iOS and Swift, so I apologize if this is a dumb question.
EDIT: The loadFromFile() method is throwing this error: "Expected to decode Dictionary<String, Any> but found a string/data instead." I'm guessing this means that the data is decoding into a String rather than a nested array of DayData objects, but I'm not sure why it's doing this or how to fix it.
EDIT: I made DayData not optional, and it works now.
I'm trying to access the data downloaded from a website which looks like this:
{"quizzes":[
{"id":1, "question": "Can't solve this :("},
{"id":2, "question": "Someone help pls"}]}
The data downloaded looks way more complex, with more values, more keys, and some keys being associated to another dictionary of Key:String, but since I can't even access the most simple fields I thought I would start with this.
I'm using JSONSerialization, so my question is:
If I want to create a variable where i can save the downloaded data... Which would be it's type? I would say [String:[String:Any]], but I'm not sure if "quizzes" represents a key on this specific key, since the data starts with '{' and not with '['.
Instead of using JSONSerialization one could use JSONDecoder.
Example
struct Question: Decodable {
let id: Int
let question: String
}
struct Quiz: Decodable {
let quizzes: [Question]
}
Assuming jsonStr is a string with the JSON in your question:
if let jsonData = jsonStr.data(using: .utf8) {
if let quiz = try? JSONDecoder().decode(Quiz.self, from: jsonData) {
for question in quiz.quizzes {
print("\(question.id) \"\(question.question)\"")
}
}
}
Output
1 "Can't solve this :("
2 "Someone help pls"
I want to get result from json and save this result in NSUserDefault, after I want to use the json array saved in NSUserDefault to add multiple annotation on the MapKit.
Currently to get json result, I use this : ( Swift 2.x )
let defaults = NSUserDefaults.standardUserDefaults()
//Get user content//
let url = NSURL(string: "http://www.example.com/folder/coordonate.php")
let request = NSMutableURLRequest(URL: url!)
// modify the request as necessary, if necessary
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if data == nil {
print("request failed \(error)")
return
}
do {
if let json = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
print(json)
// print : {tab = "[london,paris]";}
var test = json["tab"]
defaults.setObject(test, forKey: "annotation") as? NSArray
}
} catch {
print(error)
}
}
task.resume()
NSUserDefaults.standardUserDefaults().synchronize()
And to get the NSUserdefault in another view, I try this code :
let defaults = NSUserDefaults.standardUserDefaults()
let test = NSUserDefaults.standardUserDefaults().objectForKey("annotation") as! NSArray
map.addAnnotations(test as! [MKAnnotation])
print("We saved this data: \(test)")
//No print, error before
But I have an error when I try with this method.
Could not cast value of type '__NSCFString' (0x1971a3958) to 'NSArray' (0x1971a4308).
If the "tab" property JSON actually being returned is:
[london, paris]
Then the property is not an array. What you're looking for would be:
['london', 'paris']
But additionally I can tell you that even if the "tab" property is a properly formatted JSON array your code will fail when it attempts to convert it to [MKAnnotation] anyway. That's because iOS's JSON library does not know how to convert a generic NSArray into it's typed equivalent. The NSJSONSerialization documentation indicates all the types that JSON will convert to. Best case scenario the "tab" property is an array of items with the same structure as MKAnnotation and are being converted to an array of dictionaries that you will have to convert to MKAnnotation yourself. But the JSON provided currently evaluates as a string. With my suggested change it will instead evaluate to an array of strings- still not sufficient to create an MKAnnotation from.
Your JSON data has to be one of the valid NSUserDefault types (String, NSArray, NSDictionary, NSData).
The quickest fix would be to store the JSON in NSUserDefaults as the NSData that comes back from the server. Then deserialize the NSData on the reading of NSUserDefaults.
If storing a subset of the JSON from the server is really needed, I would use Dictionaries, Arrays and validate the data before storing it. As a general Swift rule, I avoid using NSDictionary and NSArray to ensure the types are what I expect and won't cause a runtime crash.
change your php code to
$results = Array("tab" => ["london","paris"]);
instead of
$results = Array("tab" => "[london,paris]");
p.s
if using php earlier than 5.5 (or 5.4 not quite remember) then use:
$results = Array("tab" => Array("london","paris"));
==========================
you are casting the setObject func to NSArray and not the test object
defaults.setObject(test, forKey: "annotation") as? NSArray
should be
if let arrayTest = test as? NSArray{
defaults.setObject(arrayTest, forKey: "annotation")
}
I'm creating an app that should retrieve some JSON from a database.
This is how my JSON looks:
[{"id":"1","longitude":"10","latitude":"10","visibility":"5","timestampAdded":"2015-10-01 15:01:39"},{"id":"2","longitude":"15","latitude":"15","visibility":"5","timestampAdded":"2015-10-01 15:06:25"}]
And this is the code i use:
if let jsonResult = JSON as? Array<Dictionary<String,String>> {
let longitudeValue = jsonResult[0]["longitude"]
let latitudeValue = jsonResult[0]["latitude"]
let visibilityValue = jsonResult[0]["visibility"]
print(longitudeValue!)
print(latitudeValue!)
print(visibilityValue!)
}
As you can see it only gets the first chunk from the JSON and if there are no JSON at all it will crash, but if i want it to count the amount and make an array out of it like this:
var longitudeArray = [10, 15]
var latitudeArray = [10, 15]
And so on...
I also need this to be apple watch compatible so i can't use SwiftyJSON.
What do i do? I really hope you can help me!
Thanks.
SOLVED!
Problems was solved by "Eric D."
This is the code:
do {
if let url = NSURL(string: "YOU URL HERE"),
let data = NSData(contentsOfURL: url),
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [[String:AnyObject]] {
print(jsonResult)
let longitudeArray = jsonResult.flatMap { $0["longitude"] as? String }
let latitudeArray = jsonResult.flatMap { $0["latitude"] as? String }
print(longitudeArray)
print(latitudeArray)
}
} catch let error as NSError {
print(error.description)
}
Thank you soo much Eric!! :-)
You could use flatMap to get an array of your elements:
let longitudeArray = jsonResult.flatMap { $0["longitude"] as? String }
let latitudeArray = jsonResult.flatMap { $0["latitude"] as? String }
etc.
flatMap is like map but unwraps optionals, which is adequate because we need to safely cast the type of the object we get from each dictionary in the json array.
$0 represents the object in the current iteration of flatMap of the array it's applied to.
If you're currently using SwiftyJSON, then that would be:
let longitudeArray = jsonResult.flatMap { $1["longitude"].string }
let latitudeArray = jsonResult.flatMap { $1["latitude"].string }
because .string is SwiftyJSON's optional String value getter.
But as you said, you don't want to use it (anymore), so you need to use NSJSONSerialization to decode your JSON data, there's plenty of examples on the Web and on SO. Then you will be able to use my original answer.
You're already getting an array with all of the elements (not just the first one. you're simply only accessing the first one). jsonResult is an array of dictionaries. Each dictionary (in this case, based on the json you provided) contains these elements: id, longitude, latitude, visibility and timestampAdded. In order to access each of them, you can simply loop over jsonResult and access the i'th element (and not always the 0 element). This will also prevent the crash you're experiencing with the json is blank or invalid (since you'll only be going over the valid elements in jsonResult.
This will give you the flexibility to create the custom arrays you wish to create (in order to create an array of all of the longitudes, for example, you will simply add that element to the new array while looping over jsonResult). However, if you'd like to save yourself the trouble of manually building these arrays and assuming you have control over the json structure, I would recommend changing the received json to the relevant structure (a dictionary or arrays instead of an array of dictionaries), so it would better fit your needs and provide you the results in the relevant format right "out of the box".
I am using the VK SDK for iOS, and I need to take out the images URLs from JSON response from VK.
The response is look like this:
The actual response could be AnyObject OR String, and I only need the largest image URL (photo_1280) as string.
In the response comes 1-10 photos and which parameters I will need to change to get the particular photo like first or second and so on.
I'm using Swift in my project but can understand Objective-C.
JSON is just a format that lets you exchange information between languages (or objects).
You need to 'parse' the string into a JSON object. Its a little different for each language. For example on iOS I create my UI elements from a JSON file where I load the file and create a dictionary object from it. In your case you are doing it from a string. You need to turn that string into a valid object in your language. In Swift I prefer a Dictionary. So I use a typealias for that and cast the nsDictionary as that type of object.
Then to access objects, I access keys in the dictionary. In your case you would create a dictionary object too, and access the "attachments" object, which in your case is an array so you'll need to do extra processing on it to get each image (i.e. go through the array). This should get you going.
typealias Dict = Dictionary<String,AnyObject>
func loadDictionaryFromJSON(jsonString:String) -> Dict
{
var JSONData:NSData! = jsonString.dataUsingEncoding(NSUTF8StringEncoding)
var JSONError:NSError?
let swiftObject:AnyObject = NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions.AllowFragments, error: &JSONError)!
if let nsDictionaryObject = swiftObject as? NSDictionary
{
if let dictionaryObject = nsDictionaryObject as Dictionary?
{
return dictionaryObject as Dict
}else
{
println("Error could not make dictionary from NSDictionary in \(self)")
}
}else
{
"Error could not make NSDictionary in \(self)"
}
println("Empty dictionary passed, fix it!")
return Dict()
}
Now to access things you just do
var objects:Dictionary<String,AnyObject> = loadDictionaryFromJSON("{"what":"ever"}")
var whatever = objects["what"]