I'm getting JSON format data from the server, then I convert the data format to the [String:Any].
JSON--> {
integer = 1;
length = "<null>";
object = (
"692b663b-b7d5-43-287ddaadc2ff"
);
string = "SANJEEV TREHAN";
}
Here is the code:
if let data = data{
do{
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
if let integer = json["integer"] as? Int {
DispatchQueue.main.async {
if integer == 1{
//retrieve data here
print(json)
}
else{
print("alert")
}
}
}
else{
print("no name")
}
}
after converting the data as [String: Any]:
json = `["length": <null>, "integer": 1, "string": SANJEEV TREHAN, "object": <__NSSingleObjectArrayI 0x2806acb10>(
692b663b-b7d5-43d5-daadc2ff) ]`
I want to retrieve the object key value from the json variable.
The data I want only is 692b663b-b7d54a-7dd-aadc2ff as the String
I tried many things but not getting the data which format I want.
Since you're using Swift, why not use Codable types instead? They're much easier to use and you don't have to do weird casting or testing everywhere.
struct Response: Codable {
let length: Int?
let integer: Int
let string: String
let object: SomeObject
}
struct SomeObject: Codable {
let uuid: UUID
}
do {
let response = try JSONDecoder().decode(Response.self, from: data)
} catch {
print(error)
}
Now you can now ask for the fields directly.
print(response.object.uuid)
Seems like your object key is an array of string. Here is how you can get the value.
if let yourObject = json["object"] as? [String] {
if yourObject.count != 0 {
yourObjectValue = yourObject[0]
}
}
Related
I'm retrieving a JSON dict with the following structure:
"SITES": [
{
"NAME": “ALICE LANE”,
"WEBSITEAPPID": “XYZ”
}
]
}
I'm saving that straight into user defaults like this:
UserDefaults.standard.set(JSON(dict as Any)["SITES"].stringValue, forKey: "adminSites")
I know there is data in the JSON because the below code provides two rows of array data:
if let arraySites = dict?["SITES"] as? [[String: Any]] {
for sitesDetails in arraySites {
print(sitesDetails["NAME"] as Any)
print(sitesDetails["WEBSITEAPPID"] as Any)
}
}
When I try print the user default data count, I get 0
let defaultAdminSites = defaults.object(forKey: "adminSites") as? [String] ?? [String]()
print(defaultAdminSites.count)
Why am I getting 0 results? How would get the array row details if I did for ["NAME"] and ["WEBSITEAPPID"]?
I would recommend using a model object and Codable to avoid all those casts:
struct Model: Codable {
let sites: [Site]
enum CodingKeys: String, CodingKey {
case sites = "SITES"
}
}
struct Site: Codable {
let name, websiteAppid: String
enum CodingKeys: String, CodingKey {
case name = "NAME"
case websiteAppid = "WEBSITEAPPID"
}
}
// write to defaults
let model = Model(sites: [Site(name: "foo", websiteAppid: "bar")])
do {
let siteData = try JSONEncoder().encode(model)
UserDefaults.standard.set(siteData, forKey: "adminSites")
} catch {
print(error)
}
// read from defaults
if let siteData = UserDefaults.standard.data(forKey: "adminSites") {
do {
let model = try JSONDecoder().decode(Model.self, from: siteData)
for site in model.sites {
print(site.name, site.websiteAppid)
}
} catch {
print(error)
}
}
UserDefault Save Json Response
let result = jsonDict["SITES"] as? NSArray ?? []
let data = try! NSKeyedArchiver.archivedData(withRootObject: result, requiringSecureCoding: false)
UserDefaults.standard.set(data, forKey: "adminSites")
Get UserDefault data
let result = UserDefaults.standard.data(forKey: "adminSites")
if result != nil{
let data = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(result!) as? NSArray ?? []
print("Current User Details : - \(data)")
}
How to parse any stringified array such as "[\"Bob\", \"Tim\", \"Tina\"]" in Swift? It should return a JSON array such as ["Bob", "Tim", "Tina"].
Sorry if this is a duplicate question, but I could not find any answer for a generic stringified array where the structure of the array elements are not known.
Try doing it like this, Works for me every time:
let jsonText = "[\"Bob\", \"Tim\", \"Tina\"]"
var array: [String]?
if let data = jsonText.data(using: String.Encoding.utf8) {
do {
array = try JSONSerialization.jsonObject(with: data, options: []) as? [String]
if let myArray = array {
print(myArray)
}
} catch let error as NSError {
print(error)
}
}
It prints : ["Bob", "Tim", "Tina"]
Hope it helps!!
extension String
{
func decodeUrl() -> String
{
return self.removingPercentEncoding!
}
}
extension Data
{
func dataToJSON() -> Any? {
do {
return try JSONSerialization.jsonObject(with: self, options: [])
} catch let myJSONError {
print(myJSONError)
}
return nil
}
}
Usage :
if let data = your_stringified_array.decodeUrl().data(using: String.Encoding.utf8) {
if let jsonc = data.dataToJSON() {
print(jsonc)
}
}
Result is in AnyObject.
My Object conforms to the new Swift 4 Codeable protocol. How to save an array of these Objects in UserDefaults?
struct MyObject: Codeable {
var name: String
var something: [String]
}
myObjectsArray = [MyObject]() // filled with objects
UserDefaults.standard.set(myObjectsArray, forKey: "user_filters")
Error
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'Attempt to insert non-property
list object
Whew, I got it working:
Here is the Swift 4 Syntax to save an Array with Codeable Objects:
My solution is to encode it as a JSON Object and save that:
static var getAllObjects: [MyObject] {
let defaultObject = MyObject(name: "My Object Name")
if let objects = UserDefaults.standard.value(forKey: "user_objects") as? Data {
let decoder = JSONDecoder()
if let objectsDecoded = try? decoder.decode(Array.self, from: objects) as [MyObject] {
return objectsDecoded
} else {
return [defaultObject]
}
} else {
return [defaultObject]
}
}
static func saveAllObjects(allObjects: [MyObject]) {
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(allObjects){
UserDefaults.standard.set(encoded, forKey: "user_objects")
}
}
You can use a more generic approach, using array with specific type:
(myObject = any custom codable object you like)
(myKey = a string constant key to be able to retrieve/set specific array)
//set
setObject(myArray, forKey: mykey)
//get
let myArray = getObject(forKey: mykey, castTo: Array<myObject>.self)
and generic functions also, for any type:
func setObject<Object>(_ object: Object, forKey: String) where Object: Encodable
{
let encoder = JSONEncoder()
do {
let data = try encoder.encode(object)
set(data, forKey: forKey)
synchronize()
} catch let encodeErr {
print("Failed to encode object:", encodeErr)
}
}
func getObject<Object>(forKey: String, castTo type: Object.Type) -> Object? where Object: Decodable
{
guard let data = data(forKey: forKey) else { return nil }
let decoder = JSONDecoder()
do {
let object = try decoder.decode(type, from: data)
return object
} catch let decodeError{
print("Failed to decode object:" , decodeError)
return nil
}
}
I get totally very confused working with JSON in swift.
according to Apple dec: https://developer.apple.com/swift/blog/?id=37
/*
{
"someKey": 42.0,
"anotherKey": {
"someNestedKey": true
}
}
*/
What is a well-formed way to format this jsonWithObjectRoot json string?
I tried serval ways but n success.
so subsequently, these methods can access it.
if let dictionary = jsonWithObjectRoot as? [String: Any] {
if let number = dictionary["someKey"] as? Double {
// access individual value in dictionary
}
for (key, value) in dictionary {
// access all key / value pairs in dictionary
}
if let nestedDictionary = dictionary["anotherKey"] as? [String: Any] {
// access nested dictionary values by key
}
}
Your json looks good. You need to parse it before casting to a [String:Any].
let jsonWithObjectRoot = "{ \"someKey\": 42.0, \"anotherKey\": { \"someNestedKey\": true } }"
let data = jsonWithObjectRoot.data(using:.utf8)!
do {
let json = try JSONSerialization.jsonObject(with:data)
if let dictionary = json as? [String: Any] {
if let number = dictionary["someKey"] as? Double {
// access individual value in dictionary
}
for (key, value) in dictionary {
// access all key / value pairs in dictionary
}
if let nestedDictionary = dictionary["anotherKey"] as? [String: Any]
{
// access nested dictionary values by key
}
}
} catch {
print("Error parsing Json")
}
I have a Realm database called NewsCount. I need to download a new news only if there is a new news (respectively when newsCount change). And I make a comparison with the data parsing. But I can not compare them properly. How do you compare them?
Thi is my code
private func parseJSONData(_ data: Data) {
do {
let temp: NSString = NSString(data: data, encoding: String.Encoding.utf8.rawValue)!
let myNSData = temp.data(using: String.Encoding.utf8.rawValue)!
guard let jsonResult = try JSONSerialization.jsonObject(with: myNSData, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary else {
return
}
guard let jsonNews = jsonResult["categories"] as? [AnyObject] else {
print("Empty array")
return
}
let realm = try Realm()
let category = realm.objects(NewsCount.self)
var array = [Int]()
for i in category {
array.append(i.newsCount)
}
print(array)
print("News COUNT2 \(category)")
for jsonnewes in jsonNews {
let newsJson = NewsCount()
//HERE I COMPARE
if !UserDefaults.standard.bool(forKey: "AppStarted") || jsonnewes["count"] as! Int > array[jsonnewes as! Int]{
newsJson.newsID = jsonnewes["term_id"] as! Int
newsJson.newsCount = jsonnewes["count"] as! Int
//print("News COUNT2 \(newsJson.newsCount)")
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "downloadNew"), object: nil)
} else {
newsJson.newsID = jsonnewes["term_id"] as! Int
newsJson.newsCount = jsonnewes["count"] as! Int
//print("News COUNT3 \(newsJson.newsCount)")
}
insertOrUpdate(newsJson)
}
} catch {
print(error)
}
}
Because Realm uses RealmOptional to use Int values you have to call the value atribute to the RealmOptional
Try change this:
for i in category {
array.append(i.newsCount.value)
}
First off, it's probably more appropriate to use Int(string) instead of as! Int force-casting to convert your JSON data to integers.
From the looks of it, jsonnewes is a dictionary full of JSON data, but you're casting it as an array index in array[jsonnewes as! Int] which (given array is an array and not a dictionary) shouldn't work.
Instead, in order to make sure you're explicitly retrieving the item you want, I'd recommend using Realm's primary key query method in order to retrieve the item you want.
let realm = try! Realm()
for newsItem in jsonNews {
let newsPrimaryKey = Int(newsItem)
let realmNews = realm.object(ofType: NewsCount.self, forPrimaryKey: newsPrimaryKey)
// Don't continue if a Realm object couldn't be found
guard let realmNews = realmNews else {
continue
}
// Perform comparison
if Int(newsItem["count"]) > realmNews.newsCount {
// Perform the update
}
}