very confused working on json in swift - swift

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")
}

Related

Swift error extending Dictionary Any? is not convertible to Profiles

I am getting an error: 'Any?' is not convertible to 'Profiles' I am not sure how to correct. I am trying to extend the dictionary. I am needing the structure show in the Profile Struct
struct Profiles {
var id: Int
var name: String
}
extension NSDictionary {
var profileDictionary: [String : Profiles] {
var dictionary: [String : Profiles] = [:]
let keys = self.allKeys.compactMap { $0 as? String }
for key in keys {
let keyValue = self.value(forKey: key) as Profiles
dictionary[key] = keyValue
}
return dictionary
}
}
Not sure why you need to use NSDictionary when coding with Swift. I will post the Dictionary approach but NSDictionary would be exactly the same. You can use reduce method and convert all key value pairs to (String, Profiles) and add it to the resulting dictionary:
extension Dictionary {
var profileDictionary: [String : Profiles] {
reduce(into: [:], { result, keyValue in
if let kv = keyValue as? (String, Profiles) {
result[kv.0] = kv.1
}
})
}
}

Swift / Firestore - How do I get a single document with nested map objects and send them to a Struct?

I'm trying to get my data from a single document into a Struct for a Tableview. For some reason when getting a single document from Firestore. It comes back as a key-value pair. I thought it would be a dictionary. The same as when you fetch all documents from a collection. I have several nested maps(photo) inside of a map(photos). How do I get the nested maps and add the individual items into my Struct?
var items: [Item] = []
db.collection("items").document("aAZWjpXwo2V05rMOsQjR")
.getDocument{ (docSnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in docSnapshot!.data()! {
print("\(document)")
if let item = Item(dictionary: document){
self.items.append(item)
}
}
}
}
struct Item {
var photoUrl: String
var item_id: String
init(photoUrl: String, item_id: String) {
self.photoUrl = photoUrl
self.item_id = item_id
}
// I don't know how to use key-value pairs in Swift
// init?(dictionary: (key: String, value: Any)) {
init?(dictionary: [String: Any]) {
for photoInfo in dictionary["photos"] {
let photoData = photoInfo["photo"] as? [String: Any]
let photoUrl = photoData!["photoUrl"] as! String
let item_id = photoData!["item_id"] as! String
}
self.init(photoUrl: photoUrl!, item_id: item_id!)
}
}
I was able to get the embedded map items this way. I used [[String: Any]] to fix my issue. I guess an embedded map is treated as an array inside of an array. I'm still learning Swift. It took me over a week to figure this out. Happy coding everyone...
var items: [Item] = []
db.collection("items").document("aAZWjpXwo2V05rMOsQjR")
.getDocument{ (docSnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
if let document = docSnapshot.data(),
let doc = document["photos"] as? [[String: Any]] {
for photoInfo in doc {
if let item = Item(dictionary: photoInfo){
self.items.append(item)
}
}
}
}
struct Item {
var photoUrl: String
var item_id: String
init(photoUrl: String, item_id: String) {
self.photoUrl = photoUrl
self.item_id = item_id
}
// I don't know how to use key-value pairs in Swift
// init?(dictionary: (key: String, value: Any)) {
init?(dictionary: [String: Any]) {
let photoData = dictionary["photo"] as? [String: Any]
let photoUrl = photoData!["photoUrl"] as! String
let item_id = photoData!["item_id"] as! String
self.init(photoUrl: photoUrl!, item_id: item_id!)
}
}

How to retrieve value from Dict

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]
}
}

How to check if an object is dictionary or not in swift 3?

