Match row selected to pass data - json

I've taken a look at all these Swift, and asp.net, and javascript questions.
1
2
3
4
5
Goal:
When I select a messages from the list of chat messages in the MessageListController I want the opened session in the next ChatDetailController to be the conversation that was selected.
I'm doing the same thing in this iOS image for my WatchKit app. The message with Sophia is selected and the chat with Sophia opens.
[![enter image description here][6]][6]
I want to pass the json "message_id" i.e. the chatMessageId property. I'm already passing the chatMessageId from the MessageModelto the ChatDetailController as you can see in code.
Is it the chatMessageId of the ChatModelI need to pass? Or am I already passing the data that I need?
Passed context: Optional(HTWatch_Extension.MessageModel(partner: "9859", nickname: "Marco", message: "Have you seen is dog?", city: "Madrid", countryBadgeImageURL: https://i.imgur.com/PJcyle7.jpg, messageListImageURL: https://i.imgur.com/PJcyle7.jpg, chatMessageId: "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ", status: "offline"))
Attempt:
Do I need to take the do-catch block where I parse the ChatModel out of the ChatDetailController's awakeWithContext method and put it in the didSelectRowAt method of the MessageListController?
MessageListController
// ...code...
var messageObject = [MessageModel]()
var chatObject = [ChatModel]()
// ...code...
override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
var messageContext = messageObject[rowIndex]
var chatContext = chatObject[rowIndex]
do {
guard let fileUrl = Bundle.main.url(forResource: "Chats", withExtension: "json") else {
print("File could not be located")
return
}
let data = try Data(contentsOf: fileUrl)
let decoder = JSONDecoder()
let msg = try decoder.decode([ChatModel].self, from: data)
self.chatObject = msg
} catch let error {
print(error)
}
messageContext.chatMessageId = (chatObject as AnyObject).filter { (dictionaryTemp:[String:String]) -> Bool in
return dictionaryTemp["message_id"] == chatContext.chatMessageId
}
// WatchKit's model presentation method.
presentController(withName: "ChatDetailController", context: messageContext)
}

If I have understood correctly, your Chat.json, will have chat's for all message id's. Select one of the message id row and load the respective chat history.
In that case you can parse based on message by using filter. Let's say you have it in a dictionary like this.
Example:
let responseString = "{\"name\":\"Tom\"}"
if let responseData = responseString.data(using: .utf8){
do {
let object = try JSONSerialization.jsonObject(with:responseData , options: .allowFragments)
print("Response Object=\(object)")
} catch{
print("parsing Error=\(error)")
}
}
You can use a similar code to create your Object. The final object should be something like chatDictionary
let chatDictionary = [
[
"fromId": "zz1234skjksmsjdfwe2zz",
"toId": "qq43922sdkfjsfmmxdfqq",
"messageText": "Have you seen is dog?",
"imageUrl": "https://i.imgur.com/PJcyle7.jpg",
"message_id": "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ",
"read": "true"
],
[
"fromId": "zz1234skjksmsjdfwe2zz",
"toId": "qq43922sdkfjsfmmxdfqq",
"messageText": "Yes I have. It's cute.",
"imageUrl": "https://i.imgur.com/PJcyle7.jpg",
"message_id": "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ",
"read": "true"
],
[
"fromId": "zz1234skjksmsjdfwe2zz",
"toId": "qq43922sdkfjsfmmxdfqq",
"messageText": "I want to get a pet too.",
"imageUrl": "https://i.imgur.com/PJcyle7.jpg",
"message_id": "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ1",
"read": "true"
]
]
Your did Select Row At index
override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
let message = messageObjects[rowIndex]
// Create a chat object Dictionary, parse it before you pass it to the detail View Controller , if you have the chat.json.
// I have used 'Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ', but here you can your message id property to make it dynamic
message.chatObjects= chatDictionary.filter { (dictionaryTemp:[String : String]) -> Bool in
return dictionaryTemp["message_id"] == "Tva9d2OJyWHRC1AqEfKjclRwXnlRDQ"
}
presentController(withName: "ChatDetailController", context: message)
}

