Related
I have a struct that I would like to parse from Json:
struct Subsidiary: Decodable {
let id: Int?
let subsidiary_ref: String?
let name: String?
let address: String?
}
I try to parse it like:
let sub: [Subsidiary] = try! JSONDecoder().decode([Subsidiary].self, from: data)
where data is Data type from
session.dataTask(with: urlRequest) { (data, response, error) in
and it gives me an error
Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a string/data instead.", underlyingError: nil))
If I do it like this:
let sub: String = try! JSONDecoder().decode(String.self, from: data)
it works and it gives me son
[
{"id": 5913, "subsidiary_ref": "0000159", "name": "Mercator Hipermarket Koper", "address": "Poslov.98-Koper,Dolinska C", "city": "Koper", "coordinates_x": null, "coordinates_y": null, "phone_number": "+386 56-636-800", "frequency": "A", "channel": "Food", "subchannel": "Hypermarket", "customer": 7, "day_planned": true, "badge_visible": true, "open_call": true, "number_of_planned": 13, "number_of_calls": 22, "last_call_day": 3, "notes": " notesi ki ne smejo nikoli zginiti bla marko bdsa"},
{"id": 5870, "subsidiary_ref": "0000773", "name": "Kompas Shop Pe Ferneti?i", "address": "Partizanska 150", "city": "Se?ana", "coordinates_x": null, "coordinates_y": null, "phone_number": "+386 57-380-636", "frequency": "A", "channel": "Food", "subchannel": "Supermarket", "customer": 17, "day_planned": true, "badge_visible": true, "open_call": true, "number_of_planned": 13, "number_of_calls": 1, "last_call_day": 1, "notes": null},...
]
What is wrong with my code?
EDIT:
I figure out, that if I create a string variable, and then convert this string value to data it works even with Subsidiary struct.
So something should be wrong with the data variable.
The fact that you can decode a String.self and get your JSON printed means that the root of the JSON is a string. The JSON you got, in text form, probably looks like this:
"[\r\n {\"id\": 5913, \"subsidiary_ref\": \"0000159\", \"name\": \"Mercator Hipermarket Koper\", \"address\": \"Poslov.98-Koper,Dolinska C\", \"city\": \"Koper\", \"coordinates_x\": null, \"coordinates_y\": null, \"phone_number\": \"+386 56-636-800\", \"frequency\": \"A\", \"channel\": \"Food\", \"subchannel\": \"Hypermarket\", \"customer\": 7, \"day_planned\": true, \"badge_visible\": true, \"open_call\": true, \"number_of_planned\": 13, \"number_of_calls\": 22, \"last_call_day\": 3, \"notes\": \" notesi ki ne smejo nikoli zginiti bla marko bdsa\"}\r\n]"
Note that the actual JSON you want is put in ""s, so all the special characters are escaped.
The web service has given the JSON in a very weird way...
To get the actual JSON data decoded, you need to get the string first, then turn the string into Data, then decode that Data again:
// nil handling omitted for brevity
let jsonString = try! JSONDecoder().decode(String.self, from: data)
let jsonData = jsonString.data(using: .utf8)!
let sub = try! JSONDecoder().decode([Subsidiary].self, from: jsonData)
Of course, the best solution is to fix the backend.
I have an array of dictionaries like this one.
[["updated_at": , "expected_qty": 1, "parts_id": 1, "mappingType": service, "description": , "name": Epoxy, "created_at": , "price": 0, "win_service_id": 1, "id": 1, "mappingID": 1], ["updated_at": , "expected_qty": 1, "parts_id": 2, "mappingType": service, "description": , "name": Wood for Lami, "created_at": , "price": 0, "win_service_id": 1, "id": 2, "mappingID": 1]]
I need to make this entire array into an escaped string. Something like this.
"[{\"updated_at\":\"\",\"expected_qty\":1,\"parts_id\":1,\"mappingType\":\"service\",\"description\":\"\",\"name\":\"Epoxy\",\"created_at\":\"\",\"price\":0,\"win_service_id\":1,\"id\":1,\"mappingID\":1},{\"updated_at\":\"\",\"expected_qty\":1,\"parts_id\":2,\"mappingType\":\"service\",\"description\":\"\",\"name\":\"Wood for Lami\",\"created_at\":\"\",\"price\":0,\"win_service_id\":1,\"id\":2,\"mappingID\":1}]"
I know you can convert an array to a string using array.description but I don't know how to escape those characters within it. I searched a lot but I couldn't find a method that does this either.
I tried adding a string extension to do something like this.
extension String {
var escaped: String {
return self.stringByReplacingOccurrencesOfString("\"", withString: "\\\"")
}
}
The result is this.
[[\"updated_at\": , \"expected_qty\": 1, \"parts_id\": 3, \"mappingType\": service, \"description\": , \"name\": Sill, \"created_at\": , \"price\": 0, \"win_service_id\": 8, \"id\": 3, \"mappingID\": 8]]
It looks like it's almost the expected output but notice the inner pairs of square brackets. Those must be the curly brackets.
So is there a better way to do this?
You can convert your dictionary into json using NSJSONSerialization. Try the following code out
let dict = ["key":"value","key1":"0","key2":"1"] as Dictionary<String, AnyObject>
let data = try? NSJSONSerialization.dataWithJSONObject(dict, options:NSJSONWritingOptions.PrettyPrinted)
var datastring = String(data: data!, encoding: NSUTF8StringEncoding)
I need to send data over socketio from ios using swift. Data looks like this:
{
birthday: {
"date": 24,
"month": 12,
"year": 1991
}
}
I tried this code, but it doesn't work:
let myJSON = [
"birthday" : [
"date": 24,
"month": 12,
"year": 1991
]
]
socket.emit("updateUserInfo", myJSON)
Please, tell me what i'm doing wrong
January 2020
Asumming that you are using Socket.IO-Client-Swift Library
Here is the sample source on how to effectively pass your JSON data.
// Adopt to SocketData protocol
struct CustomData : SocketData {
let name: String
let age: Int
func socketRepresentation() -> SocketData {
return ["name": name, "age": age]
}
}
// Usage
socket.emit("myEvent", CustomData(name: "Erik", age: 24))
You are right that if you have a dictionary to send over socket.io then you don't have to convert it to JSON because the method socket.emit does that for you.
So the syntax you show seems to be correct : socket.emit("event-as-string", dictionary).
You should tell us the exact error you are encountering such as someone can help you.
With socket.io you can send a normal object from javascript and it will be converted to JSON automatically.
I tested that code and everything is ok
let myJSON = { birthday: { "date": 24, "month": 12, "year": 1991}};
socket.emit("updateUserInfo", myJSON);
I believe you'll need to convert your Swift object to a JSON string. As Eric D. said there's a built in way to convert dictionary to JSON. If you already have a class representation of the data, you can take a look at a Swift class to JSON converter here:
https://gist.github.com/peheje/cc3618253d4f38ea4885
Have you tried just writing it as a string?
let myJSON = "[\"birthday\" : [\"date\": 24, \"month\": 12, \"year\": 1991]]"
Try this may work , internally it's getting object from zero index
[{
birthday: {
"date": 24,
"month": 12,
"year": 1991
}
}]
Sorry if my question is not clear, I'll try to make myself clear with an explanation. So here is exactly what I'm trying to do, I'm trying to use Alamofire to post more than one comment (Something that my app implements and will be stored as a JSON object whenever user writes a new comment). I'm passing these JSON comments to my post routine, where I can use SwiftyJSON to extract each value. Noe the thing is I know how to set the parameters if I'm trying to authorize the user as follows,
var parameters = [
"userName": userName,
"password": passwordSalt,
"somethingElse": somethingElse
]
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(parameters , options: nil, error: &err)
it's quite straightforward until here, now comes my problem. I'm trying to use alamofire post to post multiple json objects, which should look like this
[
{
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
},
{
"comment": "my SecondComment",
"commentDate": "2014-05-14 14:30 PM",
"isSigned": 2,
"patientId": 3,
"documentId": 4
},
{
"comment": "my third Comment",
"commentDate": "2014-05-15 14:30 PM",
"isSigned": 3,
"patientId": 4,
"documentId": 5
}
]
How do I create above array/json (I'm not exactly sure on what to call this) by iterating JSON object? I know how to get the JSON values from the JSON object all I'm asking is how to create this parameters variable to hold the data like above example. Is it even possible to do this using Alamofire? (POST multiple objects at once)
I tried a couple of ways to but they didn't work out
var dictArray = [Dictionary<String, Any>]
var dict = Dictionary<String, Any>
While iterating over JSON object inserted each value in dict and appended dict to dictArray, now when I'm trying to use dictArray as parameters in .dataWithJSONObject it doesn't like the object.
var dict = Dictionary<String, AnyObject>
var array = NSArray()
extracted each value by iterating over the JSON object and inserted them into dict and tried inserting dict into array. But this gives a different problem. The way it builds the objects is different from what is required, as follows.
[
{
comment: my First Comment,
commentDate: 2015-05-13 13:30 PM"",
isSigned: 1,
patientId: 2,
documentId: 3
},
{
comment: my Second Comment,
commentDate: 2015-05-13 13:30 PM"",
isSigned: 2,
patientId: 5,
documentId: 4
},
{
comment: my third Comment,
commentDate: 2015-06-13 13:30 PM"",
isSigned: 5,
patientId: 1,
documentId: 9
}
]
Here the Keys doesn't get wrapped inside quotes (Correct way: "comment", wrong way: comment).
Did anyone try posting multiple objects, is alamofire capable of doing so? I hope I made the question clear. Sorry if this is too simple of a question to answer, I spent my whole day figuring this out but didn't work out.
The correct representation in Swift for the array of comment objects you have posted would be like this:
let comments: Array<[String:AnyObject]> = [
[
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
],
[
"comment": "my SecondComment",
"commentDate": "2014-05-14 14:30 PM",
"isSigned": 2,
"patientId": 3,
"documentId": 4
],
[
"comment": "my third Comment",
"commentDate": "2014-05-15 14:30 PM",
"isSigned": 3,
"patientId": 4,
"documentId": 5
]
]
Sending a single comment would be fairly simple:
let comment: [String:AnyObject] = [
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
]
Alamofire.request(.POST, "http://httpbin.org/post", parameters: comment).responseJSON { (req, res, json, error) in
println(req)
println(res)
println(json)
println(error)
}
However, in order to send an array of comments, seems like you would have to generate the URLRequest your self and then pass it to Alamofire as follows:
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: "http://httpbin.org/post")!)
mutableURLRequest.HTTPMethod = "POST"
var error: NSError? = nil
let options = NSJSONWritingOptions.allZeros
if let data = NSJSONSerialization.dataWithJSONObject(comments, options: options, error: &error) {
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.HTTPBody = data
}
Alamofire.request(mutableURLRequest).responseJSON { (req, res, json, error) in
println(req)
println(res)
println(json)
println(error)
}
If you could modify the API backend to accept an object with multiple comments, you could also send them this way:
Alamofire.request(.POST, "http://httpbin.org/post", parameters: ["comments": comments]).responseJSON { (req, res, json, error) in
println(req)
println(res)
println(json)
println(error)
}
Regards.
This would be better
Create dictionary and array of dictionary var
Then loop how many parameters you need to send from data source either an array or whatever.
Here my scenario
Need to answer to all questions (will be a random number/size)
var ansParams = [[String: String]]()
var paramz = [String: String]()
for question in sectionQuestions{
paramz = [
AppConstants.PARAMETER.KEY_1 : "Value",
AppConstants.PARAMETER.KEY_2 : "Value",
AppConstants.PARAMETER.KEY_3 : "Value",
AppConstants.PARAMETER.KEY_4 : "Value",
AppConstants.PARAMETER.KEY_5 : "Value"
]
ansParams.append(paramz)
}
print(ansParams)
//Check All Paramz and its values then send ansParams as Parameter to POST request
I had a similar issue in my project while working with an API that did now allow posting multiple objects at once. The formatting of the array as noted above is fine.
let comments: Array<[String:AnyObject]> = [
[
"comment": "my First Comment",
"commentDate": "2014-05-13 14:30 PM",
"isSigned": 1,
"patientId": 2,
"documentId": 3
],
[
"comment": "my SecondComment",
"commentDate": "2014-05-14 14:30 PM",
"isSigned": 2,
"patientId": 3,
"documentId": 4
],
[
"comment": "my third Comment",
"commentDate": "2014-05-15 14:30 PM",
"isSigned": 3,
"patientId": 4,
"documentId": 5
]
]
Then I used a for loop to post each object of the array to post the API.
var index = comments.count
var i = 0
for i = 0; i < index; i++ {
let urlString = "\(.baseURL)...etc"
let parameters = comments[i]
Alamofire.request(.POST, urlString, parameters: parameters)
.responseJSON { (req, res, data, error) -> Void in
// println(req)
// println(res)
// println(data)
// println(error)
println("\(i) of \(index) posted")
}
}
More efficient ways if the API allows, but otherwise this flow works great.
I started last month with Swift and iOS. I'm working on an iPhone fitness app, specifically for weightlifting. Until now I've just been using a JSON file in the app's documents folder to store all the user's workouts, and using let workoutData = NSJSONSerialization.JSONObjectWithData( . . . ) as NSArray to load the data from the file (for display in a UITableView-like format) and then adding new workout entries with:
let newEntry: NSData = NSJSONSerialization.dataWithJSONObject(newData, options: .PrettyPrinted, error: nil)
newEntry.writeToFile(documentsPath, atomically: true)
This worked fine at first, but now I need to be able to edit workouts, add new ones at arbitrary dates, or delete specific workouts. This requires a fair amount of looping through arrays within dictionaries within arrays, and sorting the workouts properly has become difficult.
I feel like CoreData may be the best option going forward, considering the nature of my data. That said, here's an example of the structure of my JSON database:
[
{
"day": "Thursday",
"date": "June 5 2014",
"lbs": true,
"lifts": [
{
"name": "SQUAT",
"sets": 1,
"reps": 5,
"weight": 205,
"warmup": true
},
{
"name": "SQUAT",
"sets": 3,
"reps": 5,
"weight": 245,
},
{
"name": "BENCH PRESS",
"sets": 1,
"reps": 5,
"weight": 135,
"warmup": true
},
{
"name": "BENCH PRESS",
"sets": 3,
"reps": 5,
"weight": 185
},
{
"name": "YATES ROW",
"sets": 3,
"reps": 5,
"weight": 135,
}
]
},
{
"day": "Wednesday",
"date": "June 3 2014",
"lbs": true,
"lifts": [
{
"name": "DEADLIFT",
"sets": 1,
"reps": 3,
"weight": 255,
"warmup": true
},
{
"name": "DEADLIFT",
"sets": 1,
"reps": 5,
"weight": 305,
},
{
"name": "OVERHEAD PRESS",
"sets": 1,
"reps": 5,
"weight": 95,
"warmup": true
},
{
"name": "OVERHEAD PRESS",
"sets": 3,
"reps": 5,
"weight": 125
},
{
"name": "CHIN-UPS",
"sets": 3,
"reps": 5,
"weight": 180,
}
]
},
{
"day": "Monday",
"date": "June 1 2014",
"lbs": true,
"lifts": [
]
}
]
So the structure is:
Top level array, containing each workout day dictionary
Workout day dictionary, with a day string, date string, and an array of lifts
Array of lifts, each lift itself a dictionary
Lift dictionary, containing the lift name, sets, reps, weight, and a bool indicating whether this was a warmup set.
CoreData entities seem to only allow rather simple attributes. I suppose I could use a "Transformable" attribute, or use lots of To-Many relationships, but I'd rather not go to the trouble of learning those if it's not necessary.
So, should I suck it up and figure out how to make this same structure happen in CoreData? Alternatively, is this data structure too complex? Should I get rid of arrays/dictionaries altogether and just have a flat "workout" entity, and then after loading data from CoreData, collect all the workouts of the same date/type into groups manually in a ViewController?
Or, given the nature of my data, is there something better suited to use rather than CoreData or JSON? SQLite, perhaps?
If I were you I'd use CoreData or CloudKit (for online storage). The reason why is if I'm a user and I've got workouts spanning a year and a ton of data within each workout. That's going to take forever to load into memory. So you're going to have to figure out a way to segment your data anyways or else your users could be waiting a while for the file to be read into memory or possibly, the file could get bigger than is allotted and it wouldn't be possible to read in.
I don't know why you're saying it would be difficult to store these into database. It looks like to me you already have a structure that could be translated to a DBMs fairly easily. You're tables I would think should look something like this:
Workout : Table #1
wid (primary key)
date (timestamp)
lifts (array of: foreign keys)
Lifts : Table #2
lid (primary key)
name (string)
sets (number)
reps (number)
weight (number)
warmup (bool)
Don't be scared of Databases, they're really not that bad and it'll be 1000% more efficient than storing them in files.