Tried the 'is' keyword.
// Initialize the dictionary
let dict = ["name":"John", "surname":"Doe"]
// Check if 'dict' is a Dictionary
if dict is Dictionary {
print("Yes, it's a Dictionary")
}
This will give an error saying "'is' is always true".
I just want to check if an object is a dictionary. It can be with any key any value pairs.
The key is hashable and it is not accepting the Any keyword.
If you want to check if an arbitrary object is a dictionary first of all you have to make the object unspecified:
let dict : Any = ["name":"John", "surname":"Doe"]
Now you can check if the object is a dictionary
if dict is Dictionary<AnyHashable,Any> {
print("Yes, it's a Dictionary")
}
But this way is theoretical and only for learning purposes. Basically it's pretty silly to cast up a distinct to an unspecified type.
If you just want to check if your object is Dictionary you can just do this:
if let dictionary = yourObject as? Dictionary{
print("It is a Dictionary")
}
Put this in a playground:
import UIKit
// Create a dictionary and an array to convert to and from JSON
let d = ["first": "Goodbye", "second": "Hello"]
let a = [1, 2, 3, 4, 5]
let dictData = try! JSONSerialization.data(withJSONObject: d, options: [])
let arrayData = try! JSONSerialization.data(withJSONObject: a, options: [])
// Now that we have set up our test data, lets see what we have
// First, some convenience definitions for the way JSON comes across.
typealias JSON = Any
typealias JSONDictionary = [String: JSON]
typealias JSONArray = [JSON]
// Lets see what we have
let dict = try! JSONSerialization.jsonObject(with: dictData, options: [])
let array = try! JSONSerialization.jsonObject(with: arrayData, options: [])
// testing functions
func isDictionary(_ object: JSON) -> Bool {
return ((object as? JSONDictionary) != nil) ? true : false
}
func isArray(_ object: JSON) -> Bool {
return ((object as? JSONArray) != nil) ? true : false
}
// The actual tests
isDictionary(dict) // true
isArray(dict) // false
isDictionary(array)// false
isArray(array) // true
JSON dictionaries arrays have specific types
I prefer using if let or guard let while parsing JSON objects
if let dict = jsonObject as? [AnyHashable:Any] {
print("Yes, it's a Dictionary")
}
guard let dict = jsonObject as? [AnyHashable:Any] else {
print("No, it's not a Dictionary")
return
}
Simply use is operator to check type conformance.
Here's a little snippet where I check if it's a Dictionaryor an Array
let dict = ["name":"John", "surname":"Doe"]
let array = [ "John", "Doe" ]
if dict is Dictionary<String, String>
{
print("Yes, it's a Dictionary")
}
else
{
print("No, It's other thing")
}
if array is Array<String>
{
print("Yes, it's a Array")
}
else
{
print("No, It's other thing")
}

Trouble accessing dictionary item in Swift 2.0

I'm experienced in Obj-C, but fairly new to Swift. I have a simple function that takes a Set and a Dictionary as parameters:
func buildSource(dataToParse:Set<String>, lookupData:Dictionary<String,AnyObject>) {
for item in dataToParse {
for dict in lookupData {
let nameID = dict["name"] // compile error
}
}
}
The passed in parameter lookupData is a dictionary containing nested dictionaries. I know that each of these dictionaries contains a key called name but when I try to access that key using the following syntax:
let nameID = dict["name"]
I get the following comile error:
Type '(String, AnyObject)' has no subscript members
If I know that a key exists, how do I access it? Thanks!
import Foundation
func buildSource(dataToParse:Set<String>, lookupData:Dictionary<String,AnyObject>)->[AnyObject] {
var ret: Array<AnyObject> = []
for item in dataToParse {
for (key, value) in lookupData {
if key == item {
ret.append(value)
}
}
}
return ret
}
let set = Set(["alfa","beta","gama"])
var data: Dictionary<String,AnyObject> = [:]
data["alfa"] = NSNumber(integer: 1)
data["beta"] = NSDate()
data["theta"] = NSString(string: "some string")
let find = buildSource(set, lookupData: data)
dump(find)
/* prints
▿ 2 elements
- [0]: 28 Nov 2015 18:02
▿ [1]: 1 #0
▿ NSNumber: 1
▿ NSValue: 1
- NSObject: 1
*/
in your code
for dict in lookupData {
let nameID = dict["name"] // compile error
}
dict is not a dictionary, but (key, value) tuple!
Update:
get value from lookupData with "name" as the key
downcast to a dictionary with as? [String:AnyObject]
iterate over dataToParse
use the String from dataToParse to access the nested dictionary.
func buildSource(dataToParse:Set<String>, lookupData:[String:AnyObject]) {
guard let dict = lookupData["name"] as? [String:AnyObject] else {
return
}
for item in dataToParse {
let value = dict[item]
}
}
More possible solutions:
If lookupData is an array of dictionaries:
func buildSource(dataToParse:Set<String>, lookupData:[[String:AnyObject]]) {
for item in dataToParse { // String
for dict in lookupData { // dict
let nameID = dict["name"] // value
}
}
}
If lookupData is a nested dictionary :
func buildSource(dataToParse:Set<String>, lookupData:[String:[String:AnyObject]]) {
for item in dataToParse {
guard let dict = lookupData[item] else {
continue
}
guard let nameID = dict["name"] else {
continue
}
}
}
If lookupData is definitely [String:AnyObject] and it's value might be another [String:AnyObject], but it might also not be.
func buildSource(dataToParse:Set<String>, lookupData:[String:AnyObject]) {
for item in dataToParse {
guard let dict = lookupData[item] as? [String:AnyObject] else {
continue
}
guard let nameID = dict["name"] else {
continue
}
}
}