Related

Best way to dynamically store and access data in Swift

I have an app where every time it starts up (within the splash screen) it gets the currently logged in users' data from my database via an API endpoint.
The type of JSON expected to be sent back to the app when it asks for data is as shown:
{
"username": "test",
"email": "test#gmail.com",
"uid": "5f661ffe1a80160027a6cb0c",
"isVerified": true,
"hub": {
"hubID": "npnDsZegiSL5",
"isSetup": true,
"hubSWVersion": 0.11,
"_id": "5f661ffe1a80160027a6cb0d",
"cameraService": {
"isSetup": true,
"bridgeIP": "192.168.0.12",
"username": "JesCGSr6HrWoKbKnnNOHSayVKdb1"
},
"servicesSetup": {
"1": "cameraService",
"2": "TPLinkPlug"
}
},
"code": "100",
"message": "Success. User details have been provided"
}
As the user adds services/updates values, this database structure will constantly change. Which is my problem. There are tonnes of questions like this out there with all the same answer, to use the JSONDecoder and push the values to an observable object which can be accessed throughout the app via creating an instance of it or just to simply assign the values to the object when it has been fetched. Which I have done and am currently using:
The class userData object:
class userData: NSObject, ObservableObject {
var userdata = userData.self
#Published var uid: String = ""
#Published var isVerified: Bool = false
#Published var username: String = ""
#Published var email: String = ""
}
The code where I assign the values to the object when I receive the above json in my app:
...
#EnvironmentObject var user: userData
print("Successfully got user data. Code: \(code ?? "0"). UID: \(response?["uid"] as! String). Username: \(response?["username"] as! String)")
DispatchQueue.main.async {
user.email = response?["email"] as! String
user.uid = response?["uid"] as! String
user.username = response?["username"] as! String
...
Then I can access it anywhere in my via:
#EnvironmentObject var user: userData
username = self.user.username
But this would require hard-coded code to assign each value from my database, is there a better, more dynamic way to assign the whole json response from my API (no matter what values/embedded jsons there is) to an object and access these data values throughout my app via that object?
Thanks
Because you'll have new key value pairs in your response, I think you should store the response as a Dictionary and get desired values from that dictionary instead of parsing the response and storing separate variables.
here is an example:
let jsonStr = """
{\"username\": \"test\",
\"email\": \"test#gmail.com\",
\"uid\": \"5f661ffe1a80160027a6cb0c\",
\"isVerified\": true,
\"hub\": {
\"hubID\": \"npnDsZegiSL5\",
\"isSetup\": true,
\"hubSWVersion\": 0.11,
\"_id\": \"5f661ffe1a80160027a6cb0d\",
\"cameraService\": {
\"isSetup\": true,
\"bridgeIP\": \"192.168.0.12\",
\"username\": \"JesCGSr6HrWoKbKnnNOHSayVKdb1\"
},
\"servicesSetup\": {
\"1\": \"cameraService\",
\"2\": \"TPLinkPlug\"
}
},
\"code\": \"100\",
\"message\": \"Success. User details have been provided\"
}
"""
let data = jsonStr.data(using: .utf8)
do {
// store this variable
let dict = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any]
for (k,v) in dict! {
// example access to values
print("key: \(k), value: \(v)")
}
} catch {
print(error.localizedDescription)
}

Append Nested Objects in Parameters of multi-form Swift using Alamofire

