Parsing JSON Lines with Alamofire/Codable - ios

Is it possible to parse JSON lines with Alamofire and codable?
Here is my code right now.
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseString {(response) in
switch response.result {
case .success(let value):
print ("response is \(value)")
case .failure(let error):
print ("error is \(error)")
}
}
This prints all the JSON lines as a string but I want to serialize the response as an array of JSON. How would I do that? The problem with JSON lines is that it returns each set of json on a separate line so it is not familiar to alamofire.
Here is what I tried as if this was traditional JSON which obviously it is not so this did not work:
Alamofire.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: headers).responseJSON {(response) in
switch response.result {
case .success(let value):
print ("response is \(value)")
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let data = try! JSONSerialization.data(withJSONObject: value)
do {
let logs = try decoder.decode([Logs].self, from: data)
completion(logs)
} catch let error {
print ("error parsing get logs: \(error)")
}
case .failure(let error):
print ("failed get logs: \(error) ** \(response.result.value ?? "")")
}
}
For anybody unfamiliar with json lines here is the official format info: http://jsonlines.org
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:39.644199 6124 conversion.go:134] failed to handle multiple devices for container. Skipping Filesystem stats","_ts":1491869920198,"timestamp":"2017-04-11T00:18:39.000Z","_id":"804760774821019649"}
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:39.644167 6124 conversion.go:134] failed to handle multiple devices for container. Skipping Filesystem stats","_ts":1491869920198,"timestamp":"2017-04-11T00:18:39.000Z","_id":"804760774821019648"}
{"_logtype":"syslogline","_ingester":"agent","_ip":"40.121.203.183","pid":5573,"program":"docker","_host":"k8s-master-5A226838-0","logsource":"k8s-master-5A226838-0","_app":"syslog","_file":"/var/log/syslog","_line":"docker[5573]: I0411 00:18:37.053730 6124 operation_executor.go:917] MountVolume.SetUp succeeded for volume \"kubernetes.io/secret/6f322c04-e1d2-11e6-bca0-000d3a111245-default-token-swb07\" (spec.Name: \"default-token-swb07\") pod \"6f322c04-e1d2-11e6-bca0-000d3a111245\" (UID: \"6f322c04-e1d2-11e6-bca0-000d3a111245\").","_ts":1491869917193,"timestamp":"2017-04-11T00:18:37.000Z","_id":"804760762212941824"}

Here is an example of writing custom DataSerializer in Alamofire that you could use for decoding your Decodable object.
I am using an example from random posts json url https://jsonplaceholder.typicode.com/posts
Here is an example of Post class and custom serializer class PostDataSerializer.
struct Post: Decodable {
let userId: Int
let id: Int
let title: String
let body: String
}
struct PostDataSerializer: DataResponseSerializerProtocol {
enum PostDataSerializerError: Error {
case InvalidData
}
var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<[Post]> {
return { request, response, data, error in
if let error = error {
return .failure(error)
}
guard let data = data else {
return .failure(PostDataSerializerError.InvalidData)
}
do {
let jsonDecoder = JSONDecoder()
let posts = try jsonDecoder.decode([Post].self, from: data)
return .success(posts)
} catch {
return .failure(error)
}
}
}
}
You could simply hook this upto your Alamofire client which sends request to remote url like so,
let request = Alamofire.request("https://jsonplaceholder.typicode.com/posts")
let postDataSerializer = PostDataSerializer()
request.response(responseSerializer: postDataSerializer) { response in
print(response)
}
You could also do additional error checking for the error and http response code in serializeResponse getter of the custom serializer.
For you json line, it seems that each json object is separated with new line character in so called json line format. You could simply split the line with new line characters and decode each line to Log.
Here is Log class I created.
struct Log: Decodable {
let logType: String
let ingester: String
let ip: String
let pid: Int
let host: String
let logsource: String
let app: String
let file: String
let line: String
let ts: Float64
let timestamp: String
let id: String
enum CodingKeys: String, CodingKey {
case logType = "_logtype"
case ingester = "_ingester"
case ip = "_ip"
case pid
case host = "_host"
case logsource
case app = "_app"
case file = "_file"
case line = "_line"
case ts = "_ts"
case timestamp
case id = "_id"
}
}
And custom log serializer that you could use with your Alamofire. I have not handled error in the following serializer, I hope you can do it.
struct LogDataSerializer: DataResponseSerializerProtocol {
enum LogDataSerializerError: Error {
case InvalidData
}
var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> Result<[Post]> {
return { request, response, data, error in
if let error = error {
return .failure(error)
}
guard let data = data else {
return .failure(LogDataSerializerError.InvalidData)
}
do {
let jsonDecoder = JSONDecoder()
let string = String(data: data, encoding: .utf8)!
let allLogs = string.components(separatedBy: .newlines)
.filter { $0 != "" }
.map { jsonLine -> Log? in
guard let data = jsonLine.data(using: .utf8) else {
return nil
}
return try? jsonDecoder.decode(Log.self, from: data)
}.flatMap { $0 }
return .success(allLogs)
} catch {
return .failure(error)
}
}
}
}

