Taking out array from dictionary in swift 3 - ios

Hi I am trying to populate a view using the response obtained from service but not able to fetch the exact value out of the whole response ,
[
["product_id": PRO161519,
"name": clothes,
"brand_name": Levis,
"discountprice": 0,
"images": <__NSArrayM 0x6000002541c0>(
{
image = "HTTP://i.vinove.com/dnn/backend/uploads/954tshirt_PNG5434.png";
}
)
"category": Accessories,
"price": 23.00
]
]
ProductList-Model
import UIKit
import SpeedLog
let KImages = "images"
let KListImage = "image"
struct ProductList{
var images = ""
var itemArray = [String]()
func bindProductListDataToPopulateView(_ response:[[String:Any]])->[ProductList]{
SpeedLog.print("response value as result",response)
for items in response{
print("items values",items)
}
print("item array",itemArray)
return []
}
}
response value as result
[["image":
item Values
["image":
Kindly help me to get the values images here.

You have to use like this :
for product in products {
if let productImages = product["images"], let images = productImages as? NSArray {
for image in images {
if let image = image as? [String: String] {
print(image["image"])
}
}
}
}

More than likely that JSON response you posted will eventually find its way to you in the form of a key-value Dictionary. You then use a "key" from the JSON you posted to extract the key's corresponding "value". In the snippet you posted, the keys would be the values on the left of the colon (e.g. "product_id", "name", etc).
Now, lets say your dictionary of key-values was called "jsonDictionary". You then would extract the values like so:
let productId = jsonDictionary["product_id"]
let name = jsonDictionary["name"]
If, however, you don't have logic to deserialize that raw JSON data (that you posted in your question) into a Dictionary, then you'll have to start there instead.

Related

Save Nested Dictionary in UserDefaults and manage the duplication check [Swift 4.2]

I have a nested Dictionary required to save in UserDefaults and share to extension. The dictionary structure like below:
let dict = [
"Sections" : [
["Title" : "Title1", "Items": ["item1-1", "item1-2", "item1-3"]],
["Title" : "Title2", "Items": ["item2-1", "item2-2", "item2-3", "item2-4"]],
["Title" : "Title3", "Items": ["item3-1"]],
]
]
Which saved successfully with:
UserDefaults(suiteName: "group.identifier.test")!.setValue(dict, forKey: "savedDict")
But now I wish to get it back and check is Title2 already exists, if yes then delete it and add again with new Items
I used to do following but can't get the Title back:
let savedDict:[String:AnyObject] = UserDefaults(suiteName: "group.identifier.test")!.object(forKey: "savedDict") as! Dictionary
success to get the data under "Sections" by following code
let savedSection = savedDict["Sections"]
print("Saved Section: \(savedSection)")
but not able to get the Title with:
print("Saved Title: \(savedSection!["Title"])") *// return nil*
I tried for (key, value) too, but fired a data type error
for (key, value) in savedSection{ *// Type 'AnyObject?' does not conform to protocol 'Sequence'*
print("Key: \(key) Value: \(value)")
}
May I know is there any way to get the "Title" back for checking and update? Am I using the wrong way to store this kind of nested data?
Many Thanks!
in your code
print("Saved Title: \(savedSection!["Title"])") *// return nil*
here it should be
if let savedSection = savedDict["Sections"] as? [[String : Any]] { //EDIT***
print("Saved Title: \(savedSection[0]["Title"])") *// inplace of 0 any index you want,
}
as if now in your dictionary there are three elements in section so it safe to get value at 0, hope you understood that the underlying dictionary is array of dictionary in sections key, also instead of using dictionary you can use struct or class to save your data and while getting it retrieve it as that struct type.
First of all, never use KVC method setValue(:forKey with UserDefaults.
There is generic set(:forKey. And there is dictionary(forKey: to get a [String:Any] dictionary back
The value for key Sections is an array (index-based). Lets assume you have this new data
let newTitle2 : [String:Any] = ["Title" : "Title2", "Items": ["item4-1", "item4-2", "item4-3"]]
This is a way to load the dictionary – you should always safely check if the dictionary exists – update it and save it back. If the item for "Title2" exists it will be overwritten otherwise the new item is appended to the array.
let groupDefaults = UserDefaults(suiteName: "group.identifier.test")!
if var savedDict = groupDefaults.dictionary(forKey: "savedDict"),
var sections = savedDict["Sections"] as? [[String:Any]] {
if let title2Index = sections.firstIndex(where: {($0["Title"] as! String) == "Title2"}) {
sections[title2Index] = newTitle2
} else {
sections.append(newTitle2)
}
savedDict["Sections"] = sections
groupDefaults.set(savedDict, forKey: "savedDict")
}

How to fetch data from array of Dictionary - swift

How can I fetch data from this array?
Here there is an array which contains some key value pairs, and some keys contain an array of dictionary.
var dataArray = [
["teamName":"Arsenal",
"image":"imageName",
"nextMatch":"in 2 days",
"matches":[
["oppositeTeam":"teamName",
"matchTimings":"121212",
"matchId":"ID 213432"],
["oppositeTeam":"teamName",
"matchTimings":"121212",
"matchId":"ID 213432"]
],
"fixtures":[
["oppositeTeam":"teamName",
"oppositeTeamScore":"7",
"HomeTeamScore":"4",
"HomeTeamCards":"True",
"oppositeTeamCards":"false",
"fixturesId":"ID 213432"],
]
],["teamName":"Chelsea",
"image":"imageName",
"nextMatch":"in 2 days",
"matches":[["oppositeTeam":"teamName",
"matchTimings":"121212",
"matchId":"ID 213432"],["oppositeTeam":"teamName",
"matchTimings":"121212",
"matchId":"ID 213432"]
],"fixtures":[["oppositeTeam":"teamName",
"oppositeTeamScore":"7",
"HomeTeamScore":"4",
"HomeTeamCards":"True",
"oppositeTeamCards":"false",
"fixturesId":"ID 213432"],["oppositeTeam":"teamName",
"oppositeTeamScore":"7",
"HomeTeamScore":"4",
"HomeTeamCards":"True",
"oppositeTeamCards":"false",
"fixturesId":"ID 213432"]
]
],["teamName":"India",
"image":"imageName",
"nextMatch":"null",
"matches":[],
"fixtures":[]
]]
I tried but I was unable to fetch data from this array.
You need to use a Model like this
struct Team {
let teamName:String
let image:String
let nextMatch:String
let matches:[Match]?
let fixtures:[Fixture]?
}
struct Match {
let oppositeTeam:String
let matchTimings:String
let matchId:String
}
struct Fixture {
let oppositeTeam:String
let oppositeTeamScore:String
let HomeTeamScore:String
let HomeTeamCards:String
let oppositeTeamCards:String
let fixturesId:String
}
Next you need to learn about Codeable in swift for which I have attached an article below
Codeable Tutorial in swift
Here is how you can access the arrays/dictionaries defined in your dataArray:
// To access team object at zero index
if let team = dataArray[0] as? [String: Any] {
print("Team: \(team["teamName"])")
// To access matches array of team object at zero index
if let matches = team["matches"] as? [[String: Any]] {
print( matches)
// To access first match
if let match = matches.first {
print(match)
}
}
// Similar to matches access fixtures
if let fixtures = dataArray[0]["fixtures"] as? [[String: Any]] {
print(fixtures)
// To access first fixture
if let fixture = fixtures.first {
print(fixture)
}
}
}
This is ok if you are just prototyping. If you plan to extend this into an actual app creating separate models is the best approach.
You can have a Team model that can contain team name, image and matches and fixtures. For matches you can create a model with matche information in it. Similarly you can create a model for fixtures as well. Your Team class will then contain arrays of Match and Fixture classes like this:
var matches: [Match]
var fixtures: [Fixture]
and your dataArray will be of type
var dataArray: [Team]
Create model for your data using Codable. Parse the data in model using JSON decoder. Then you can use your model wherever you want.
For JSON parsing, you can refer this tutorial:-
https://medium.com/xcblog/painless-json-parsing-with-swift-codable-2c0beaeb21c1
You can fetch data from your Array like this:
for attributesObj in dataArray{
let dicFrmArray = attributesObj as! NSDictionary
if ((dicFrmArray["teamName"] as? NSNull) == nil && dicFrmArray["teamName"] != nil){
print(dicFrmArray[teamName"])
}
}

How to organise data when populating UICollectionView with images using Firebase

I'm new to Swift, and coding in general, and have been working on a project where I'd like to create a UICollectionView populated with images from Firebase.
Each section of the UICollectionView would be a category, and each category would contain images related to that category. Each UICollectionView belongs to a parent, and I need to keep track of which parent has which categories, and which images are in each category.
To track the parents, categories, and images, I've set up the Firebase database in the following way (with bowl being the parent, the names of fruit as categories, and the keys are references to image data stored elsewhere in the database):
"bowl" : {
"apple" : {
"-LOM1R4EH9nszjJp0Va5" : true,
"-LOM1aRZT2XCE-6fvLBK" : true,
"-LOM1hSTmRY6wGrWMvIo" : true,
"-LOM1xnvKE6lc7fizomh" : true
},
"banana" : {
"-LOLmQWLXXyiCUwDBwID" : true
},
"pear" : {
"-LOLHakW-EtqevCeHfzl" : true,
"-LOM2DBGGuX5VQLmBz46" : true
},
"orange" : {
"-LOM26_pm6lbJ1D6hVPB" : true
}
}
The image data section of the database looks as follows:
"image" : {
"fruit" : {
"-LOLHakW-EtqevCeHfzl" : {
"description" : "round orange",
"imageURL" : "https://firebasestorage.googleapis.com/1/image1"
},
"-LOLmQWLXXyiCUwDBwID" : {
"description" : "big banana",
"imageURL" : "https://firebasestorage.googleapis.com/1/image2"
},
"-LOM1R4EH9nszjJp0Va5" : {
"description" : "small apple",
"imageURL" : "https://firebasestorage.googleapis.com/1/image3"
}
}
}
The approach I have been attempting to take is to create a dictionary with the image keys in it, then iterate through the image keys to grab the image data associated with each key (such as the imageURL), and then use the imageURL to download the images and populate the UICollectionView.
I've created a struct, as follows to transform the image data:
struct FruitPicture {
let imageURL: String
let description: String
init(imageURL: String, description: String) {
self.imageURL = imageURL
self.description = description
}
init?(snapshot: DataSnapshot) {
guard
let value = snapshot.value as? [String: AnyObject],
let imageURL = value["imageURL"] as? String,
let description = value["description"] as? String else {
return nil
}
self.imageURL = imageURL
self.description = description
}
func toAnyObject() -> Any {
return [
"imageURL": imageURL,
"description": description
]
}
}
I've been able to gather the imageURLs and populate a UICollectionView but it doesn't include the category details, and so far has involved a lot of manipulation of the data via snapshots, dictionaries, arrays, arrays of dictionaries, and so on, from one configuration to another and back again, and I've now become stuck and confused.
I've started looking at using multiple structs and nesting one within the other, like so, but I'm muddled on it all and am spending hours getting nowhere:
struct Picture {
var url: URL
var image: UIImage?
}
struct PictureCategory {
var name: String
var pictures: [Picture]
}
I was hoping for some advice, or roadmap, or details of how you would approach this, or some sample code, or anything to point me in the right direction. Thanks.
Edit to add more info
Thank you Iraniya your reply was very helpful and helped me consider things in a different way, I really appreciate it.
Taking your advice I've written the following which looks up a bowling creates a snapshot of the image meta data within (e.g the fruit and keys associated with that fruit) then uses those keys to create a snapshot of the image data (e.g key, imageURL, description). I then transform both snapshots into dictionaries, and return the dictionaries to the method which called it:
// GET DATA
static func getPicData(forKey bowlKey: String, completion: #escaping ([String : [Any]], [String : [FruitPicture]]) -> Void) {
var imageMetaDict: [String : [Any]] = [:]
var imageDataDict: [String : [FruitPicture]] = [:]
// DEFINE DATABASE TARGET
let ref = Database.database().reference().child("meta").child("bowl").child(bowlKey).child("fruit")
// GET DATA INTO SNAPSHOT AND TRANSFORM INTO DICTIONARY
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let dict = snapshot.value as? [String:[String:Any]] else {
return completion([:],[:])
}
// DEFINE DISPATCH GROUP
let dispatchGroup = DispatchGroup()
// ITERATAE THROUGH DICTIONARY
for (categoryObject, fruitData) in dict {
// CREATE ARRAY TO STORE ITEMS
var itemArray = [String]()
// ITERATE THROUGH ITEMS IN FRUIT DATA
for item in fruitData {
// APPEND ITEM.KEY TO ITEM ARRAY
itemArray.append(item.key)
// ENTER DISPATCH GROUP
dispatchGroup.enter()
// USE ITEM.KEY TO GATHER IMAGE DATA
Service.viewPicData(forKey: item.key) { (fruitItem) in
if let fruitItem = fruitItem {
imageDataDict[item.key] = [fruitItem]
}
// EXIT DISPATCH GROUP
dispatchGroup.leave()
}
}
// STORE ARRAY IN DICTIONARY UNDER FRUIT CATEGORY KEY
imageMetaDict[categoryObject] = itemArray
}
// RETURN COMPLETION
dispatchGroup.notify(queue: .main, execute: {
completion(imageMetaDict, imageDataDict)
})
})
}
Each dictionary looks similar to the following:
imageMetaDict
[
"apple": ["-LOM1R4EH9nszjJp0Va5", "-LOM1xnvKE6lc7fizomh", "-LOM1hSTmRY6wGrWMvIo", "-LOM1aRZT2XCE-6fvLBK"],
"pear": ["-LOLHakW-EtqevCeHfzl", "-LOM2DBGGuX5VQLmBz46"],
"banana": ["-LOLmQWLXXyiCUwDBwID"],
"orange": ["-LOM26_pm6lbJ1D6hVPB"]
]
imageDataDict
[
"-LOM26_pm6lbJ1D6hVPB": [myApp.FruitPicture(imageURL: "https://firebasestorage.googleapis.com/1/image1", description: "pear 1")],
"-LOM2DBGGuX5VQLmBz46": [myApp.FruitPicture(imageURL: "https://firebasestorage.googleapis.com/1/image2", description: "banana 1")],
"-LOLmQWLXXyiCUwDBwID": [myApp.FruitPicture(imageURL: "https://firebasestorage.googleapis.com/1/image3", description: "apple 1")]
]
Is this on the right track with what you were suggesting?
From what I understand the next steps are:
Create an array of fruit.keys sorted alphabetically
Use the fruit.keys to get image.keys from 'imageMetaDict'
Use those image.keys to look up the image data (imageURL, etc) in 'imageDataDict'
Transform all of this data into a new FruitDict which contains fruitCategory -> [fruitObject]
Is this similar to what you were suggesting? I'm happy to hear any further pointers, code or suggestions you have, you've really helped me so far!
To store images Create imageDict hash-map(dictionary) with the key you getting from firebase key in image->>fruits eg: "-LOLHakW-EtqevCeHfzl" with value you are getting or the stuct you already create, now when populating fruits-->apple get key from your bowl array or dict and then use that same key to get the image from imagesDict dict(hashmap you just create earlier
now while storing data in firebase make sure each image has unique keys and store that same key in your bowl-->apple->image that way it will be fast and easy to manage as image data and fruits data are mapped using key you get while storing new image :-) if you like the solution I can explain in more dept :-) #HappyCoding
Example
firebase node
"bowl" : {
"apple" : {
"-LOLHakW-EtqevCeHfzl" : true,
"--LOLmQWLXXyiCUwDBwID" : false,
}
}
"image" : {
"fruit" : {
"-LOLHakW-EtqevCeHfzl" : {
"description" : "round orange",
"imageURL" : "https://firebasestorage.googleapis.com/1/image1"
},
"-LOLmQWLXXyiCUwDBwID" : {
"description" : "big banana",
"imageURL" : "https://firebasestorage.googleapis.com/1/image2"
}
}
}
ImageDict
"-LOLHakW-EtqevCeHfzl":{
"description" : "round orange",
"imageURL" : "https://firebasestorage.googleapis.com/1/image1"
},
"-LOLmQWLXXyiCUwDBwID" : {
"description" : "big banana",
"imageURL" : "https://firebasestorage.googleapis.com/1/image2"
}
or
{"-LOLHakW-EtqevCeHfzl":imageStruct1,
"-LOLmQWLXXyiCUwDBwID" :imageStruct2}
now to show image while populating apple
var keys = boul["apple"].allKeys;
if(boul["apple"][keys[0]]){ //value is true show image
var imageUrl = imageDict[keys[0]["imageURL"]; //if using dict
//or
var image = imageDict[key[0]].imageURL //if using struct
}
Now to store parent, categories and there image details
create a Dict called fruitsDict or whatever with dict in side of another dict making key as fruite name eg: "apple":{apples Details like image price etc} but if you only interested in storing images just create list of images which have true value eg: "apple":[key1, key2...]; (keys you get from imageDict.
Now based on your requirement like
Show all category!! then create all category from fruitDict use that as datasource
and use imageDict and fruitsDict for details
Show only specific category like based on seasonal fruits then crate list of those fruits and show those based on imageDict and fruiteDict
HappyCoding :-)

swift parse json as per maintaining order

Suppose i have json string in which there is a json array called data.
The array holds json object of user profile data for example name,age,gender etc.
Now want to parse that json object as per order, for example if the object is
{
"name": "sample name",
"age": "30",
"gender": "male"
}
i want to parse the list as ordered like name,age,gender but with ios,when i convert the json object as dictionary , the order is changed,i know dictionary is not ordered so what is the the alternative to achieve this?
its a third party api so i dont have any hand on it,we have done it in android with linked hash map,but really stuck in swift , the last thing i would want to do is parse with regular expression.
im parsing the json in following way :
var rootData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
if let val = fromList["data"] {
let dataNode = val as! [[String:Any]]
for row in dataNode {
for (key,keyVal) in row {
//here the key is not in order.because when we cast it as dictionary the order gets changed.
}
}
For android we have achieved to do this with following function :
public ArrayList<LinkedHashMap<String, Object>> parseJsonArrayList(String odata, String arrayName) {
ArrayList<LinkedHashMap<String, Object>> mylist = new ArrayList<>();
try {
JSONObject e = new JSONObject(odata);
JSONArray data = e.getJSONArray(arrayName);
for(int i = 0; i < data.length(); ++i) {
JSONObject v = data.getJSONObject(i);
LinkedHashMap<String, Object> map = new LinkedHashMap<>(100, 0.75f, false);
Iterator keys = v.keys();
while(keys.hasNext()) {
String key = String.valueOf(keys.next());
//gph.log("debug4", key);
map.put(key, v.getString(key));
//gph.log("debug4", v.getString(key));
}
mylist.add(map);
}
} catch (JSONException var10) {
var10.printStackTrace();
}
return mylist;
}
Don’t try to order the dictionary. Instead create an array of the keys in the order you desire:
let keys = [“name”, “age”, “gender”]
Then access the dictionary with them:
for key in keys {
let value = dict[key]
// Present the value.
}
That will ensure the order you expect.
As you mentioned you cannot get the ordered data in Dictionary. If possible you can add the "order" key in your JSON like
[
{
"name": "sample name",
"order": 1
},
{
"age": "30",
"order": 1
},
{
"gender": "",
"male": "",
"order": 1
}
]
so that based on the order key you can do the sorting.

SwiftyJSON keeps returning empty objects

I am new to SwiftyJSON, and I'm having some trouble with it. I can get it to return the entire JSON file as a string, but the moment I try to parse it, I keep getting empty variables back, and I'm not sure what I'm doing wrong.
This is the formatting of my JSON file:
[
{
"entryID": 1,
"from": "String",
"to": "String",
"value": "String"
},
{
...
},
...
]
And this is roughly what I want to do with it (in quite inelegant code, I do apologise, I'm new to Swift):
for entry: JSON in indexJSON.arrayValue {
var vEntryID: Int
var vFrom: String
var vTo: String
var vValue: String
for (dictKey: String, dictVal: JSON) in entry.dictionaryValue {
if(dictKey=="entryID") {vEntryID = dictVal.intValue}
if(dictKey=="from") {vFrom = dictVal.stringValue}
if(dictKey=="to") {vTo = dictVal.stringValue}
if(dictKey=="value") {vValue = dictVal.stringValue}
}
someSwiftObject[vEntryID]["from"] = vFrom
someSwiftObject[vEntryID]["to"] = vTo
someSwiftObject[vEntryID]["value"] = vValue
}
However, this block never executes at all, because indexJSON.arrayValue is always empty.
When I try to run the following, it correctly prints the complete file contents to the console:
let indexJSON = JSON(content!)
println(indexJSON.stringValue)
But when I try to go deeper, to fetch any element, it returns nothing:
if(indexJSON.arrayValue.isEmpty==true) {println("indexJSON.arrayValue is Empty")}
if(indexJSON[0].arrayValue.isEmpty==true) {println("indexJSON[0].arrayValue is Empty")}
if(indexJSON[0].dictionaryValue.isEmpty==true) {println("indexJSON[0].dictionaryValue is Empty")}
if(indexJSON[0]["entryID"]==nil) {println("indexJSON[0][\"entryID\"].stringValue is Empty")}
Output:
indexJSON.arrayValue is Empty
indexJSON[0].arrayValue is Empty
indexJSON[0].dictionaryValue is Empty
indexJSON[0]["entryID"].stringValue is Empty
I'd be grateful for any help! What am I doing wrong?
I checked SwiftyJSON source code and I think I know where the problem is.
I suppose that you are using String to initialize the JSON object like this
let s = "{\"entryID\": 1,\"from\": \"String\",\"to\": \"String\",\"value\": \"String\"}"
let j = JSON(s)
In this case the JSON object is actuall given a type "String", not Array. That's why it's not iterable and its arrayValue is empty.
To do what you want to do, you need to initialize it with an Array object:
let arr = [
[
"entryID":1,
"from":"String",
"to":"String",
"value":"String",
]
]
let j2 = JSON(arr)
Now j2 is an array JSON object and iterable.
SwiftyJSON can only be initialized with NSData and object. So if you want to initialize it with a String you need to do this:
if let data = s.dataUsingEncoding(NSUTF8StringEncoding) {
let j = JSON(data:data)
println(j)
}
first of all, make sure the format of your json string is correct. in your question, your json string is a array, just format the string like this(the content is from my code):
let jsonStr = "[{\"name\": \"hangge\", \"age\": 100, \"phones\": [{\"name\": \"公司\",\"number\": \"123456\"}, {\"name\": \"家庭\",\"number\": \"001\"}]}, {\"name\": \"big boss\",\"age\": 1,\"phones\": [{ \"name\": \"公司\",\"number\": \"111111\"}]}]"
then you can use SwityJson to get the array object, like this:
let strData = jsonStr.data(using: String.Encoding.utf8, allowLossyConversion: false)
let json = JSON(data: strData!)
for object in json.arrayValue {
let name = object["name"].string
}
Take a look at the documentation here: https://github.com/lingoer/SwiftyJSON#loop
You are iterating it incorrectly. You should be iterating over the array with a for loop like this:
for (index: String, subJson: JSON) in json {
//Do something you want
}

Resources