Having trouble parsing received data from SignalR - swift

When trying to parse the data received, the values are nil, I can see the received data in the debug log, but just cannot parse the data.
I am using
https://github.com/moozzyk/SignalR-Client-Swift
self.chatHubConnection!.on(method: "SendMessage", callback: { (payload: ArgumentExtractor?) in
let response = try! payload?.getArgument(type: SignalR?.self)
print("Response: \(response!)")
})
Model
struct SignalR: Codable {
let type: Int?
let target: String?
let arguments: [Argument]?
}
struct Argument: Codable {
let id: ID
enum CodingKeys: String, CodingKey {
case id = "_id"
}
}
struct ID: Codable {
let timestamp, machine, pid, increment: Int
let creationTime: Date
}

Use do try catch so you can get a better error instead of a crash.
self.chatHubConnection!.on(method: "SendMessage", callback: { (payload: ArgumentExtractor?) in
do{
let response = try payload?.getArgument(type: SignalR?.self)
print("Response: \(response!)")
}catch{
print(error)
}
})

Related

How to decode an http response data from URLSession and map to a response structure swift

I am new to swift programming..was able to obtain a successful response from URLSession but I am unable to parse (decode) the data object to my desired APIResponse Structure
this is my url request code:
func load(urlRequest: URLRequest, withCompletion completion: #escaping (_ response: APIResponse) -> Void) {
let task = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
guard error == nil else {
print("Error fetching data from server\nERROR: \(String(describing: error))")
return
}
guard let jsonData = data else {
print("Response Data is empty")
return
}
printResponseBody(response: data)
let decoder = JSONDecoder()
let response = try? decoder.decode(APIResponse.self, from: jsonData)
guard let decodedResponse = response else {
print("Unable to parse data from response")
return
}
print("Decoded Response: ", decodedResponse)
DispatchQueue.main.async { completion(decodedResponse) }
}
task.resume()
}
and here is my response structure which i need the response data to map to to use in my code:
struct APIResponse: Codable {
let responseCode: Int
let data: ResultSet
let meta: Meta
enum CodingKeys: String, CodingKey {
case responseCode = "response_code"
case data, meta
}
}
// MARK: - DataClass
struct ResultSet: Codable {
let appVersionUpdate: String
let offers: [Offer]
let rate: Int
enum CodingKeys: String, CodingKey {
case appVersionUpdate = "app_version_update"
case offers, rate
}
}
// MARK: - Offer
struct Offer: Codable, Identifiable {
let id: Int
let title: String
let image: String?
let r, resultCount: Double
enum CodingKeys: String, CodingKey {
case id, r = "r"
case title, image
case resultCount = "result_count"
}
}
// MARK: - Meta
struct Meta: Codable {
let apiVersion: Int
enum CodingKeys: String, CodingKey {
case apiVersion = "api_version"
}
this is the json from server which I am trying to decode
{
"response_code": 0,
"data": {
"app_version_update": "",
"offers": [
{
"title": "Special Scheme1",
"image": "http://59.145.109.138:11101/Offers/BGL_banner_1080_x_540_1.jpg",
"r": 1.0,
"result_count": 5.0
},
{
"title": "test 1",
"image": "http://59.145.109.138:11101/Offers/Yoho-National-Park2018-10-27_10-10-52-11.jpg",
"r": 2.0,
"result_count": 5.0
},
{
"title": "Offer Test 1234444",
"image": "http://59.145.109.138:11101/Offers/Stanley-Park2018-10-27_10-11-27-44.jpg",
"r": 3.0,
"result_count": 5.0
}
],
"rate": 2000
},
"meta": {
"api_version": 2.0
}
}
whenever i run this code i am getting the "unable to parse data from response" error. Would really appreciate if someone tells me what I am doing wrong here
The problem is with decoding the id in Offer. Replace your Offer with this:
struct Offer: Codable, Identifiable {
let id: Int
let title: String
let image: String?
let r, resultCount: Int
enum CodingKeys: CodingKey {
case id, r, title, image, resultCount
var stringValue: String {
switch self {
case .id, .r: return "r"
case .title: return "title"
case .image: return "image"
case .resultCount: return "result_count"
}
}
}
}
Notes
First of all, you shouldn't have gotten rid of the error. Instead you could print the error and try to find out whats going wrong.
If you declare enum CodingKeys: String, CodingKey, the raw value for each case Must be different from the other or the Xcode will complain. In your case it didn't complain because id is the requirement of the Identifiable protocol, but it also didn't even use the raw value that you set for id. If you want to use the same key for 2 different variables, you should do the same thing i did above.
Better code
This will work the same as the your code, but is quite cleaner:
struct Offer: Codable, Identifiable {
var id: Int { r }
let title: String
let image: String?
let r, resultCount: Int
enum CodingKeys: String, CodingKey {
case r, title, image, resultCount
}
}
It basically says everytime you need id, get it from r. You can also remove the stringValue in CodingKeys as i did, and use the CodingKeys: String conformation.

Getting error : - The data couldn’t be read because it isn’t in the correct format in Swift using Alamofire and Codable

I am calling api using alamofire and codable . And i am getting api response succesfully. But when i am getting extra parameter in this api i am getting error like "The data couldn’t be read because it isn’t in the correct format".
My code is here
My Model class is:
import Foundation
struct SingleUserProfile: Codable {
let result: [SingleProfileResult]?
let success: Int?
let message: String?
enum CodingKeys: String, CodingKey {
case result = "Result"
case success = "Success"
case message = "Message"
}
}
struct SingleProfileResult: Codable {
let userID: Int?
let firstName: String?
let location: String?
let state, about: String?
let totalScore: Int?
let sun, rising, moon: String?
let ego, communication, birthStar, sexual: Int?
let intellectual, temperament, emotional, healthGenetic: Int?
let profilePic: String?
let numberOfPhotos: Int?
let imagelist: [UserImagelist]?
let verified: Int?
let dateOfBirth, timeOfBirth: String?
let age, isLastPage: Int?
enum CodingKeys: String, CodingKey {
case userID = "User_Id"
case firstName = "First_Name"
case location = "Location"
case state = "State"
case about = "About"
case totalScore = "TotalScore"
case sun
case rising = "Rising"
case moon = "Moon"
case ego = "Ego"
case communication = "Communication"
case birthStar = "Birth_star"
case sexual = "Sexual"
case intellectual = "Intellectual"
case temperament = "Temperament"
case emotional = "Emotional"
case healthGenetic = "Health_Genetic"
case profilePic = "Profile_Pic"
case numberOfPhotos = "NumberOfPhotos"
case imagelist, verified
case dateOfBirth = "DateOfBirth"
case timeOfBirth = "TimeOfBirth"
case age = "Age"
case isLastPage = "IsLastPage"
}
}
struct UserImagelist: Codable {
let userID: Int?
let imagePath, photoID: String?
enum CodingKeys: String, CodingKey {
case userID = "User_Id"
case imagePath = "Image_path"
case photoID = "Photo_Id"
}
var getFullURL : String{
return BASE_URL + (imagePath ?? "")
}
}
In my Constant class
let SINGLE_USER_PROFILE_DETAILS = URL_BASE + GET_USER_PROFILELIST
typealias singleUserProfileDetailsCompletion = (SingleUserProfile?) -> Void
In my Api Request class
//MARK:- Get profile details for
func getSingleUseProfileDetails(currentUserId: Int, accessToken: String, completion: #escaping singleUserProfileDetailsCompletion) {
guard let url = URL(string: "\(SINGLE_USER_PROFILE_DETAILS)?UserID=\(currentUserId)") else{return}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = HTTPMethod.get.rawValue
urlRequest.addValue(accessToken, forHTTPHeaderField: "Authorization")
Alamofire.request(urlRequest).responseJSON { (response) in
if let error = response.result.error {
debugPrint(error)
completion(nil)
return
}
guard let data = response.data else { return completion(nil)}
Common.sharedInstance().printURLRequestParameter(url: url, urlRequest: urlRequest, accessToken: accessToken)
Common.sharedInstance().printRequestOutput(data: data)
let jsonDecoder = JSONDecoder()
do {
let person = try jsonDecoder.decode(SingleUserProfile.self, from: data)
completion(person)
} catch {
debugPrint(error)
completion(nil)
}
}
}
The code is working fine. But if new extra parameter is coming i am getting error like "The data couldn’t be read because it isn’t in the correct format". How to write the model class, so that if new data will come, it will handle the situation automatically
Please help me to resolve the issue.

Parse Codable classes and avoid repetition

I have a JSON response as the following:
{
"http_status": 200,
"success": true,
"has_error": false,
"error": [
""
],
"response": {
"token": "",
"verified": false,
"message": ""
}
}
As far as i can say for an app API usage http_status, success, has_error, error are shared between all APIS and thus i will create a Codable class to handle it, but the response could be different model, so here is what I'm trying to do.
I have created a general response class as the below, so this class i can use for all apis in the project to avoid duplication of same class but different names:
class GeneralResponse:Codable {
let http_status: Int?
let success, has_error: Bool?
let error: [String]?
enum CodingKeys: String, CodingKey {
case http_status = "http_status"
case success = "success"
case has_error = "has_error"
case error = "error"
}
init(http_status: Int?, success: Bool?, has_error: Bool?,error: [String]?) {
self.http_status = http_status
self.success = success
self.has_error = has_error
self.error = error
}
}
Now i have created the response class which will handle for now the registration response:
class RegistrationResponseDetails: Codable {
let token: String?
let verified: Bool?
let message: String?
init(token: String?, verified: Bool?, message: String?) {
self.token = token
self.verified = verified
self.message = message
}
}
And lets say i need to parse the registration the response so here is what i did, i have created a class and used both of them:
class RegistrationResponse: Codable {
let generalResponse:GeneralResponse?
let response: RegistrationResponseDetails?
init(generalResponse: GeneralResponse?, response: RegistrationResponseDetails?) {
self.generalResponse = generalResponse
self.response = response
}
}
So i will mainly use RegistrationResponse to parse the response which will parse "generalResponse" which includes http_status, success, has_error, error, and then response will parse the desired response object.
But at some point generalResponse object is always nil and response has the data parsed correctly, what should i do to make generalResponse get parsed without duplication in each api, because in each api i will have generalResponse object so is it possible to solve it ?
Note: I'm using Alamofire as the networking library.
You can make your GeneralResponse generic and tell it what type to use when parsing the response:
class GeneralResponse<T: Codable>: Codable {
let http_status: Int?
let success, has_error: Bool?
let error: [String]?
let response: T?
}
class RegistrationResponseDetails: Codable {
let token: String?
let verified: Bool?
let message: String?
}
Then you can give it the inner response class when you parse the json:
let generalResponse = try JSONDecoder().decode(GeneralResponse<RegistrationResponseDetails>.self, from: jsonData)
// generalResponse.response is of type RegistrationResponseDetails?
First of all if
http_status, success, has_error, error are shared between all APIS
why are the class properties optional?
If the mentioned keys are the same but the value for key response is different use generics.
In most cases structs are sufficient.
struct JSONParser<T : Decodable> {
struct Response<U : Decodable> : Decodable {
let httpStatus: Int
let success, hasError: Bool
let error: [String]
let response : U
}
let responseData : Response<T>
init(data: Data) throws {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
responseData = try decoder.decode(Response.self, from: data)
}
}
Then create the different structs e.g.
struct RegistrationResponseDetails : Decodable {
let token: String
let verified: Bool
let message: String
}
and parse it
let parser = try JSONParser<RegistrationResponseDetails>(data: data)
let registrationResponseDetails = parser.responseData.response
For a simple case
class Main:Decodable {
let name:String? // insert all shared vars
}
class Sub:Main {
let id:String?
}
This will parse
{
"name" : "rr" ,
"id" : "oo"
}

Unable to parse response with Swift Codable

Unable to decode json response from server with Decodable
A help or a suggestion would be appreciated
JSON:
*
["error": <__NSArrayM 0x60400044ab60>(
)
, "data": <__NSArrayM 0x60400044fae0>(
{
id = 0;
name = all;
},
{
id = 1;
name = "MONTHLY SUPPLIES";
}
)
, "success": 1]
//Response is in Dictionary of Array
Code:
struct CategoryData: Decodable {
var categories: [Category]! // Array
//codable enum case
private enum DataKeys: String, CodingKey {
case data
}
// Manually decode values
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DataKeys.self)
let data = try container.decode([[String: String]].self, forKey: .data)
print(data)
/* Category is a class here contains 2 variable name and id.*/
categories = data.map({ Category($0) })
print(categories)
}
}
Juse make your Category structure conform to Codable. You should also map categories to "data".
//: Playground - noun: a place where people can play
import Foundation
struct CategoryData: Codable {
let categories: [Category]
private enum CodingKeys: String, CodingKey {
case categories = "data"
}
}
struct Category: Codable {
let id: Int
let name: String
}
// create json mock by encoding
let category1 = Category(id: 0, name: "all")
let category2 = Category(id: 1, name: "MONTHLY SUPPLIES")
let categoryData = CategoryData(categories: [category1, category2])
let json = try! JSONEncoder().encode(categoryData)
print(String(bytes: json, encoding: String.Encoding.utf8)) // Optional("{\"data\":[{\"id\":0,\"name\":\"all\"},{\"id\":1,\"name\":\"MONTHLY SUPPLIES\"}]}")
// create category data by decoding json (your actual question)
do {
let categoryDataAgain = try JSONDecoder().decode(CategoryData.self, from: json)
for category in categoryDataAgain.categories {
print(category.id) // 0, 1
print(category.name) // "all", "MONTLY SUPPLIES"
}
} catch {
print("something went wrong")
}