Alamofire is extensible, so I'd suggest writing your own response ResponseSerializer that can parse JSON by line. It seems that each line should parse fine, they're just not parseable together since the whole document isn't valid JSON.

Related

Swift - Alamofire - Completion Handler is not executed when I call my function

As the title says, I have a function with these parameters:
typealias Handler = (Swift.Result<Any?, APIErrors>) -> Void
func viewApi (ownerId: String, accessToken: String, completionHandler: #escaping Handler ){
var viewArray: [Information] = [Information]()
let headers: HTTPHeaders = [
.authorization("Bearer \(accessToken)")
]
let viewUrl = "\(viewTaskUrl)?userId=\(ownerId)"
let url = URL(string: viewUrl)
var request = URLRequest(url: url!)
request.method = .get
request.headers = headers
var taskList:[Task] = []
AF.request(request).responseJSON(completionHandler: { (response) in
var holder = Information(id: 0, title: "", description: "", status: "")
switch response.result {
case .success(let data):
do{
if response.response?.statusCode == 200 {
// completionHandler(.success("JSON HERE: \
(String(describing: json))"))
let jsonData = try JSONSerialization.data(withJSONObject: data)
let jsonString = String(data: jsonData, encoding: .utf8)
let trySplit = jsonString?.components(separatedBy: ",")
for i in 0...trySplit!.count-1{
if trySplit![i].contains("id"){
let id = trySplit![i].components(separatedBy: ":")[1]
holder.id = Int(id)!
}
if trySplit![i].contains("title"){
let title = trySplit![i].components(separatedBy: ":")[1]
holder.title = title
// print("TITLE: \(title)")
}
if trySplit![i].contains("description"){
let desc = trySplit![i].components(separatedBy: ":")[1]
holder.description = desc
}
if trySplit![i].contains("status"){
let stat = trySplit![i].components(separatedBy: ":")[1]
let edited = stat.replacingOccurrences(of: "}", with: "")
holder.status = edited
viewArray.append(holder)
}
}
}else{
// completionHandler(.failure(.custom(message: "Please check your network connectivity")))
print("Error")
}
}catch{
print(error.localizedDescription)
}
case .failure(let err):
print (err)
}
print("ARRAY: \(viewArray)")
for i in 0 ..< viewArray.count {
let task = Task()
let edited = viewArray[i].status.replacingOccurrences(of: "]", with: "")
let new = viewArray[i].title.replacingOccurrences(of: "\"", with: "")
task.taskName = new
task.id = viewArray[i].id
task.taskStatus = edited
task.taskDescription = viewArray[i].description
task.email = ownerId
taskList.append(task)
}
try! self.realm.write{
self.realm.add(taskList)
}
}).resume()
}
and that function runs well, it does what it needs to do by returning a JSON object and storing the contents in Realm but when I call it in my main ViewController, it does not go in the completionHandler and tries to store the contents from Realm into an array returns an empty array even though the Realm is populated. Here is the call in my ViewController:
self.APIManager.viewApi(ownerId: self.userId!, accessToken: self.token!) { (result) in
switch result{
case .success(_):
//does not run
print("SUCCESS COMPLETION")
case .failure(let err):
print(err.localizedDescription)
}
}
Is there any way for the function to run before the rest of the code is executed so that the array is not empty and so that it actually goes in my completionHandler?
P.S. Don't mind the janky way I'm storing the JSON Object since I still have to find a better way to store it into an array besides converting it to string and then cutting it
try something like this approach:
func viewApi (ownerId: String, accessToken: String, completionHandler: #escaping Handler ) {
//...
AF.request(request).responseJSON { response in // <-- here
//...
completionHandler(.success("JSON HERE:(String(describing: json))")) // <-- here
//...
}
//...
}
what is your Handler type ? and why the completion handler is not setting
return the handler type according to the failure, success example.
self.APIManager.viewApi(ownerId: self.userId!, accessToken: self.token!, completionHandler: CompletionHandler) { (result) in
switch result{
case .success(_):
completionHandler(success:true)
case .failure(let err):
print(err.localizedDescription)
completionHandler(success: false)
}
}
you can return the raw response as it is in the completion, write a helper class to process the json response as per your requirement