I am uploading multiple images as well as JSON data using "Content-type": "multipart/form-data"
my issue is, I have nested objects to pass to the parameters
I was looking for a solution, and what I find is nested data with an array of String or Int, not another custom object (I was struggling with it for so long)
struct Car: Codable{
var id :Int,
var name:String,
var address:String,
var new:Bool
var users:[User]
}
struct User: Codable{
var id :Int,
var name:String,
var address:String,
var age:Int
}
I wanted to convert the data to do a dictionary to use it as parameters
func addNewCae(newCar:Car, images:[UIImage]){
let encoder = JSONEncoder()
let jsonData = try! encoder.encode(newCar)
let test = convertStringToDictionary(text: jsonData)
print(test)
let headers: HTTPHeaders
headers = ["Content-type": "multipart/form-data",
"Content-Disposition" : "form-data"]
AF.upload(multipartFormData: { multipartFormData in
for imageData in images {
guard let imgData = imageData.pngData() else { return }
multipartFormData.append(imgData , withName: "images[]", fileName: "image.jpeg", mimeType: "image/jpeg")
}
for (key, value) in self.convertStringToDictionary(text: jsonData)! {
multipartFormData.append("\(value)".data(using: .utf8)!, withName: key)
}
},to: "\(url)", usingThreshold: UInt64.init(),
method: .post,
headers: headers).responseString{ response in
switch response.result {
case .success(let value):
print(value)
break
case .failure(let error):
print(error)
}
}
}
func convertStringToDictionary(data: Data) -> [String:AnyObject]? {
do {
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:AnyObject]
return json
} catch {
print("Something went wrong")
}
return nil
}
As a request of my code: all the car fields are registered in the database except the users' field
(in reality, I have more fields than that)
Why the users' field is not added to the parameters?
Any idea?
The users filed normally be of type [[String: AnyObject]] in the parameters
here is the data after I convert it to a dictionary
Optional(["name":"whatever" , "address": "here", "users": <__NSArrayM 0x6000008e4f90>(
{
"name" = "hello";
"address" = "there";
"age" = 20;
},
{
"name" = "hi";
"address" = "location";
"age" = 25;
}
)
, "new": 0])
hope am clear enough, I can add any further code or information if needed to be clearer
Thanks
Update
I did the encoding manually so now I have a correct dictionary format of type [String: AnyObject] but one of them is nested :
["name":"whatever" , "address": "here", "users": [
[
"name" = "hello";
"address" = "there";
"age" = 20;
],
[
"name" = "hi";
"address" = "location";
"age" = 25;
]
]
, "new": 0]
BUT, still, the "users" field is not able to be read, I think because the parameters don't support that kind of type?
Anyone have any other idea how to deal with these nested objects?

How to get array of json value using Graphql in swift 4?