Swift 4 + Alamofire Decodable Json URL format

I have a JSON format which I do not decodable with Alamofire.
Here is my json:
"data":[
{
"id":37,
"status":"A\u00e7\u0131k",
"department":"Muhasebe",
"title":"Y\u00f6netim Panelinden Deneme 4 - Mail Kontrol",
"message":"<p>Y\u00f6netim Panelinden Deneme 4 - Mail Kontrol<br><\/p>",
"file":null,
"created_at":{
"date":"2018-01-13 01:59:49.000000",
"timezone_type":3,
"timezone":"UTC"
},
"replies":[
{
"id":6,
"ticket_id":37,
"admin_id":null,
"user_id":8593,
"message":"<p>test<\/p>",
"file":"uploads\/tickets\/8593-P87wd8\/GFV6H5M94y5Pt27YAxZxHNRcVyFjD554i80og3xk.png",
"created_at":"2018-01-18 11:16:55",
"updated_at":"2018-01-18 11:16:55"
}
]
},
Here is my model for the JSON:
struct TeknikDestek : Decodable {
var id: Int?
var status: String?
var title: String?
var department: String?
var message: String?
var replies: [Replies]?
}
struct Replies: Decodable {
var replyid: Int?
var ticket_id: Int?
var admin_id: Int?
var user_id: Int?
var message: String?
}
I called it Alamofire, but it does not come back when I do response.data.
Alamofire.request("https://myurl.com.tr/api/tickets/\(userid)").responseJSON { (response) in
switch response.result {
case .success:
if((response.result) != nil) {
let jsonData = response.data
print("jsonData: \(test)")
do{
self.allReplies = try JSONDecoder().decode([TeknikDestek].self, from: jsonData!)
print(self.allReplies)
for reply in self.allReplies {
print("Reply: \(reply)")
}
}catch {
print("Error: \(error)")
}
self.view.dismissNavBarActivity()
}
case .failure(let error):
print(error)
}
}
This is the error console:
How can I make it work? I've spent several hours now but without success. Please help me. Many Thanks.
The question is not related to Alamofire. It's only related to JSONDecoder / Decodable
You need an umbrella struct for the root object, which is a dictionary containing the data key, not an array. That's what the error message states.
struct Root : Decodable {
let data : [TeknikDestek]
}
Then decode Root
let root = try JSONDecoder().decode(Root.self, from: jsonData!)
and get the replies with
self.allReplies = root.data.first?.replies // returns `nil` if data is empty
Note: It's highly recommended to name data structs in singular form (e.g. Reply), semantically you have a collection of singular items