how to determine the specific 409 error from an AFError?

I have a method that returns a Single<(HTTPURLResponse, Any)> doing a call to a webservice.
This call returns an 409 for multiple reasons and this reason is passed as a JSON in the response.
I know the JSON is in the data attribute of the DataResponse object but I would like to have it in the AFError that I pass when an error occurs. I want to display the specific 409 error message related to the JSON response to the user to allow him understand what happened.
How could I do that ?
I searched for that in Stackoverflow and also on the github of Alamofire but couldn't find any help to my case.
return Single<(HTTPURLResponse, Any)>.create(subscribe: { single in
let request = self.sessionManager.request(completeURL, method: httpMethod, parameters: params, encoding: encoding, headers: headers)
request.validate().responseJSON(completionHandler: { (response) in
let result = response.result
switch result {
case let .success(value): single(.success((response.response!, value)))
case let .failure(error): single(.error(error))
}
})
return Disposables.create { request.cancel() }
})
I'm working with Alamofire 4.9.1
request.validate().responseJSON { (response) in
let statusCode = response.response?.statusCode ?? 0
guard statusCode != 409 else {
if let data = response.data, let errorJson = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
let errorMessage = errorJson["message"] as? String
let customError = CustomError(message: errorMessage)
single(.error(customError))
}
return
}
let result = response.result
switch result {
case let .success(value): single(.success((response.response!, value)))
case let .failure(error): single(.error(error))
}
}
I guess you can achieve your requirement by this way. create a custom Error class to pass the error to completion. dont forget to call completion if errorJson is not serialised.
class CustomError: Error {
var localizedDescription: String { message ?? "" }
var message: String?
init(message: String?) {
self.message = message
}
}

How to convert '__NSIArrayI' to JSON Array in Swift?