I am using graphql for get the API values using Apollo. I have successfully downloaded schema.json and get the values from grpahql. but i can't get the values array of json values.
This is my sample response:
{
"data": {
"team_Dashboard": {
"activeMission": "Food Mission",
"currentMissionChallenges": [
{
"id": "123",
"title": "Challenge",
"estTime": "5",
"location": "Anywhere",
"maxPts": "30",
"status": "Not yet started"
},
{
"id": "1234",
"title": " II Challenge",
"estTime": "5",
"location": "Anywhere",
"maxPts": "70",
"status": "Not yet started"
}
]
}
}
}
Graphql query:
query teamDashboard($teamId: ID!) {
team_Dashboard(teamId: $teamId) {
activeMission
currentMissionChallenges
}
}
Graphql schema response:
missionDeadLine: String
currentMissionChallenges: [JSON]
When i add the currentMissionChallenges([JSON]) in my Graphql query get error response from the server. but When i remove currentMissionChallenges from Graphql query, get success response and values from the server.
The problem is currentMissionChallenges is [JSON] format. When i change my graphql query This is graphql Response
query teamDashboard($teamId: ID!) {
team_Dashboard(teamId: $teamId) {
activeMission
currentMissionChallenges {
id
title
estTime
location
maxPts
status
}
}
}
Following error display in dashBord.grpahql
Field "currentMissionChallenges" must not have a selection since type "[JSON]" has no subfields.
How can i get the json array values from graphql. what is problem for getting Json Values? Please help me!
The best thing about GraphQL is we can use the query as model
Because the response would be as same as the query, so better we can assign the response to a variable of Query type.
Let me explain with an example:-
Suppose if I need to query about my profile data,
Profile.graphql
query MyProfile{
player {
me {
id
secret
name
email
state
country
timezone
picture
pictureType
audio
rank
}
}
countries{
value
viewValue
}
}
Once we'll build the app, it'll create MyProfileQuery in API.swift. In viewController we can use the response as below-
var myProfileData: MyProfileQuery.Data.Player.Me? // Declaring the valiable of player Type
ApolloClientManager.sharedInstance
.fetchQuery(MyProfileQuery(), showLoader: true,
viewController: self) { (response) in // Fetching response using Apollo Client Manager
if let allData = response {
if let profiledata = allData.player?.me {
self.myProfileData = profiledata // Assigning response into the variable declared
self.myEdittingProfileData = profiledata
self.updateUI()
}
if let countryData = allData.countries {
self.allCountrydata = countryData
self.getPickerDataForState(comppletion: {
self.openTimeZonePicker(completion: {
print("got timeZone Data")
})
})
}
}
}
Now we have response into the myProfileData variable which we can use
as follows -
Now we can access all the values we have mentioned in our query as below
print("player id is- \(myProfileData?.id)")
print("player name is- \(myProfileData?.name)")
print("player email is- \(myProfileData?.email)")
print("player state is- \(myProfileData?.state)")
print("player rank is- \(myProfileData?.rank)")
print("player pictureType is- \(myProfileData?.pictureType)")
// player id is- 10
// player name is- jordan
// player email is- jordan#domain.com
// player state is- Ohio
// player rank is- 101
// player pictureType is- custome
//
Hope this will help you out 👍👍👍
i suggest you use custom scalar.
import Apollo
public typealias JSON = [String: Any]
extension Dictionary: JSONDecodable {
public init(jsonValue value: JSONValue) throws {
if let array = value as? NSArray {
self.init()
if var dict = self as? [String: JSONDecodable & JSONEncodable] {
dict["data"] = array as! [[String: Any]]
self = dict as! Dictionary<Key, Value>
return
}
}
guard let dictionary = value as? Dictionary else {
throw JSONDecodingError.couldNotConvert(value: value, to: Dictionary.self)
}
self = dictionary
}
}
var currentMissionChallanges = [JSON]()
func getTeamDashboard(id:String) {
let query = TeamDashboardQuery(id:id)
apollo.fetch(query:query) { [weak self] result, error in
if let dashboards = result.data?.team_dashboard {
if let array = dashboards!["currentMissionChallanges"] as?
[JSON] {
self?.currentMissionChallanges = array
}
}
}
}

Parsing JSON using codable and ignoring first layer of JSON

I have JSON like this:
{
"success": true,
"message": "",
"result": {
"buy": [
{
"Quantity": 0.0056,
"Rate": 18527
},
{
"Quantity": 0.11431426,
"Rate": 18526
}
],
"sell":[
{
"Quantity": 8.20604116,
"Rate": 18540
},
{
"Quantity": 0.95600491,
"Rate": 18574.99999998
}
]
}
}
and another set of JSON like this:
{
"lastUpdateId": 1027024,
"bids": [
[
"4.00000000", // PRICE
"431.00000000", // QTY
[] // Can be ignored
]
],
"asks": [
[
"4.00000200",
"12.00000000",
[]
]
]
}
What is the best way to parse these two responses using codable. They both need to be parsed using the same struct or need to be converted to the same struct (whatever will do the job faster). I don't want to create a struct for the entire first response because I am not going to use keys like "success" and "message". I basically want to ignore those and get directly to the "result" key But in the second response, I will being using all the data so I have created a struct for that called TotalOrderBook. What is the best way to do this?
What is confusing me is ignoring the keys "success" and "message" in the first JSON response and getting straight to the value for the key "result". Is it possible to do that without creating an additional struct?
This is what I have right now. I would like to avoid adding another struct since the only thing I really need is the values under buy/bid and sell/sell.
struct TotalOrderBook:Codable{
var buy:[UniversalOrder]?
var sell:[UniversalOrder]?
var bid:[UniversalOrder]?
var ask:[UniversalOrder]?
var buyOrderBook: [UniversalOrder] {
return bid ?? buy ?? [UniversalOrder]()
}
var sellOrderBook: [UniversalOrder] {
return ask ?? sell ?? [UniversalOrder]()
}
var updatedTime:Date
}
struct UniversalOrder:Codable{
var price : Double {
return Double(rate ?? binPrice ?? 0)
}
var size : Double {
return Double(quantity ?? binQuantity ?? 0 )
}
//let numOrders : Int
var quantity:Double?
var rate:Double?
var binPrice:Double?
var binQuantity:Double?
private enum CodingKeys: String, CodingKey {
case rate = "Rate"
case quantity = "Quantity"
//case numOrders, binPrice,
}
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
binPrice = Double(try container.decode(String.self)) ?? nil
binQuantity = Double(try container.decode(String.self)) ?? nil
quantity = nil
rate = nil
}
}
This is how I am decoding:
let decoder = JSONDecoder()
let data = try! JSONSerialization.data(withJSONObject: value) //value is the response from Alamofire
var theWholeOrderBook:UniversalOrder!
do {
theWholeOrderBook = try decoder.decode(UniversalOrder.self, from: data)
} catch let error {
//print ("error is \(e) ** \(value)")
}
To answer your questions directly, yes it is very easy to ignore the success and message key-value pairs and head straight to results.
Despite this it will be a bit complicated to have a single struct to parse both of these JSON responses.
Both of them have a very different structure which will make it easier to use two different structs to use encoding. To highlight some differences :
buy, sell are nested inside results. bids, asks aren't.
The keys are completely different.
buy, sell have an array of key-value pairs while bids, asks simple have an array of values.
Codable structs should be simple and clear. It's better to have two of those corresponding to each response.

