Xcode suddenly quit in runtime when parsing JSON - ios

I'm trying to parse JSON with struct :
this is the JSON looks alike (the object value on SEAT has more than 100):
{
"Message": "Success",
"Status": 200,
"data": {
"SEATS": [
{
"SEAT_LOC_NO": "01404301",
"ROW_NM": "A",
"SEAT_NO": 1
},
{
"SEAT_LOC_NO": "01404401",
"ROW_NM": "A",
"SEAT_NO": 2
}
],
"SEATCOUNT": {
"COL_CNT": 42,
"ROW_CNT": 12,
}
}
}
and my struct be like:
struct Response : Codable {
var data : datas?
var Message : String?
var Status : Int64?
}
struct datas : Codable {
var SEATS : [SEATS]?
var SEATCOUNT : SEATINFO?
}
struct SEATS : Codable {
var SEAT_LOC_NO : String?
var ROW_NM : String?
var SEAT_NO : String?
}
struct SEATINFO : Codable {
var COL_CNT : Int64?
var ROW_CNT : Int64?
}
and this is the process of parsing:
var getSeat = Response()
getSeat = try? JSONDecoder().decode(Response.self, from: json) as Response
The xcode exit when trying to run this progress, json is a Data type which has been called in API Service.
I've been trying to delete derived data, remove some data in xcodeproject, restarting xcode and OS. However this problem still comes. And I wonder why but only on this process xcode suddenly quit while the others process is fine (different data to parse).
I'm using the latest XCODE Version.
Is it a bug, failed parsing or something else?

Your "SEAT_NO" key is integer value. You declare it as String? in struct.
Change var SEAT_NO : String? to var SEAT_NO : Int? or value in "SEAT_NO" key to String
If there is more problems try to catch errors
do {
getSeat = try JSONDecoder().decode(Response.self, from: data)
} catch {
print(error)
}

#Vanillatte please check your JSON first it's not valid please remove extra semicolon seat count dictionary. and try and to use error handling during parsing response.

Related

Is it possible to create Swift Codable for plain k-v json?

I've JSON data like:
{
"peopleA": "nnll",
"peopleB": "ihyt",
"peopleC": "udr",
"peopleD": "vhgd",
"peopleE": "llll"
}
There're thousands of data like that, basically what I wanna to do is read the JSON file, and fetch the relate info, like: input peopleC, return udr.
Trying to use some online solution, I got
struct Welcome: Codable {
let peopleA, peopleB, peopleC, peopleD: String
let peopleE: String
}
I know I can refactor the JSON file to:
{
"candidates": [
{
"name": "peopleA",
"info": "nnll"
},
{
"name": "peopleB",
"info": "ihyt"
},
{
"name": "peopleC",
"info": "udr"
}
]
}
And get the related Swift struct:
struct Welcome: Codable {
let candidates: [Candidate]
}
// MARK: - Candidate
struct Candidate: Codable {
let name, info: String
}
I'm just wondering if maybe we could make it work in Swift without postprocessing the json file?
You can simply decode it as a dictionary. Then you can map your dictionary into your array of Candidate structures if you would like to:
struct Welcome: Codable {
let candidates: [Candidate]
}
struct Candidate: Codable {
let name, info: String
}
let js = """
{
"peopleA": "nnll",
"peopleB": "ihyt",
"peopleC": "udr",
"peopleD": "vhgd",
"peopleE": "llll"
}
"""
do {
let dictionary = try JSONDecoder().decode([String: String].self, from: Data(js.utf8))
let welcome = Welcome(candidates: dictionary.map(Candidate.init))
print(welcome)
} catch {
print(error)
}
This will print:
Welcome(candidates: [Candidate(name: "peopleA", info: "nnll"), Candidate(name: "peopleC", info: "udr"), Candidate(name: "peopleB", info: "ihyt"), Candidate(name: "peopleE", info: "llll"), Candidate(name: "peopleD", info: "vhgd")])