I am new to Swift 4 and working on an iOS app. I have the following JSON data retrieved from the server which will be extracted to data table.
[
{
"fund_nav_validity":"24 August to 31 August 2016\n",
"fund_nav_cost":10,
"fund_nav_sell":9.85,
"nav_id":118,
"fund_nav_buy":10,
"fund_id":1,
"nav_as_on_date":"24-Aug-16",
"fund_nav_market":9.95
},
{
"fund_nav_validity":"04 September to 07 September 2016\n",
"fund_nav_cost":10,
"fund_nav_sell":9.85,
"nav_id":117,
"fund_nav_buy":10,
"fund_id":1,
"nav_as_on_date":"01-Sep-16",
"fund_nav_market":9.95
}
]
I have done following task in the navdata function to retrieve the '__NSIArrayI' to JSON:
func getNAVData(){
Alamofire.request(url, method: .post).responseJSON{
response in
if response.result.isSuccess{
print("Success")
//let navDataJSON : JSON = JSON(response.result.value!
let navDataJSON : JSON = JSON(response.result.value!)
print(navDataJSON)
}else{
print("Failed")
}
}
}
I need to convert it to JSON Array which is done in java. How to do the similar task in Swift 4?
you are in the right way, in here you need to convert your JSON response to array the reason your json started with this type[{}] and check your JSON response contains the value or not in intially. try the below code
if response.result.isSuccess{
print("Success")
// convert your JSON to swiftyJSON array type if your JSON response as array as well as check its contains data or not
guard let resJson = JSON(responseObject.result.value!).array , !resJson.isEmpty else { return
}
// create the Swifty JSON array for global access like wise
var navDataJSON = [JSON]() //[JSON]() --> Swifty-JSON array memory allocation.
navDataJSON = resJson
}
You're getting the JSON array and if you want to convert to model Array then you can use create Model Struct and confirm to Decodable Protocol, without using SwiftyJSON using inbuilt solution to parse JSON.
import Foundation
// MARK: - Element
struct Model: Decodable {
let fundNavValidity: String
let fundNavCost: Int
let fundNavSell: Double
let navId: Int
let fundNavBuy: Int
let fundId: Int
let navAsOnDate: String
let fundNavMarket: Double
enum CodingKeys: String, CodingKey {
case fundNavValidity = "fund_nav_validity"
case fundNavCost = "fund_nav_cost"
case fundNavSell = "fund_nav_sell"
case navId = "nav_id"
case fundNavBuy = "fund_nav_buy"
case fundId = "fund_id"
case navAsOnDate = "nav_as_on_date"
case fundNavMarket = "fund_nav_market"
}
}
then in getNAVData method you can use JSONDecoder to covert to Model struct array like below
func getNAVData() {
Alamofire.request(url, method: .post).responseJSON {
response in
switch response.result {
case .success(let data):
do {
let modelArray = try JSONDecoder().decode([Model].self, from: data)
print(modelArray)
} catch {
print("Error: \(error)")
}
case .failure(let error):
print(error.localizedDescription)
}
}
}
If you want to use SwiftyJSON return responseData rather than responseJSON. This avoids a double conversion, and handle always a potential error
func getNAVData(){
Alamofire.request(url, method: .post).responseData { response in
switch response.result {
case .success(let data):
let navDataJSON = JSON(data).arrayValue
print(navDataJSON)
case .failure(let error): print(error)
}
}
}
The result navDataJSON is a JSON array.
However in Swift 4+ it's highly recommended to use the more efficient and built-in Codable protocol
struct Fund : Decodable {
let fundNavValidity, navAsOnDate : String
let fundNavCost, navId, fundNavBuy, fundId : Int
let fundNavSell, fundNavMarket : Double
}
func getNAVData(){
Alamofire.request(url, method: .post).responseData { response in
switch response.result {
case .success(let data):
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let navDataJSON = decoder.decode([Fund].self, from : data)
print(navDataJSON)
} catch { print(error) }
case .failure(let error): print(error)
}
}
}
The result is an array of Fund structs.

errors decoding json with Alamofire

I try to decode JSON data from web using Alamofire. My app is sending the same GET requests, which differs by id. Some JSON is decoded successfully, but some can not be decoded. What can be the problem? How can I solve this issue? All responses are checked by JSON validator and are valid. Trying to decode with URLSession.shared.dataTask(with: url) just cannot decode a single response, even response that was successfully decoded with Alamofire
Code is:
var hostURL = "https://public-api.nazk.gov.ua/v1/declaration/"
hostURL = hostURL + declarationID
print(hostURL)
Alamofire.request(hostURL).responseData { response in
switch response.result {
case .success(let data):
let declarationInfoElement = try? JSONDecoder().decode(DeclarationInfoElement.self, from: data)
print(declarationInfoElement)
case .failure:
print("fail")
}
}
Console output is:
https://public-api.nazk.gov.ua/v1/declaration/3509369f-b751-444a-be38-dfa66bb8728f
https://public-api.nazk.gov.ua/v1/declaration/3e7ad106-2053-48e4-a5d2-a65a9af313be
https://public-api.nazk.gov.ua/v1/declaration/743b61d5-5082-409f-baa0-9742b4cc2751
https://public-api.nazk.gov.ua/v1/declaration/5d98b3d9-8ca6-4d5d-b39f-e4de98d451aa
https://public-api.nazk.gov.ua/v1/declaration/7e3c488c-4d6a-49a3-aefb-c760f317dca4
nil
Optional(Customs_UA.DeclarationInfoElement(id: "4647cd5d-5877-4606-8e61-5ac5869b71e0")
nil
nil
nil
#objc func getJSON(){
let hostURL = "https://public-api.nazk.gov.ua/v1/declaration/"
print(hostURL)
Alamofire.request(hostURL).responseData { response in
switch response.result {
case .success(let data):
do {
if let json = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? Dictionary<String,Any>
{
print(json)
} else {
print("bad json")
}
} catch let error as NSError {
print(error)
}
print(data)
case .failure:
print("fail")
}
}
}
The problem is that some parameters of the JSON are optional. You have to post your DeclarationInfoElement class to check.
Use something like this to detect the error.
class DeclarationInfoElement: Decodable {
let id: String?
let created_date: String?
/// and so on
}

