I am trying to add json values in JSQMessageData to show the message on JSQMessagesViewController. The view is set up and this the lite chat(can chat only once).We use an api to send and receive messages. The problem is when I fetched data from api as json it returns the value. I want to append that json data to the rest of my JSQMessages objects, I tried the last few days and have failed to accomplish this. Here is the full code and json response.
APIHandler.requestGETURL(urlString, success: { (JSON) in
print(JSON)
// var messageDictionary : [JSQMessageData] = []
// this is the message object
// i want to add the json data to my messageDictionary
// reload collection view
/*
{
"message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "hi",
"user_thumb" : "<image_path>"
},
{
"message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "how are you?",
"user_thumb" : "<image_path>"
}
*/
// i want to
let arrayNames = JSON["data"]
self.messageDictionary.append(JSQMessageData())
// I am stuck here
}) { (Error) in
print(Error.localizedDescription)
}
If I understand you correctly you're trying to parse json into a JSQMessage object. Your message data is not overly complex, it contains all the things a standard JSQMessage needs. So there is not any reason to create your own JSQMessageData object. You can just use one of the JSQMessage initializers. Since you are only using "Text" messages and not any other "Media" the
JSQMessage(senderId: <String!>, senderDisplayName: <String!>, date: <#Date>, text: <String>)
should be all you need. So all you need to do is get the values out of your json response. There are many ways to do this.
I am going to assume that the json you provided is also wrapped in a list like this
[
{ "message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "hi",
"user_thumb" : "<image_path>"
},
{ "message_time" : "27-05-2017",
"user_id" : 1924,
"user_name" : "Tester name",
"message" : "how are you?",
"user_thumb" : "<image_path>"
}
]
We can utilise the flatmap function to get our "Messages" out of the json data. You also do not need a dictionary becasue there is not key for each message so just use a list that contains JSQMessageObjects
var messages:[JSQMessages] = []
var imageDictionary: [userID: String: imagePath: String] = [:]
APIHandler.requestGETURL(urlString, success: { (JSON) in
print(JSON)
let messagesJSON = response.result.value as? [[String: Any]] ?? [[:]]
guard let listOfMessages = JSON as? [[String: AnyObject]]
messages: [JSQMessage] = listOfMessages.flatmap { messageData in
guard let dateCreated = messageData["message_time"] as? Date,
let userID = messageData["user_id"] as? String,
let userName = messageData["user_name"] as? String,
let text = messageData["message"] as? String else {
//If any of these things are missing we do not want to save the entry
return nil
}
let imagePath = messageData["user_thumb"] as? String
imageDictionary[userID] = imagePath
return JSQMessage(senderId: userID, senderDisplayName: userName, date: dateCreated, text: text)
}) { (let error: Error) in
if error != nil {
print(Error.localizedDescription)
}
}
I would save your image paths into a dictionary and fetch them on a background thread that way users can view the messages while the images populate as they arrive. Then once they have loaded apply them to your messages. or you can add it to your own custom message object that conformes to the JSQMessageDataSource protocol. for more on how to accomplish that check out this post
#Daniel is saying right, your json is enough simple that you don't need to add any JSQMessageData and maybe you are actually doing some extra effort, i have faced similar kind of problem when i need to pass a NSDictionary object with JSQMessage Objects so i used a tricky way for doing that ( and it works perfectly fine :) )
not sure about your case but this helps me a lot in many situations so follow these steps :
convert your json data into string
now save this json string into the accessebilityHint property of JSQmessage object. like -
(jsqmessageObj).accessibilityHint = jsonString
as according to your need as you want to use this json just extract the JSQmessage Object (like in cellForRowAtIndexPath ! ) , just use (jsqmessageObj).accessibilityHint to get back your json string decode it and use it as your need.
like - strJson = (jsqmessageObj).accessibilityHint
Hope this will help :p
Related
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")
}
This question already has answers here:
How to parse a JSON file in swift?
(18 answers)
Closed 3 years ago.
I want to print just value for key "User_number"
[
{
"User_fullname": null,
"User_sheba": null,
"User_modifiedAT": "2019-01-31T18:37:02.716Z",
"_id": "5c53404e91fc822c80e75d23",
"User_number": "9385969339",
"User_code": "45VPMND"
}
]
I suppose this is some JSON in Data format
let data = Data("""
[ { "User_fullname": null, "User_sheba": null, "User_modifiedAT": "2019-01-31T18:37:02.716Z", "_id": "5c53404e91fc822c80e75d23", "User_number": "9385969339", "User_code": "45VPMND" } ]
""".utf8)
One way is using SwiftyJSON library, but, this is something what I don't suggest since you can use Codable.
So, first you need custom struct conforming to Decodable (note that these CodingKeys are here to change key of object inside json to name of property of your struct)
struct User: Decodable {
let fullname, sheba: String? // these properties can be `nil`
let modifiedAt, id, number, code: String // note that all your properties are actually `String` or `String?`
enum CodingKeys: String, CodingKey {
case fullname = "User_fullname"
case sheba = "User_sheba"
case modifiedAt = "User_modifiedAT"
case id = "_id"
case number = "User_number"
case code = "User_code"
}
}
then decode your json using JSONDecoder
do {
let users = try JSONDecoder().decode([User].self, from: data)
} catch { print(error) }
So, now you have Data decoded as array of your custom model. So if you want to, you can just get certain User and its number property
let user = users[0]
let number = user.number
The following code takes takes in Data and saves "User_number" as an Int
if let json = try? JSONSerialization.jsonObject(with: Data!, options: []) as! NSDictionary {
let User_number= json["User_number"] as! Int
}
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 :-)
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.
I'm using firebase to collect data, and i'm trying to retrieve the data in a usable format for an iPhone app, and i can't quite get it out properly. I'm writing the app in Swift.
The data is grouped by a date string then the with a random key and then the data. Eg:
{
"20160304" : {
"-KC-aOwSWpt4dlYmjJE4" : {
"coordinates" : "-37.7811465912404, 145.005993055861",
"event" : "Test event",
"time" : "2016-03-04 07:48:43 +0000"
}, etc...
I'm so far grabbing the data like this:
ref.queryOrderedByKey().observeEventType(.ChildAdded, withBlock: {
snapshot in
//print(snapshot.key) // date
print(snapshot.value)
})
And it returns something like this to the console:
{
"-KD8O0gL7gDGu_hRyFzQ" = {
coordinates = "-37.7540958861003, 145.001224694195";
event = "Test event";
time = "2016-03-18 11:02:32 +0000";
}; etc...
Does anyone know how i can get down to the next level, past the random keys, to the meaningful data? I had trouble before with this for javascript, but it's confusing me using swift.
I'd like to be able to grab the detailed data (bottom level) for a defined date (top level).
Try this code
let jsonLocations = snapshot.valueInExportFormat() as! NSDictionary
let keys = jsonLocations.allKeys
for key in keys {
let json = jsonLocations[key] as! [String: AnyObject]
self.sections.append(Location(JSONObject: json))
}
I usually try to stick to methods of FDatasnapshot as long as possible, which leads to:
ref.queryOrderedByKey().observeEventType(.ChildAdded, withBlock: { snapshot in
for child in snapshot.children {
print(child.key); // -KC-aOwSWpt4dlYmjJE4
print(child.childSnapshotForPath("event").value)
}
});