How can I parse this JSON Pokemon Dictionary? (swift 3)

I have an issue with parsing JSON data from new version of the Pokemon API, specifically with values of the "defense" and the"attack".
In Pokemon API v1 it was easy...
//JSON:
"attack": 45,
"defense": 65
//After parsing in Alamofire i'm got the next solution:
if let dict = response.result.value as? Dictionary<String, AnyObject> {
if let attack = dict["attack"] as? Int {
self._attack = attack
}
if let defense = dict["defense"] as? Int {
self._defense = defense
}
print(self._attack)
print(self._defense)
In Pokemon API v2 i have an issue with JSON format:
//JSON:
"stats": [
{
"stat": {
"url": "http://pokeapi.co/api/v2/stat/3/",
"name": "defense"
},
"effort": 0,
"base_stat": 45
},
{
"stat": {
"url": "http://pokeapi.co/api/v2/stat/2/",
"name": "attack"
},
"effort": 0,
"base_stat": 65
}
]
I have tried this option, but it doesn't work:
if let stats = dict["stats"] as? [Dictionary<String, AnyObject>], stats.count > 0 {
if let stat = stats[0]["stat"] as? Dictionary<String, String>{
if name = stat["defense"] {
if let defense = stats[0]["base_stat"] as? Int {
self._defense = defense
}
}
}
}
print(self._defense)
Please advise, how can I parse and get the value of "defense" correctly?
The problem is in this line if name = stat["defense"] { your JSON has key name and defense & attack are its value, so you need to get its value and need to check is it defense or attack.
if name = stat["name"] as? String, name == "defense" {
}
You can also reduce the code of getting base_stat with single if let like this.
if name = stat["name"] as? String, let score = stats[0]["base_stat"] as? Int, name == "defense" {
print("defense : \(score)")
}
Try this
guard let statsNode = dict["stats"] as? [[String: Any]] else { return }
for (index, statNode) in statsNode.enumerated() {
guard let statValue = statNode["base_stat"] as? Int else { continue }
switch index {
case 0:
self._defense = statValue
case 1:
self._attack = statValue
case 2:
self._someStat = statValue
....
default:
break
}
}
print(self._attack)
print(self._defense)
I've worked on a similar project when I started out in iOS Development.
These days I prefer guard let statements over if let statements when parsing JSON for information that's required in my project
I'm also assuming since the stats node is an array, the order of attack, defense, special attack, special defense, speed and HP won't change so the switch statement is an appropriate tool to use in this case.
It might also be helpful to put a print statement before return and continue in the guard statements' else block to see if you hit those else blocks in the program.