Alamofire response object mapping

I am an android developer new to swift 3 programming, I am using Alamofire for making api calls and to avoid tedious json paring I am using AlamofireObjectMapper library.
I have a ApiController which has a function to make api calls below is the code for that:
public static func makePostRequest<T: Mappable>(url: String, params: Parameters, networkProtocol: NetworkProtocol, responseClass: T){
let headers = getHeaders()
networkProtocol.showProgress()
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers)
.validate()
.responseData{ response in
let json = response.result.value
var jsonString = String(data: json!, encoding: String.Encoding.utf8)
let responseObject = responseClass(JSONString: jsonString!)
switch(response.result){
case .success(_):
networkProtocol.hideProgress()
networkProtocol.onResponse(response: response)
break
case .failure(_):
networkProtocol.hideProgress()
networkProtocol.onErrorResponse(response: response)
break
}
}
The Json response template I am getting from server is:
{
"some_int": 10,
"some_array":[...]
}
Below is my model class:
import ObjectMapper
class BaseResponse: Mappable {
var some_int: Int?
var some_array: [Array_of_objects]?
required init?(map: Map) {
some_int <- map["some_int"]
some_array <- map["some_array"]
}
func mapping(map: Map) {
}
}
And below is the function to class make the api call:
public static func callSomeApi(params: Parameters, networkProtocol: NetworkProtocol){
ApiHelper.makePostRequest(url: AppConstants.URLs.API_NAME, params: params, networkProtocol: networkProtocol, responseClass: BaseResponse)
}
Now the error is in the below line
let responseObject = responseClass(JSONString: jsonString!)
I am not able to understand how to convert jsonString into the responseClass generic object which I am accepting from View controller
Someone please help me resolve this, stuck on this issue for quite a while now.
You can use AlamofireMapper:
With json:
{
"page":1,
"per_page":3,
"total":12,
"total_pages":4,
"data":[
{
"id":1,
"first_name":"George",
"last_name":"Bluth",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
},
{
"id":2,
"first_name":"Janet",
"last_name":"Weaver",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
},
{
"id":3,
"first_name":"Emma",
"last_name":"Wong",
"avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/olegpogodaev/128.jpg"
}
]
}
Swift class:
class UserResponse: Decodable {
var page: Int!
var per_page: Int!
var total: Int!
var total_pages: Int!
var data: [User]?
}
class User: Decodable {
var id: Double!
var first_name: String!
var last_name: String!
var avatar: String!
}
Request with alamofire
let url1 = "https://raw.githubusercontent.com/sua8051/AlamofireMapper/master/user1.json"
Alamofire.request(url1, method: .get
, parameters: nil, encoding: URLEncoding.default, headers: nil).responseObject { (response: DataResponse<UserResponse>) in
switch response.result {
case let .success(data):
dump(data)
case let .failure(error):
dump(error)
}
}
Link: https://github.com/sua8051/AlamofireMapper
Generic Response Object Serialization using Swift 4 Codable
If you don't want to use another dependency like ObjectMapper you can do the following way but you may have to make some chagnes.
Following is a typical model which we use to deserialize JSON data with generics using Alamofire. There is plenty of examples and excellent documentation on Alamofire.
struct User: ResponseObjectSerializable, ResponseCollectionSerializable, CustomStringConvertible {
let username: String
let name: String
var description: String {
return "User: { username: \(username), name: \(name) }"
}
init?(response: HTTPURLResponse, representation: Any) {
guard
let username = response.url?.lastPathComponent,
let representation = representation as? [String: Any],
let name = representation["name"] as? String
else { return nil }
self.username = username
self.name = name
}
}
Using Codable protocol introduced in Swift 4
typealias Codable = Decodable & Encodable
The first step in this direction is to add helper functions that
will do half of the work in deserialization JSON data and handle
errors. Using Swift extensions we add functions to decode incoming
JSON into our model struct/class that we will write afterward.
let decoder = JSONDecoder()
let responseObject = try? decoder.decode(T.self, from: jsonData)
The decoder (1) is an object that decodes instances of a data type from JSON objects.
Helper functions
extension DataRequest{
/// #Returns - DataRequest
/// completionHandler handles JSON Object T
#discardableResult func responseObject<T: Decodable> (
queue: DispatchQueue? = nil ,
completionHandler: #escaping (DataResponse<T>) -> Void ) -> Self{
let responseSerializer = DataResponseSerializer<T> { request, response, data, error in
guard error == nil else {return .failure(BackendError.network(error: error!))}
let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
guard case let .success(jsonData) = result else{
return .failure(BackendError.jsonSerialization(error: result.error!))
}
// (1)- Json Decoder. Decodes the data object into expected type T
// throws error when failes
let decoder = JSONDecoder()
guard let responseObject = try? decoder.decode(T.self, from: jsonData)else{
return .failure(BackendError.objectSerialization(reason: "JSON object could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
}
return .success(responseObject)
}
return response(queue: queue, responseSerializer: responseSerializer, completionHandler: completionHandler)
}
/// #Returns - DataRequest
/// completionHandler handles JSON Array [T]
#discardableResult func responseCollection<T: Decodable>(
queue: DispatchQueue? = nil, completionHandler: #escaping (DataResponse<[T]>) -> Void
) -> Self{
let responseSerializer = DataResponseSerializer<[T]>{ request, response, data, error in
guard error == nil else {return .failure(BackendError.network(error: error!))}
let result = DataRequest.serializeResponseData(response: response, data: data, error: error)
guard case let .success(jsonData) = result else{
return .failure(BackendError.jsonSerialization(error: result.error!))
}
let decoder = JSONDecoder()
guard let responseArray = try? decoder.decode([T].self, from: jsonData)else{
return .failure(BackendError.objectSerialization(reason: "JSON array could not be serialized \(String(data: jsonData, encoding: .utf8)!)"))
}
return .success(responseArray)
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
Second, I earlier mentioned “using Swift 4 Codable” but if all we
want is to decode JSON from the server, we only need is a model
struct/class that conforms to protocol Decodable. (If you have the
same structure you want to upload you can use Codable to handle both
decoding and encoding) So, now our User model struct now looks like
this.
struct User: Decodable, CustomStringConvertible {
let username: String
let name: String
/// This is the key part
/// If parameters and variable name differ
/// you can specify custom key for mapping "eg. 'user_name'"
enum CodingKeys: String, CodingKey {
case username = "user_name"
case name
}
var description: String {
return "User: { username: \(username), name: \(name) }"
}
}
Lastly, our function call to API looks like.
Alamofire.request(Router.readUser("mattt"))).responseObject{ (response: DataResponse<User>) in
// Process userResponse, of type DataResponse<User>:
if let user = response.value {
print("User: { username: \(user.username), name: \(user.name) }")
}
}
For more complex (nested) JSON, the logic remains the same and only modifications you need in model struct/class is that all structs/classes must conform to Decodable protocol and Swift takes care of everything else.
You can use SwiftyJSON: https://cocoapods.org/pods/SwiftyJSON
Here is some example code could help you:
Alamofire.request(endpointURL, method: .get, parameters: params, encoding: URLEncoding.default, headers: nil).validate().responseJSON()
{
(response) in
if response.result.isFailure
{
print("ERROR! Reverse geocoding failed!")
}
else if let value = response.result.value
{
var country: String? = nil
var county: String? = nil
var city: String? = nil
var town: String? = nil
var village: String? = nil
print("data: \(value)")
let json = JSON(value)
print("json: \(json)")
country = json["address"]["country"].string
county = json["address"]["county"].string
city = json["address"]["city"].string
town = json["address"]["town"].string
village = json["address"]["village"].string
}
else
{
print("Cannot get response result value!")
}
}
Please let you know the code has been simplified (lot of line has been removed) and pasted here from my actual project, so this code is not tested, maybe contains typos or something like that, but the logic is visible
For Object Mapping you need follow this with AlamofireObjectMapper .
//Declare this before ViewLoad
var BaseResponse: Array<BaseResponse>?
// After you receive response from API lets say "data"
if let jsonData = data as? String {
self.BaseResponse = Mapper< BaseResponse >().mapArray(JSONString: jsonData)
}

Resources