I'm new to Swift and I started working on a Swift 4 project with a PHP server.
I use Alamofire for requests, and print the data using print(). This is is what i'm getting:
{"error":false,"n":"Raghad"}
But when I want to convert it to String, it returns "" (empty) and
when I convert to boolean it returns the value correctly.
So, how can I fix it?
let wJSON : JSON = JSON(response.result.value!)
print(wJSON["n"].stringValue)
print(wJSON["error"].boolValue)
Simple solution using Decodable, define a Struct that conforms to the Decodable protocol for your dictionary
struct Reply: Decodable {
let error: Bool
let n: String
}
let data = response.data
do {
let result = try JSONDecoder().decode(Reply.self, from: data)
print("\(result.n) \(result.error)")
} catch {
print(error)
}
I change the responseString to responseJSON
Alamofire.request(Url!, method: .post, parameters: par).validate().responseJSON { response in if response.result.isSuccess { let wJSON : JSON = JSON(response.result.value!)
and it's work
Related
I'm using Firebase Database and I'm attempting to retrieve and use data with NSObject. I'm receiving an NSUnknownKeyException error when running the app, causing it to crash.
NSObject:
class WatchList: NSObject {
var filmid: Int?
}
Firebase Code:
ref.child("users").child(uid!).child("watchlist").observe(DataEventType.childAdded, with: { (info) in
print(info)
if let dict = info.value as? [String: AnyObject] {
let list = WatchList()
list.setValuesForKeys(dict)
print(list)
}
}, withCancel: nil)
I'm not sure of what could cause this.
Also, to enhance this solution is their a way to take this data and, instead of using NSObject, use Codable and JSONDecoder with the Firebase data?
You can simply use JSONSerialization to convert the snapshot value property from Any to Data:
let data = try? JSONSerialization.data(withJSONObject: snapshot.value)
You can also extend Firebase DataSnapshot type and add a data and json string properties to it:
import Firebase
extension DataSnapshot {
var data: Data? {
guard let value = value, !(value is NSNull) else { return nil }
return try? JSONSerialization.data(withJSONObject: value)
}
var json: String? { data?.string }
}
extension Data {
var string: String? { String(data: self, encoding: .utf8) }
}
usage:
guard let data = snapshot.data else { return }
It's 2021 now.
Firebase finally added support for decoding Firestore documents. Just let your objects conform to Codable and decode like this:
let result = Result {
try document?.data(as: City.self)
}
switch result {
case .success(let city):
if let city = city {
print("City: \(city)")
} else {
print("Document does not exist")
}
case .failure(let error):
// A `City` value could not be initialized from the DocumentSnapshot.
print("Error decoding city: \(error)")
}
Just don't forget to add the 'FirebaseFirestoreSwift' pod and then import it into your file.
Read more:
https://firebase.google.com/docs/firestore/query-data/get-data#custom_objects
Original answer
A really nice library to use here is Codable Firebase which I am also using in my project. Just make your class / struct conform to Codable protocol and use FirebaseDecoder to decode your Firebase data into a Swift object.
Example:
Database.database().reference().child("model").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value else { return }
do {
let model = try FirebaseDecoder().decode(Model.self, from: value)
print(model)
} catch let error {
print(error)
}
})
I have tried pretty much all the potential solutions on stackoverflow and so far no luck,
This is my json response:
[
"{\"id\":5,\"request_id\":\"rqst5c17fc752d44f1.15452158\",\"business_name\":\"611 Solutions\",\"business_email\":\"611thesolutions#gmail.com\",\"title\":\"123ABC - TESTING\",\"details\":\"Package is fragile, please haul with care\",\"load_description\":\"Royal Timber\",\"amount_offered\":\"2500\",\"pickup_address\":\"123 Colliumeal Dr, Fort Wayne, Indiana\",\"dropoff_address\":\"647 Airportway, Chicago, Illinois\",\"timestamp\":\"2018-12-17 19:43:49\"}"
]
Notice there are backslashes within the key and values of the json and my parsing is failing, this is how I am parse the json:
Alamofire.request(JOB_REQUEST_BASE_URL, method: .post, parameters: parameter, encoding: URLEncoding(), headers: nil).responseArray { (response: DataResponse<[JobResponseDataObject]>) in
log.debug("Fetching Job Requests...")
switch response.result {
case .success(let responseArray) :
log.debug(response.debugDescription)
log.debug("Sucessfully fetch job requests")
log.debug("Job request counts: \(responseArray.count)")
completionHandler(JobRequest.fetchJobRequest.Response(jobResponses: responseArray), nil)
case .failure(let error) :
log.debug("Fetching error: JobRequest")
log.debug(error.localizedDescription)
completionHandler(nil, .FailedToFetchEmptyJobRequests)
}
}
I have also tried fetching the pure string using .responseString and doing let json = response.result.value?.replacingOccurrences(of: "\\", with: "") and mapping it like so let jobs = Mapper<JobResponseDataObject>().map(JSONString: json!) so far no luck too. Please help
Thanks
You can try
if let str = responseArray.first as? String , let data = str.data(using:.utf8) {
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode(Root.self,from:data)
}
catch {
print(error)
}
}
struct Root: Codable {
let id: Int
let requestId, businessName, businessEmail, title: String
let details, loadDescription, amountOffered, pickupAddress: String
let dropoffAddress, timestamp: String
}
You don't need to remove backslashes - it's just serilized one more time, that means it needs to deserialize back.
Look at: Why json response includes backward slashes in web api response
Just make a Data object from the string item:
let data = stringItem.data(using: .utf8)
then decode normally using JSONDecoder.
I am using two textfields to pass login information to the PHP web service using Alamofire in the following way.
#IBAction func LoginButton(_ sender: Any) {
//getting the username and password
let parameters: Parameters=[
"Name":TextFieldUserName.text!,
"Pass":TextFieldPassword.text!
]
Alamofire.request(URL_USER_LOGIN, method: .post, parameters: parameters).responseJSON
{
response in
//printing response
print(response)
The following Json data is received on login.
[{"code":0,"message":"Check Username and Password....","userid":""}]
I want to use either "code" value (0 for false and 1 for true) or "message" value as String to put into an if - else statement for further steps. If Alamofire is not the best way to go about this, can someone please show another way. Thanks in advance for the help.
Do you need to deserialize the response from the server?
The easiest option is parsing response value as NSDictionary
if let JSON = response.result.value as? NSDictionary {
let code = JSON.value(forKey: "code") as? Int
let message = JSON.value(forKey: "message") as? String
}
You can also use the Codable protocol and the JSONDecoder to decode this response into your structure.
For example, declare struct:
struct LoginResponse: Codable {
var code: Int
var message: String
}
and decode response using JSONDecoder
let jsonDecoder = JSONDecoder()
let loginResponse = try? jsonDecoder.decode(LoginResponse.self, from: response.data)
I receive from a post request, this JSON:
"clinic_info": {
"city": "Querétaro",
"state": "Querétaro",
"country": "México",
"phone": null,
"ext": null,
"coords": "20.6046089,-100.37826050000001",
"location": "Querétaro"
}
But when it is empty the JSON is:
"clinic_info": []
This produces an error: Expected to decode Dictionary but found an array instead.
This is happening because decoder want dictionary and your JSON is array
Need to check before decoding that JSON response is dictionary or Array and do decoding accordingly.
If you find Dictionary then do like this
let myData = try JSONDecoder().decode(YourModel.self, from: jsonData)
If you find Array then do like this
let myData = try JSONDecoder().decode([YourModel].self, from: jsonData)
You can do it using try, throw like that
import Foundation
struct ClinicData: Codable {
let clinicInfo: ClinicInfo?
enum CodingKeys: String, CodingKey {
case clinicInfo = "clinic_info"
}
}
struct ClinicInfo: Codable {
let city, state, country: String
let coords, location: String
}
// MARK: Convenience initializers
extension ClinicData {
init(data: Data) throws {
self = try JSONDecoder().decode(ClinicData.self, from: data)
}
init(_ json: String, using encoding: String.Encoding = .utf8) throws {
guard let data = json.data(using: encoding) else {
throw NSError(domain: "JSONDecoding", code: 0, userInfo: nil)
}
try self.init(data: data)
}
func jsonData() throws -> Data {
return try JSONEncoder().encode(self)
}
func jsonString(encoding: String.Encoding = .utf8) throws -> String? {
return String(data: try self.jsonData(), encoding: encoding)
}
}
**get clinicInfo**
if let clinicData = try? ClinicData.init(data: Data()), let clinicInfo =
clinicData.clinicInfo{
}
The service that provides those JSON responses replies with:
"clinic_info": { ... }
Where ... is a valid JSON object.
But when it is empty, you are saying it looks like this:
"clinic_info": []
Notice the [] that say this is an empty array of objects.
You might want to change the service response (if possible), since it looks inconsistent to me having it return an object when it has valid data, and an array when there is no valid data.
The error message you are getting is clear:
Expected to decode Dictionary but found an array instead.
It expected an object {}, but found an array [].
The Array class has a method for this.
Using typeof will always return "object".
The code below shows how to use the isArray() method in the Array class.
const obj = {
_array: [],
_object: {}
}
console.log(Array.isArray(obj._array)); // true
console.log(Array.isArray(obj._object)); // false
I want convert a json string which contains a double field to JSON object using JSONSerialization.data function. I print the result json object and it shows the double field as string. The following is the sample code:
let test = "{\"statusCode\":2.334}"
do {
let responseJSON = try JSONSerialization.jsonObject(with: test.data(using: String.Encoding.utf16)!, options: [])
print(responseJSON)
} catch {
print(error)
}
The responseJSON as following:
{
statusCode = "2.334";
}
I have two questions:
Is it, in general, all JSON serialization engine will convert double
value to string or it is only happen in Swift JSON serialization?
Anyway to force JSONSerialization to output double, not string?
This is purely an artifact of how the value is printed out — the value you get is in fact a Double. You can confirm this yourself with the following:
import Foundation
let test = "{\"statusCode\":2.334}"
do {
let responseJSON = try JSONSerialization.jsonObject(with: test.data(using: String.Encoding.utf16)!, options: []) as! [String: Any]
print(responseJSON["statusCode"] is Double) // => true
} catch {
print(error)
}