Swift dispatch queue block not running

I am currently writing an Alamofire HTTP request and am running into an issue where my view is not loading - likely because there is no data. The confusing part is that this was working yesterday. In the request I was able to do print(data) and the result was 506 bytes which, if my calculation is correct, is about the correct size given the JSON payload returned from the endpoint below.
#State var recipes = [Recipe]()
AF.request("http://localhost:3000/recipes").responseJSON { response in
guard let data = response.data else { return }
if let response = try? JSONDecoder().decode([Recipe].self, from: data) {
DispatchQueue.main.async {
self.recipes = response
}
return
}
}
I can confirm that the endpoint that is being hit returns the following data...
[
{
"name":"Manhattan",
"image":"https://images.unsplash.com/photo-1536935338788-846bb9981813?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=2486&q=80",
"spirit":"Bourbon",
"ice":"Crushed",
"glass":"Coupe",
"yield":"3.75",
"description":"This is a good drink. You should make it.",
"ingredients":[
{
"bottle":"High West Son Of Bourye",
"amount":"2.5"
},
{
"bottle":"Cocchi Vermouth Di Torino",
"amount":"0.75"
},
{
"bottle":"Simple Syrup",
"amount":"0.083"
}
]
}
]
I also have my Recipe and Ingredient model here which should be able to decode based on the above JSON.
struct Recipe: Decodable, Identifiable {
var id = UUID()
var name: String
var image: String
var spirit: String
var ice: String
var glass: String
var yield: String
var description: String
var ingredients: [Ingredient]
}
struct Ingredient: Decodable, Identifiable {
var id = UUID()
var bottle: String
var amount: String
}
Is anybody able to spot an issue? I was trying to put a debugging print in the DispatchQueue but it is not printing which, to me, sounds like an error. However I am new to Swift/XCode/iOS and am not sure the best debugging practices for this.
If you can't debug yourself, NEVER USE try?. With more experience, I'd say that we tend to not use try?, but sometimes we do. But when we write try?, we are able to find an possible issue, ie debug if needed.
Let's do a proper try then, with a do/catch:
do {
let response = try JSONDecoder().decode([Recipe].self, from: data
DispatchQueue.main.async {
self.recipes = response
}
} catch {
print("Oops, there was en error while decoding: \(error)") // and not error.localizedDescription as it's more for users than developpers, so you'll skip all the useful informations
}
And read the output.
Going further?
Don't believe what's the API is supposed to return.
I've seen plenty and plenty of questions where the returned values was an error message, a XML Error message, a JSON Error message, an HTML Error message, and a JSON value missing, or of bad type, etc. And that, your JSONDecoder wasn't expecting it...
Reasons could be various, from bad/missing parameters, bad/missing APIKey, server down, bad/missing header, etc.
But, then, print the returned value.
print(String(data: data, encoding: .utf8) ?? "No data found")
So print it directly when you get it, or at least in the catch:
} catch {
print("Oops, there was en error while decoding: \(error)") // and not error.localizedDescription as it's more for users than developpers, so you'll skip all the useful informations
print("While getting response stringified: \(String(data: data, encoding: .utf8) ?? "No data found")")
}
If you don't understand the error message output, it's okay, there is no shame about it. But your first job is to get that error message. You can share it on SO if you don't understand it, you might get help with that. But currently, we can't guess what's wrong with your code.
It's a good idea to drop some clues in your code when looking for a failure.
If it were me I'd do something like this:
AF.request("http://localhost:3000/recipes").responseJSON { response in
guard let data = response.data else {
print("Error trying to receive data in ", #file, #function)
return
}
do {
let response = try JSONDecoder().decode([Recipe].self, from: data) {
DispatchQueue.main.async {
self.recipes = response
}
} catch {
print("Error failed to decode json data with error: \(error) in \(#file)", #function)
}
}

Alamofire: Issue with decimal type value response

I am facing one issue while fetching data from server using Alamofire. Could anyone please help me out.
Here is my WS response:
{
"id": "32321",
"name": "product name",
"brandName": "product brand name",
"ratings": 4.6364,
"totalReviews": 11,
}
And my model is
struct FALProductModel: Decodable, FALProductBasicInfo {
var id: String?
var name: String?
var brandName: String?
let ratings: Double?
let totalReviews: Int?
}
When I am fetching data using Alamofire Alamofire.responseData. ratings and totalReviews params are missing.
I tried with Alamofire.responseJson to check the response but here also the same issue that json is not returning above two params.
I tried with a simple URLSession and here I am getting the proper response.
does anyone have any idea what I am doing wrong.

Vapor 3: transform array of Future object to an array of Future other objects

I tried to make the most basic example that I could think of for my problem. I have a Course model and a many-to-many table to User that also stores some extra properties (the progress in the example below).
import FluentPostgreSQL
import Vapor
final class Course: Codable, PostgreSQLModel {
var id: Int?
var name: String
var teacherId: User.ID
var teacher: Parent<Course, User> {
return parent(\.teacherId)
}
init(name: String, teacherId: User.ID) {
self.name = name
self.teacherId = teacherId
}
}
struct CourseUser: Pivot, PostgreSQLModel {
typealias Left = Course
typealias Right = User
static var leftIDKey: LeftIDKey = \.courseID
static var rightIDKey: RightIDKey = \.userID
var id: Int?
var courseID: Int
var userID: UUID
var progress: Int
var user: Parent<CourseUser, User> {
return parent(\.userID)
}
}
Now, when I return a Course object, I want the JSON output to be something like this:
{
"id": 1,
"name": "Course 1",
"teacher": {"name": "Mr. Teacher"},
"students": [
{"user": {"name": "Student 1"}, progress: 10},
{"user": {"name": "Student 2"}, progress: 60},
]
}
Instead of what I would normally get, which is this:
{
"id": 1,
"name": "Course 1",
"teacherID": 1,
}
So I created some extra models and a function to translate between them:
struct PublicCourseData: Content {
var id: Int?
let name: String
let teacher: User
let students: [Student]?
}
struct Student: Content {
let user: User
let progress: Int
}
extension Course {
func convertToPublicCourseData(req: Request) throws -> Future<PublicCourseData> {
let teacherQuery = self.teacher.get(on: req)
let studentsQuery = try CourseUser.query(on: req).filter(\.courseID == self.requireID()).all()
return map(to: PublicCourseData.self, teacherQuery, studentsQuery) { (teacher, students) in
return try PublicCourseData(id: self.requireID(),
name: self.name,
teacher: teacher,
students: nil) // <- students is the wrong type!
}
}
}
Now, I am almost there, but I am not able to convert studentsQuery from EventLoopFuture<[CourseUser]> to EventLoopFuture<[Student]>. I tried multiple combinations of map and flatMap, but I can't figure out how to translate an array of Futures to an array of different Futures.
The logic you're looking for will look like this
extension Course {
func convertToPublicCourseData(req: Request) throws -> Future<PublicCourseData> {
return teacher.get(on: req).flatMap { teacher in
return try CourseUser.query(on: req)
.filter(\.courseID == self.requireID())
.all().flatMap { courseUsers in
// here we should query a user for each courseUser
// and only then convert all of them into PublicCourseData
// but it will execute a lot of queries and it's not a good idea
}
}
}
}
I suggest you to use the SwifQL lib instead to build a custom query to get needed data in one request 🙂
You could mix Fluent's queries with SwifQL's in case if you want to get only one course, so you'll get it in 2 requests:
struct Student: Content {
let name: String
let progress: Int
}
extension Course {
func convertToPublicCourseData(req: Request) throws -> Future<PublicCourseData> {
return teacher.get(on: req).flatMap { teacher in
// we could use SwifQL here to query students in one request
return SwifQL.select(\CourseUser.progress, \User.name)
.from(CourseUser.table)
.join(.inner, User.table, on: \CourseUser.userID == \User.id)
.execute(on: req, as: .psql)
.all(decoding: Student.self).map { students in
return try PublicCourseData(id: self.requireID(),
name: self.name,
teacher: teacher,
students: students)
}
}
}
}
If you want to get a list of courses in one request you could use pure SwifQL query.
I simplified desired JSON a little bit
{
"id": 1,
"name": "Course 1",
"teacher": {"name": "Mr. Teacher"},
"students": [
{"name": "Student 1", progress: 10},
{"name": "Student 2", progress: 60},
]
}
first of all let's create a model to be able to decode query result into it
struct CoursePublic: Content {
let id: Int
let name: String
struct Teacher:: Codable {
let name: String
}
let teacher: Teacher
struct Student:: Codable {
let name: String
let progress: Int
}
let students: [Student]
}
Ok now we are ready to build a custom query. Let's build it in some request handler function
func getCourses(_ req: Request) throws -> Future<[CoursePublic]> {
/// create an alias for student
let s = User.as("student")
/// build a PostgreSQL's json object for student
let studentObject = PgJsonObject()
.field(key: "name", value: s~\.name)
.field(key: "progress", value: \CourseUser.progress)
/// Build students subquery
let studentsSubQuery = SwifQL
.select(Fn.coalesce(Fn.jsonb_agg(studentObject),
PgArray(emptyMode: .dollar) => .jsonb))
.from(s.table)
.where(s~\.id == \CourseUser.userID)
/// Finally build the whole query
let query = SwifQLSelectBuilder()
.select(\Course.id, \Course.name)
.select(Fn.to_jsonb(User.table) => "teacher")
.select(|studentsSubQuery| => "students")
.from(User.table)
.join(.inner, User.table, on: \Course.teacherId == \User.id)
.join(.leftOuter, CourseUser.table, on: \CourseUser.teacherId == \User.id)
.build()
/// this way you could print raw query
/// to execute it in postgres manually
/// for debugging purposes (e.g. in Postico app)
print("raw query: " + query.prepare(.psql).plain)
/// executes query with postgres dialect
return query.execute(on: req, as: .psql)
/// requests an array of results (or use .first if you need only one first row)
/// You also could decode query results into the custom struct
.all(decoding: CoursePublic.self)
}
Hope it will help you. There may be some mistakes in the query cause I wrote it without checking 🙂 You can try to print a raw query to copy it and execute in e.g. Postico app in postgres directly to understand what's wrong.

Everything is parsed correctly but one element always pulls null even though it's inside the Json? Swift 4.1

I am trying to parse the messages and it just pulls nil EVERY time. It would be fine if it was like once or twice, but it does it every time so something is. definitely going wrong here.
Here is what the console output is looking like
commitJson(sha: "3665294d1e813d35594d6bcdc0a61983caa6e0cd", message: nil, url: "https://api.github.com/repos/apple/swift/commits/3665294d1e813d35594d6bcdc0a61983caa6e0cd", commit: GitHubCommits.commit(author: GitHubCommits.author(date: Optional("2018-10-03T19:12:15Z"), name: "Karoy Lorentey")))
It is pulling everything but the message. I might be missing something, but I think it's better if I let my code talk. Sorry for the struct layout......
Here is the struct with the json
struct author : Codable{
var date: String
var name: String
}
struct commit : Codable {
var author: author
}
struct commitJson : Codable {
var sha: String
var message: String?
var url: String
var commit: commit
}
seems solid right? I need the optional or the thing will crash on me....
Here is the parsing
guard let url = URL(string: "https://api.github.com/repos/apple/swift/commits?per_page=100") else {return}
URLSession.shared.dataTask(with: url) { (data, statusCode, error) in
//print(statusCode)
if let error = error{
print("error : \(error)")
return
}
guard let data = data else {return}
do{
let decoder = JSONDecoder()
self.commitsArray = try decoder.decode([commitJson].self, from: data)
for commit in self.commitsArray{
print(commit)
}
} catch {
print("I have failed you with \(error)")
}
}.resume()
I feel like I am not doing anything wrong, but I wouldn't be here if I wasn't. I tried converting the thing into a string and switching the some stuff like the quotes then back into a data object, but I either got it wrong or it doesn't help at all.
Here is a cleaner sample to show what I want out of there.
*note this is all wrapped around an array brackets at the start and end
{
"sha": "80d765034c61d8bcad1d858cfa38ec599017a2f0",
"commit": {
"author": {
"name": "swift-ci",
"date": "2018-10-08T18:59:06Z"
}
"message": "Merge pull request #19764 from tokorom/vim-syntax-case-label-region",
}
Here is what a sample of what the GitHub full data block example looks like.
{
"sha": "80d765034c61d8bcad1d858cfa38ec599017a2f0",
"node_id": "MDY6Q29tbWl0NDQ4Mzg5NDk6ODBkNzY1MDM0YzYxZDhiY2FkMWQ4NThjZmEzOGVjNTk5MDE3YTJmMA==",
"commit": {
"author": {
"name": "swift-ci",
"email": "swift-ci#users.noreply.github.com",
"date": "2018-10-08T18:59:06Z"
},
"committer": {
"name": "GitHub",
"email": "noreply#github.com",
"date": "2018-10-08T18:59:06Z"
},
"message": "Merge pull request #19764 from tokorom/vim-syntax-case-label-region",
"tree": {
"sha": "d6bd4fe23f4efabcfee7fbfb6e91e5aac9b4bf6d",
"url": "https://api.github.com/repos/apple/swift/git/trees/d6bd4fe23f4efabcfee7fbfb6e91e5aac9b4bf6d"
},
"url": "https://api.github.com/repos/apple/swift/git/commits/80d765034c61d8bcad1d858cfa38ec599017a2f0",
"comment_count": 0,
"verification": {
"verified": true,
"reason": "valid",
"signature": "-----BEGIN PGP SIGNATURE-----\n\nwsBcBAABCAAQBQJbu6j6CRBK7hj4Ov3rIwAAdHIIAKv4lE8AwQ/hrqfjNaOdW/EW\nsFqNisjTOhj1YiW64VSU7l2uztogJJG0Shl/+zQQQGFNVcvxlNXjq3JF9rrThrPl\nFKwvNZoSZBgNoEbTNoMPCkS+GMVDlMw96VVHrSo4Nae4yiU+Y+WSnCqf6I+TUSRp\n5JyL6oMlSqaihgq9gkIqlDnp6i0lRJWtMyGJ7xUrJ0C985RyGyb6fG20/34UJ4TT\nzT/Beb0RyYOdwnXy+mOm/NnmhcVozOrBbZlR3X2e4myQJ6Q7INOOyYPpmAZxEXps\nmajg6J73cwaH2x6PxRmMJ3+qxCau+bX3v4pEEeT5nYEIH+hDK2uC2wC/PkM7VsU=\n=2jhi\n-----END PGP SIGNATURE-----\n",
"payload": "tree d6bd4fe23f4efabcfee7fbfb6e91e5aac9b4bf6d\nparent 52deae30eb5833e53ba68ebc8a9a87614630751d\nparent ea2c860ddb4817dc83c7152035aa05569f3a2770\nauthor swift-ci <swift-ci#users.noreply.github.com> 1539025146 -0700\ncommitter GitHub <noreply#github.com> 1539025146 -0700\n\nMerge pull request #19764 from tokorom/vim-syntax-case-label-region\n\n"
}
}
Here is the link to the API. It does have like a 60 requests per hour without an API Key limit, so be wary of that.
GitHub Json Swift
message is part of the commit, not part of the outer object.
You need:
struct author : Codable{
var date: String
var name: String
}
struct commit : Codable {
var author: author
var message: String?
}
struct commitJson : Codable {
var sha: String
var url: String
var commit: commit
}

Resources