Unstable error message from the API in Xcode? - ios

while i am trying to decode some json data using Alamofire and PromiseKit, I am encountering some unstable error message.. why I am saying " unstable error message is that sometimes when I hit the API .GET request, i am getting the response in full format, without any error, but after a couple of times running the application Xcode throwing an Alamofire Response Serialization Error which is so confusing.. . I have implemented Coding keys too, data and json response formats are also in right format.. can anyone please help me decode this error message .?
here is the response i got from the API in one of random application run:
and after the next run, here is the error message from the xcode console:
Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error: Swift.DecodingError.typeMismatch(Swift.Array<Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil))))
if any part of the code is required for more analysis, please let me know so that I will update the question contents with those required pieces of code.. .
here is my json response model structs:
// MARK: - TickerByPair
struct TickerByPair {
var price, ask: Double
var askVolume: Double
var bid: Double
var bidVolume, volume: Double
var time: String
}
extension TickerByPair: Decodable{
enum TrackCodingKeys: String, CodingKey {
case price = "price"
case ask = "ask"
case askVolume = "askVolume"
case bid = "bid"
case bidVolume = "bidVolume"
case volume = "volume"
case time = "time"
}
init(from decoder: Decoder) throws {
let trackContainer = try decoder.container(keyedBy: TrackCodingKeys.self)
if trackContainer.contains(.price){
price = try trackContainer.decode(Double.self, forKey: .price)
}else{
price = 0
}
if trackContainer.contains(.ask) {
ask = try trackContainer.decode(Double.self, forKey: .ask)
} else {
ask = 0
}
if trackContainer.contains(.askVolume) {
askVolume = try trackContainer.decode(Double.self, forKey: .askVolume)
} else {
askVolume = 0
}
if trackContainer.contains(.bid) {
bid = try trackContainer.decode(Double.self, forKey: .bid)
} else {
bid = 0
}
if trackContainer.contains(.bidVolume) {
bidVolume = try trackContainer.decode(Double.self, forKey: .bidVolume)
} else {
bidVolume = 0
}
if trackContainer.contains(.volume) {
volume = try trackContainer.decode(Double.self, forKey: .volume)
} else {
volume = 0
}
if trackContainer.contains(.time) {
time = try trackContainer.decode(String.self, forKey: .time)
}
else {
time = ""
}
}
}
// MARK: - TradingPairElement
struct TradingPair {
var id: Int
var name: String
var quoteAsset: String
var baseAsset: String
}
extension TradingPair: Decodable {
enum TrackCodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case quoteAsset = "quoteAsset"
case baseAsset = "baseAsset"
}
init(from decoder: Decoder) throws {
let trackContainer = try decoder.container(keyedBy: TrackCodingKeys.self)
if trackContainer.contains(.id){
id = try trackContainer.decode(Int.self, forKey: .id)
}else{
id = 0
}
if trackContainer.contains(.name) {
name = try trackContainer.decode(String.self, forKey: .name)
} else {
name = ""
}
if trackContainer.contains(.quoteAsset) {
quoteAsset = try trackContainer.decode(String.self, forKey: .quoteAsset)
} else {
quoteAsset = ""
}
if trackContainer.contains(.baseAsset) {
baseAsset = try trackContainer.decode(String.self, forKey: .baseAsset)
} else {
baseAsset = ""
}
}
}
and the API .GET request using Alamofire :
class ServerCommunicator {
static func getAssets() -> Promise<[Assets]> {
let decoder = JSONDecoder()
return Promise { seal in
AF.request(API.assets, method: .get, parameters: .none, headers: .none).responseDecodable(of: [Assets].self, decoder: decoder) { response in
switch response.result {
case .success(let assets):
return seal.fulfill(assets)
case .failure(let error):
return seal.reject(error)
}
}
}
}
static func getPairs() -> Promise<[TradingPair]> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseDecodable(of: [TradingPair].self, decoder: decoder) { response in
switch response.result {
case .success(let pairs):
return seal.fulfill(pairs)
case .failure(let error):
return seal.reject(error)
}
}
}
}
static func getPair(with pairName: String?) -> Promise<TickerByPair> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.getPairByTicker(pairName: pairName!), method: .get, parameters: .none, headers: .none).responseDecodable(of: TickerByPair.self, decoder: decoder) { response in
switch response.result {
case .success(let ticker):
return seal.fulfill(ticker)
case .failure(let error):
return seal.reject(error)
}
}
}
}
}
and my json response format is also as follows:
[
{
"name": "ETH-KRW",
"baseAsset": "ETH",
"quoteAsset": "KRW"
}, {
"name": "BTC-KRW",
"baseAsset": "BTC",
"quoteAsset": "KRW"
}, {
"name": "BCH-KRW",
"baseAsset": "BCH",
"quoteAsset": "KRW"
}
]
-------------
{
"price": 10194500,
"ask": 10195000,
"bid": 10184500,
"volume": 1752.05558316,
"time": "2018-03-14T03:50:41.184Z"
}

If there are cases where your API that supposed to return array, returns dictionary instead, you can try re-implementing the convenient responseDecodable(of:decoder:) function of Alamofire.
Use the responseData(queue:completionHandler:) and try to decode your response data to array and if that fails try again to an object type.
Your getPairs() function for example could be something like this:
static func getPairs() -> Promise<[TradingPair]> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseData { response in
switch response.result {
case .success(let data):
do {
let pairs = try decoder.decode([TradingPair].self, from: data)
return seal.fulfill(pairs)
} catch DecodingError.typeMismatch {
do {
let pair = try decoder.decode(TradingPair.self, from: data)
return seal.fulfill([pair])
} catch {
return seal.reject(error)
}
} catch {
return seal.reject(error)
}
case .failure(let error):
return seal.reject(error)
}
}
}
}
Or more generally, make an extension of DataRequest like this:
extension DataRequest {
#discardableResult func responseArray<T: Decodable>(
of type: T.Type,
decoder: DataDecoder,
completionHandler: #escaping (AFDataResponse<[T]>) -> Void
) -> Self {
responseData { response in
switch response.result {
case .success(let data):
do {
let array = try decoder.decode([T].self, from: data)
let dataResponse = self.dataResponse(from: response, result: .success(array))
completionHandler(dataResponse)
} catch DecodingError.typeMismatch {
do {
let object = try decoder.decode(T.self, from: data)
let dataResponse = self.dataResponse(from: response, result: .success([object]))
completionHandler(dataResponse)
} catch {
let dataResponse: AFDataResponse<[T]> = self.dataResponse(
from: response,
result: .failure(.responseSerializationFailed(reason: .decodingFailed(error: error)))
)
completionHandler(dataResponse)
}
} catch {
let dataResponse: AFDataResponse<[T]> = self.dataResponse(
from: response,
result: .failure(.responseSerializationFailed(reason: .decodingFailed(error: error)))
)
completionHandler(dataResponse)
}
case .failure(let error):
let dataResponse: AFDataResponse<[T]> = self.dataResponse(from: response, result: .failure(error))
completionHandler(dataResponse)
}
}
}
private func dataResponse<T: Decodable>(from response: AFDataResponse<Data>, result: Result<[T], AFError>) -> AFDataResponse<[T]> {
return .init(
request: response.request,
response: response.response,
data: response.data,
metrics: response.metrics,
serializationDuration: response.serializationDuration,
result: result
)
}
}
and then your getPairs() function will be much simpler:
static func getPairs() -> Promise<[TradingPair]> {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .useDefaultKeys
return Promise { seal in
AF.request(API.tradingPairs, method: .get, parameters: .none, headers: .none).responseArray(of: TradingPair.self, decoder: decoder) { response in
switch response.result {
case .success(let pairs):
return seal.fulfill(pairs)
case .failure(let error):
return seal.reject(error)
}
}
}
}

Related

How can access one item of struct in swift

i came from javascript and now i'm studying for swift. i want to print one item of my URLSession return but i dont know how i can do this.
My code:
import Foundation
import Dispatch
enum ServiceError: Error {
case invalidURL
case decodeFail(Error)
case network(Error?)
}
struct Address: Codable {
let zipcode: String
let address: String
let city: String
let uf: String
let complement: String?
enum CodingKeys: String, CodingKey {
case zipcode = "cep"
case address = "logradouro"
case city = "localidade"
case uf
case complement = "complemento"
}
}
class Service {
private let baseURL = "https://viacep.com.br/ws"
func get(cep: String, callback: #escaping (Result<Any, ServiceError>) -> Void) {
let path = "/\(cep)/json"
guard let url = URL(string: baseURL + path) else {
callback(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
callback(.failure(.network(error)))
return
}
guard let json: Address = try? JSONDecoder().decode(Address.self, from: data) else {
return
}
callback(.success(json))
}
task.resume()
}
}
do {
let service = Service()
service.get(cep: "1231234") { result in
DispatchQueue.main.async {
switch result {
case let .failure(error):
print(error)
case let .success(data):
print(data)
}
}
}
}
This is my return:
Address(zipcode: "12341231", address: "Teste", city: "Teste", uf: "TE", complement: Optional(""))
I want want my return is like:
print(data.zipcode)
Output: 12341231
Unlike javascript, Swift is strongly typed, so return the Address type
from your func get(cep:...), not Any.
Note, there are many types of errors do deal with when doing a server request.
An error could mean that the response could not be decoded,
due to a bad request, for example, as in the case with cep: "1231234". If you use cep: "01001000", you don't get an error, and all works well.
So I suggest update your code (especially the ServiceError) to cater for the various errors you may get.
Here is the code I used to test my answer:
let service = Service()
// try also "01001000" for no errors
service.get(cep: "1231234") { result in
switch result {
case let .failure(error):
print("\n---> error: \(error)")
case let .success(data):
print("\n---> data: \(data)")
print("---> zipcode: \(data.zipcode)")
}
}
and
class Service {
private let baseURL = "https://viacep.com.br/ws"
// here return Address, not Any
func get(cep: String, callback: #escaping (Result<Address, ServiceError>) -> Void) {
let path = "/\(cep)/json"
guard let url = URL(string: baseURL + path) else {
callback(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
callback(.failure(.network(error)))
return
}
// this shows you what the server is really sending you
print("\n---> data: \(String(data: data, encoding: .utf8)) \n")
guard let httpResponse = response as? HTTPURLResponse else {
callback(.failure(.apiError(reason: "Unknown")))
return
}
switch httpResponse.statusCode {
case 400: return callback(.failure(.apiError(reason: "Bad Request")))
case 401: return callback(.failure(.apiError(reason: "Unauthorized")))
case 403: return callback(.failure(.apiError(reason: "Resource forbidden")))
case 404: return callback(.failure(.apiError(reason: "Resource not found")))
case 405..<500: return callback(.failure(.apiError(reason: "client error")))
case 500..<600: return callback(.failure(.apiError(reason: "server error")))
default:
callback(.failure(.apiError(reason: "Unknown")))
}
// here use a proper do/catch
do {
let address = try JSONDecoder().decode(Address.self, from: data)
callback(.success(address))
} catch {
// could not be decoded as an Address.
callback(.failure(.decodeFail(error)))
}
}
task.resume()
}
}
enum ServiceError: Error {
case invalidURL
case decodeFail(Error)
case network(Error?)
case apiError(reason: String) // <-- here
}

JSON Decoding Not Populating Table View

I am trying to parse data from the website movieDatabase.com However there's some issue decoding the data to json and populating my table view.I am not sure why this is happening. Please I need help spotting out the problem. Here's my code. https://github.com/lexypaul13/Movie-Browser/tree/main/Movie-Browser
struct Movies: Codable {
let overview:String?
let original_title: String?
let poster_path:String
}
struct ApiResponse:Codable, Hashable {
let page:Int
let shows:[Movies]
enum CodingKeys:String, CodingKey {
case page = "page"
case shows = "results"
}
}
class NetworkManger{
enum EndPoint{
case showList
}
static let shared = NetworkManger()
private let baseURL : String
private var apiKeyPathCompononent :String
private init(){
self.baseURL = "https://api.themoviedb.org/3/movie/now_playing?"
self.apiKeyPathCompononent = "api_key=a07e22bc18f5cb106bfe4cc1f83ad8ed"
}
private var jsonDecoder:JSONDecoder = {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return decoder
}()
func get<T:Decodable>(_ endPoints: EndPoint, urlString: String, completed:#escaping(Result<T?,ErroMessage>)->Void){
guard let url = urlBuilder(endPoint: endPoints) else {
completed(.failure(.invalidURL))
return
}
let task = URLSession.shared.dataTask(with: url){ data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode==200 else {
print(ErroMessage.invalidResponse.rawValue)
completed(.failure(.invalidResponse))
return
}
guard let data = data else{
completed(.failure(.invalidData))
return
}
do{
let apiResponse = try self.jsonDecoder.decode([T].self, from: data)
DispatchQueue.main.async {
completed(.success(apiResponse as? T))
}
} catch{
print(ErroMessage.invalidData.rawValue)
}
}
task.resume()
}
private func urlBuilder(endPoint:EndPoint )->URL?{
switch endPoint {
case .showList:
return URL(string: baseURL + apiKeyPathCompononent )
}
}
func getMovies(){
NetworkManger.shared.get(.showList, urlString: "") { [weak self] (result: Result<[Movies]?,ErroMessage> ) in
guard let self = self else { return }
switch result{
case .success(let movies):
self.movies = movies ?? []
DispatchQueue.main.async {self.tableView.reloadData()}
case .failure(let error):
print(error.localizedDescription)
}
}
}
}
The root object returned from the api is your ApiResult struct. This contains an array of movies (which you have mapped to the shows property of the ApiResult)
You need to change the getMovies function so that the right generic type can be inferred and the json decoder can do the right thing
func getMovies(){
NetworkManger.shared.get(.showList, urlString: "") { [weak self] (result: Result<ApiResult,ErroMessage> ) in
guard let self = self else { return }
switch result{
case .success(let apiResult):
self.movies = apiResult.shows
DispatchQueue.main.async {self.tableView.reloadData()}
case .failure(let error):
print(error.localizedDescription)
}
}
}

Error: 'Result<Data, FourSquareError>' cannot be constructed because it has no accessible initializers

I'm trying to call the API and when I pass the value to the result variable it gives me an error. I also get this error on the func mapError function below.
Any idea on why am I getting this error?
Thanks in advance
'Result[Data, FourSquareError]' cannot be constructed because it has no accessible initializers
'Result[Data, E]' cannot be constructed because it has no accessible initializers
The errors are on these lines:
let result = Result<Data, FourSquareError>(value: data, error:
translatedError)
return Result<Value, E>(value)
return Result<Value, E>(try transform (error))
Here's the full code:
typealias JSON = [String: Any]
enum FourSquareError: Error {
case couldNotCreateURL
case networkError(Error)
case serverError(errorType: String, errorDetail: String)
case couldNotParseData
}
func createURL(endpoint: String, parameters: [String: String]) -> URL? {
let baseURL = "https://api.foursquare.com/v2/"
// We convert the parameters dictionary in an array of URLQueryItems
var queryItems = parameters.map { pair -> URLQueryItem in
return URLQueryItem(name: pair.key, value: pair.value)
}
// Add default parameters to query
queryItems.append(URLQueryItem(name: "v", value: apiVersion))
queryItems.append(URLQueryItem(name: "client_id", value: clientId))
queryItems.append(URLQueryItem(name: "client_secret", value: clientSecret))
var components = URLComponents(string: baseURL + endpoint)
components?.queryItems = queryItems
return components?.url
}
func getVenues(latitude: Double, longitude: Double, completion: #escaping
(Result<[JSON], FourSquareError>) -> Void) {
let parameters = [
"ll": "\(latitude),\(longitude)",
"intent": "browse",
"radius": "250"
]
guard let url = createURL(endpoint: "venues/search", parameters:
parameters)
else {
completion(Result.failure(.couldNotCreateURL))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response,
error in
let translatedError = error.map { FourSquareError.networkError($0) }
let result = Result<Data, FourSquareError>(value: data, error:
translatedError)
// Parsing Data to JSON
.flatMap { data in
do {
return Result.success(try parseData(data))
} catch {
return Result.failure(.unexpectedError(error))
}
}
// Check for server errors
.flatMap { (json: JSON) -> Result<JSON, FourSquareError> in
do {
return Result.success(try validateResponse(json: json))
} catch {
return Result.failure(.unexpectedError(error))
}
}
// Extract venues
.flatMap { (json: JSON) -> Result<[JSON], FourSquareError> in
do {
return Result.success(try extractVenues(json: json))
} catch {
return Result.failure(.unexpectedError(error))
}
}
completion(result)
}
task.resume()
}
extension Result {
public func mapError<E: Error>(_ transform: (ErrorType) throws -> E) rethrows -> Result<Value, E> {
switch self {
case .success(let value):
return Result<Value, E>(value)
case .failure(let error):
return Result<Value, E>(try transform (error))
}
}
}
Result is an enum so you can construct it. You can only instantiate a Result instance through one of it's two cases: failure or success. For example:
let result: Result<Data, FourSquareError> = .success(Data())
or
let result: Result<Data, FourSquareError> = .failure(FourSquareError.whatever)

Swift error type server response for wrong input from rest API

I hope all you well. I have a question. I have a simple login page with email and password and also I have a user object like that
// MARK: - UserModel
struct UserModel: Codable {
let error: Bool
let desc: String
let user: User
let token: String
}
// MARK: - User
struct User: Codable {
let id: Int
let email, firstName, lastName, lang: String
let status: Int
let referer, star: String?
let phone: String?
let ip: String?
let birth, idNumber: String?
let regionID: String?
let createdAt, updatedAt: String
enum CodingKeys: String, CodingKey {
case id, email
case firstName = "first_name"
case lastName = "last_name"
case lang, status, referer, star, phone, ip, birth
case idNumber = "id_number"
case regionID = "region_id"
case createdAt, updatedAt
}
}
the return type is the upper one(UserModel). If the user entered his/her credentials true there is no problem. But troubles starts if he/she entered the wrong credentials. I can not parse the return value from the server. Always give me error that line.
And the console output is:
Rentover[2343:150674] Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Bool, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "error", intValue: nil)], debugDescription: "Expected to decode Bool but found a dictionary instead.", underlyingError: nil)): file
Here is my login request function. I used codable for simplicity.
class func requestLogIn(router: Router, completion: #escaping (Result<UserModel, Error>) -> ()) {
guard let url = setUrlComponents(router: router).url else { return }
var urlRequest = URLRequest(url: url)
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.httpMethod = router.method
if router.method == "POST"{
let model = LoginModel(email: router.parameters[0], password: router.parameters[1])
urlRequest.httpBody = try? JSONEncoder().encode(model)
}
let dataTask = URLSession.shared.dataTask(with: urlRequest) { data, response, error in
guard error == nil else {
print(error?.localizedDescription)
return
}
guard response != nil else {
print("no response")
return
}
guard let data = data else {
print("no data")
return
}
let responseObject = try! JSONDecoder().decode(UserModel.self, from: data)
print(responseObject.user)
DispatchQueue.main.async {
completion(.success(responseObject))
}
}
dataTask.resume()
}
And here is my error struct.
struct LogInError: Codable, Error{
let error: Bool
let desc: String
let fields: [String] ----> 'Edit here old: let fileds: [String'
}
And last my real call function is like that
NetworkService.requestLogIn(router: Router.login(email: nameTextField.text!, passowrd: passwordTextField.text!)) { (result) in
switch result {
case .success(let userModel):
print("RESULT SUCCESS")
print("Hello \(userModel.user.firstName)")
let selectedVC = UIUtils.checkUserStatus(status: userModel.user.status)
self.navigationController?.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(selectedVC, animated: true)
case .failure(let error):
print("RESULT FAILED")
print(error)
}
}
I followed that medium link for creating my router and network service. I am very glad and thankful if you help me with that issue. Or give me some advice about networking api's and usage.
[Edit For error response from server]
My request and response message-body frame is also like that:
Have a nice day. And good codding.
To decode two different JSON strings a convenient solution is an enum with associated types because it can represent the success and failure cases very descriptively.
First it decodes the common error key and then it decodes UserModel or LogInError
enum Response : Decodable {
case success(UserModel), failure(LogInError)
private enum CodingKeys : String, CodingKey { case error }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let hasError = try container.decode(Bool.self, forKey: .error)
if hasError {
let errorContainer = try decoder.singleValueContainer()
let errorData = try errorContainer.decode(LogInError.self)
self = .failure(errorData)
} else {
let successContainer = try decoder.singleValueContainer()
let successData = try successContainer.decode(UserModel.self)
self = .success(successData)
}
}
}
Use it
class func requestLogIn(router: Router, completion: #escaping (Result<Response, Error>) -> ()) {
...
do {
let responseObject = try JSONDecoder().decode(Response.self, from: data)
print(responseObject)
DispatchQueue.main.async {
completion(.success(responseObject))
}
} catch {
DispatchQueue.main.async {
completion(.failure(error))
}
}
and
NetworkService.requestLogIn(router: Router.login(email: nameTextField.text!, passowrd: passwordTextField.text!)) { (response) in
switch response {
case .success(let result):
switch result {
case .success(let userModel):
print("RESULT SUCCESS")
print("Hello \(userModel.user.firstName)")
let selectedVC = UIUtils.checkUserStatus(status: userModel.user.status)
self.navigationController?.modalPresentationStyle = .fullScreen
self.navigationController?.pushViewController(selectedVC, animated: true)
case .failure(let errorData):
print(errorData)
}
case .failure(let error):
print("RESULT FAILED")
print(error)
}
}
Declare LoginError as a standard Decodable struct
struct LogInError: Decodable {

How to parse JSON response from Alamofire API in Swift?

Following code I have written and I am getting response in JSON also but the type of JSON is "AnyObject" and I am not able to convert that into Array so that I can use that.
Alamofire.request(.POST, "MY URL", parameters:parameters, encoding: .JSON) .responseJSON
{
(request, response, JSON, error) in
println(JSON?)
}
The answer for Swift 2.0 Alamofire 3.0 should actually look more like this:
Alamofire.request(.POST, url, parameters: parameters, encoding:.JSON).responseJSON
{ response in switch response.result {
case .Success(let JSON):
print("Success with JSON: \(JSON)")
let response = JSON as! NSDictionary
//example if there is an id
let userId = response.objectForKey("id")!
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%203.0%20Migration%20Guide.md
UPDATE for Alamofire 4.0 and Swift 3.0 :
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
print(response)
//to get status code
if let status = response.response?.statusCode {
switch(status){
case 201:
print("example success")
default:
print("error with response status: \(status)")
}
}
//to get JSON return value
if let result = response.result.value {
let JSON = result as! NSDictionary
print(JSON)
}
}
like above mention you can use SwiftyJSON library and get your values like i have done below
Alamofire.request(.POST, "MY URL", parameters:parameters, encoding: .JSON) .responseJSON
{
(request, response, data, error) in
var json = JSON(data: data!)
println(json)
println(json["productList"][1])
}
my json product list return from script
{ "productList" :[
{"productName" : "PIZZA","id" : "1","productRate" : "120.00","productDescription" : "PIZZA AT 120Rs","productImage" : "uploads\/pizza.jpeg"},
{"productName" : "BURGER","id" : "2","productRate" : "100.00","productDescription" : "BURGER AT Rs 100","productImage" : "uploads/Burgers.jpg"}
]
}
output :
{
"productName" : "BURGER",
"id" : "2",
"productRate" : "100.00",
"productDescription" : "BURGER AT Rs 100",
"productImage" : "uploads/Burgers.jpg"
}
Swift 3, Alamofire 4.4, and SwiftyJSON:
Alamofire.request(url, method: .get)
.responseJSON { response in
if response.data != nil {
let json = JSON(data: response.data!)
let name = json["people"][0]["name"].string
if name != nil {
print(name!)
}
}
}
That will parse this JSON input:
{
people: [
{ name: 'John' },
{ name: 'Dave' }
]
}
I found the answer on GitHub for Swift2
https://github.com/Alamofire/Alamofire/issues/641
Alamofire.request(.GET, URLString, parameters: ["foo": "bar"])
.responseJSON { request, response, result in
switch result {
case .Success(let JSON):
print("Success with JSON: \(JSON)")
case .Failure(let data, let error):
print("Request failed with error: \(error)")
if let data = data {
print("Response data: \(NSString(data: data, encoding: NSUTF8StringEncoding)!)")
}
}
}
I'm neither a JSON expert nor a Swift expert, but the following is working for me. :) I have extracted the code from my current app, and only changed "MyLog to println", and indented with spaces to get it to show as a code block (hopefully I didn't break it).
func getServerCourseVersion(){
Alamofire.request(.GET,"\(PUBLIC_URL)/vtcver.php")
.responseJSON { (_,_, JSON, _) in
if let jsonResult = JSON as? Array<Dictionary<String,String>> {
let courseName = jsonResult[0]["courseName"]
let courseVersion = jsonResult[0]["courseVersion"]
let courseZipFile = jsonResult[0]["courseZipFile"]
println("JSON: courseName: \(courseName)")
println("JSON: courseVersion: \(courseVersion)")
println("JSON: courseZipFile: \(courseZipFile)")
}
}
}
Hope this helps.
Edit:
For reference, here is what my PHP Script returns:
[{"courseName": "Training Title","courseVersion": "1.01","courseZipFile": "101/files.zip"}]
Swift 5
class User: Decodable {
var name: String
var email: String
var token: String
enum CodingKeys: String, CodingKey {
case name
case email
case token
}
public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.email = try container.decode(String.self, forKey: .email)
self.token = try container.decode(String.self, forKey: .token)
}
}
Alamofire API
Alamofire.request("url.endpoint/path", method: .get, parameters: params, encoding: URLEncoding.queryString, headers: nil)
.validate()
.responseJSON { response in
switch (response.result) {
case .success( _):
do {
let users = try JSONDecoder().decode([User].self, from: response.data!)
print(users)
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
case .failure(let error):
print("Request error: \(error.localizedDescription)")
}
swift 3
pod 'Alamofire', '~> 4.4'
pod 'SwiftyJSON'
File json format:
{
"codeAd": {
"dateExpire": "2017/12/11",
"codeRemoveAd":"1231243134"
}
}
import Alamofire
import SwiftyJSON
private func downloadJson() {
Alamofire.request("https://yourlinkdownloadjson/abc").responseJSON { response in
debugPrint(response)
if let json = response.data {
let data = JSON(data: json)
print("data\(data["codeAd"]["dateExpire"])")
print("data\(data["codeAd"]["codeRemoveAd"])")
}
}
}
This was build with Xcode 10.1 and Swift 4
Perfect combination "Alamofire"(4.8.1) and "SwiftyJSON"(4.2.0). First you should install both pods
pod 'Alamofire' and pod 'SwiftyJSON'
The server response in JSON format:
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip;q=1.0, compress;q=0.5",
"Accept-Language": "en;q=1.0",
"Host": "httpbin.org",
"User-Agent": "AlamoFire TEST/1.0 (com.ighost.AlamoFire-TEST; build:1; iOS 12.1.0) Alamofire/4.8.1"
},
"origin": "200.55.140.181, 200.55.140.181",
"url": "https://httpbin.org/get"
}
In this case I want print the "Host" info : "Host": "httpbin.org"
Alamofire.request("https://httpbin.org/get").validate().responseJSON { response in
switch response.result {
case .success:
print("Validation Successful)")
if let json = response.data {
do{
let data = try JSON(data: json)
let str = data["headers"]["Host"]
print("DATA PARSED: \(str)")
}
catch{
print("JSON Error")
}
}
case .failure(let error):
print(error)
}
}
Keep Calm and happy Code😎
I found a way to convert the response.result.value (inside an Alamofire responseJSON closure) into JSON format that I use in my app.
I'm using Alamofire 3 and Swift 2.2.
Here's the code I used:
Alamofire.request(.POST, requestString,
parameters: parameters,
encoding: .JSON,
headers: headers).validate(statusCode: 200..<303)
.validate(contentType: ["application/json"])
.responseJSON { (response) in
NSLog("response = \(response)")
switch response.result {
case .Success:
guard let resultValue = response.result.value else {
NSLog("Result value in response is nil")
completionHandler(response: nil)
return
}
let responseJSON = JSON(resultValue)
// I do any processing this function needs to do with the JSON here
// Here I call a completionHandler I wrote for the success case
break
case .Failure(let error):
NSLog("Error result: \(error)")
// Here I call a completionHandler I wrote for the failure case
return
}
I usually use Gloss library to serialize or deserialize JSON in iOS. For example, I have JSON that looks like this:
{"ABDC":[{"AB":"qwerty","CD":"uiop"}],[{"AB":"12334","CD":"asdf"}]}
First, I model the JSON array in Gloss struct:
Struct Struct_Name: Decodable {
let IJ: String?
let KL: String?
init?(json: JSON){
self.IJ = "AB" <~~ json
self.KL = "CD" <~~ json
}
}
And then in Alamofire responseJSON, I do this following thing:
Alamofire.request(url, method: .get, paramters: parametersURL).validate(contentType: ["application/json"]).responseJSON{ response in
switch response.result{
case .success (let data):
guard let value = data as? JSON,
let eventsArrayJSON = value["ABDC"] as? [JSON]
else { fatalError() }
let struct_name = [Struct_Name].from(jsonArray: eventsArrayJSON)//the JSON deserialization is done here, after this line you can do anything with your JSON
for i in 0 ..< Int((struct_name?.count)!) {
print((struct_name?[i].IJ!)!)
print((struct_name?[i].KL!)!)
}
break
case .failure(let error):
print("Error: \(error)")
break
}
}
The output from the code above:
qwerty
uiop
1234
asdf
in swift 5 we do like, Use typealias for the completion. Typlealias nothing just use to clean the code.
typealias response = (Bool,Any?)->()
static func postCall(_ url : String, param : [String : Any],completion : #escaping response){
Alamofire.request(url, method: .post, parameters: param, encoding: JSONEncoding.default, headers: [:]).responseJSON { (response) in
switch response.result {
case .success(let JSON):
print("\n\n Success value and JSON: \(JSON)")
case .failure(let error):
print("\n\n Request failed with error: \(error)")
}
}
}
The easy answer is to let AlamoFire do the decoding directly.
Surprisingly, you don't use the .responseJSON because that returns an untyped json object
Instead, you make your objects Decodable - and ask AF to decode directly to them
My json response contains an array of Account objects. I only care about the id and name keys (though there are many more)
struct Account:Codable {
let id:Int
let name:String
}
then simply
AF.request(url,
method: .get)
.responseDecodable(of:[Account].self) { response in
switch response.result {
case .success:
switch response.response?.statusCode {
case 200:
//response.value is of type [Account]
default:
//handle other cases
}
case let .failure(error):
//probably the decoding failed because your json doesn't match the expected format
}
}
let semaphore = DispatchSemaphore (value: 0)
var request = URLRequest(url: URL(string: Constant.localBaseurl2 + "compID")!,timeoutInterval: Double.infinity)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let response = response {
let nsHTTPResponse = response as! HTTPURLResponse
print(nsHTTPResponse)
}
if let error = error {
print ("\(error)")
return
}
if let data = data {
DispatchQueue.main.async {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase//or any other Decoder\
do{
let jsonDecoder = JSONDecoder()
let memberRecord = try jsonDecoder.decode(COMPLAINTSVC.GetComplaints.self, from: data)
print(memberRecord.message)
for detailData in memberRecord.message{
print(detailData)
}
}catch{
print(error.localizedDescription)
}
}
}
semaphore.signal()
}
task.resume()
semaphore.wait()
}
pod 'Alamofire'
pod 'SwiftyJSON'
pod 'ReachabilitySwift'
import UIKit
import Alamofire
import SwiftyJSON
import SystemConfiguration
class WebServiceHelper: NSObject {
typealias SuccessHandler = (JSON) -> Void
typealias FailureHandler = (Error) -> Void
// MARK: - Internet Connectivity
class func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
zeroAddress.sin_family = sa_family_t(AF_INET)
guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
SCNetworkReachabilityCreateWithAddress(nil, $0)
}
}) else {
return false
}
var flags: SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
return false
}
let isReachable = flags.contains(.reachable)
let needsConnection = flags.contains(.connectionRequired)
return (isReachable && !needsConnection)
}
// MARK: - Helper Methods
class func getWebServiceCall(_ strURL : String, isShowLoader : Bool, success : #escaping SuccessHandler, failure : #escaping FailureHandler)
{
if isConnectedToNetwork() {
print(strURL)
if isShowLoader == true {
AppDelegate.getDelegate().showLoader()
}
Alamofire.request(strURL).responseJSON { (resObj) -> Void in
print(resObj)
if resObj.result.isSuccess {
let resJson = JSON(resObj.result.value!)
if isShowLoader == true {
AppDelegate.getDelegate().dismissLoader()
}
debugPrint(resJson)
success(resJson)
}
if resObj.result.isFailure {
let error : Error = resObj.result.error!
if isShowLoader == true {
AppDelegate.getDelegate().dismissLoader()
}
debugPrint(error)
failure(error)
}
}
}else {
CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
}
}
class func getWebServiceCall(_ strURL : String, params : [String : AnyObject]?, isShowLoader : Bool, success : #escaping SuccessHandler, failure :#escaping FailureHandler){
if isConnectedToNetwork() {
if isShowLoader == true {
AppDelegate.getDelegate().showLoader()
}
Alamofire.request(strURL, method: .get, parameters: params, encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: {(resObj) -> Void in
print(resObj)
if resObj.result.isSuccess {
let resJson = JSON(resObj.result.value!)
if isShowLoader == true {
AppDelegate.getDelegate().dismissLoader()
}
success(resJson)
}
if resObj.result.isFailure {
let error : Error = resObj.result.error!
if isShowLoader == true {
AppDelegate.getDelegate().dismissLoader()
}
failure(error)
}
})
}
else {
CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
}
}
class func postWebServiceCall(_ strURL : String, params : [String : AnyObject]?, isShowLoader : Bool, success : #escaping SuccessHandler, failure :#escaping FailureHandler)
{
if isConnectedToNetwork()
{
if isShowLoader == true
{
AppDelegate.getDelegate().showLoader()
}
Alamofire.request(strURL, method: .post, parameters: params, encoding: JSONEncoding.default, headers: nil).responseJSON(completionHandler: {(resObj) -> Void in
print(resObj)
if resObj.result.isSuccess
{
let resJson = JSON(resObj.result.value!)
if isShowLoader == true
{
AppDelegate.getDelegate().dismissLoader()
}
success(resJson)
}
if resObj.result.isFailure
{
let error : Error = resObj.result.error!
if isShowLoader == true
{
AppDelegate.getDelegate().dismissLoader()
}
failure(error)
}
})
}else {
CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
}
}
class func postWebServiceCallWithImage(_ strURL : String, image : UIImage!, strImageParam : String, params : [String : AnyObject]?, isShowLoader : Bool, success : #escaping SuccessHandler, failure : #escaping FailureHandler)
{
if isConnectedToNetwork() {
if isShowLoader == true
{
AppDelegate.getDelegate().showLoader()
}
Alamofire.upload(
multipartFormData: { multipartFormData in
if let imageData = UIImageJPEGRepresentation(image, 0.5) {
multipartFormData.append(imageData, withName: "Image.jpg")
}
for (key, value) in params! {
let data = value as! String
multipartFormData.append(data.data(using: String.Encoding.utf8)!, withName: key)
print(multipartFormData)
}
},
to: strURL,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint(response)
//let datastring = String(data: response, encoding: String.Encoding.utf8)
// print(datastring)
}
case .failure(let encodingError):
print(encodingError)
if isShowLoader == true
{
AppDelegate.getDelegate().dismissLoader()
}
let error : NSError = encodingError as NSError
failure(error)
}
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { (response) -> Void in
if response.result.isSuccess
{
let resJson = JSON(response.result.value!)
if isShowLoader == true
{
AppDelegate.getDelegate().dismissLoader()
}
success(resJson)
}
if response.result.isFailure
{
let error : Error = response.result.error! as Error
if isShowLoader == true
{
AppDelegate.getDelegate().dismissLoader()
}
failure(error)
}
}
case .failure(let encodingError):
if isShowLoader == true
{
AppDelegate.getDelegate().dismissLoader()
}
let error : NSError = encodingError as NSError
failure(error)
}
}
)
}
else
{
CommonMethods.showAlertWithError("", strMessage: Messages.NO_NETWORK, withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
}
}
}
==================================
Call Method
let aParams : [String : String] = [
"ReqCode" : Constants.kRequestCodeLogin,
]
WebServiceHelper.postWebServiceCall(Constants.BaseURL, params: aParams as [String : AnyObject]?, isShowLoader: true, success: { (responceObj) in
if "\(responceObj["RespCode"])" != "1"
{
let alert = UIAlertController(title: Constants.kAppName, message: "\(responceObj["RespMsg"])", preferredStyle: UIAlertControllerStyle.alert)
let OKAction = UIAlertAction(title: "OK", style: .default) { (action:UIAlertAction!) in
}
alert.addAction(OKAction)
self.present(alert, animated: true, completion: nil)
}
else
{
let aParams : [String : String] = [
"Password" : self.dictAddLogin[AddLoginConstants.kPassword]!,
]
CommonMethods.saveCustomObject(aParams as AnyObject?, key: Constants.kLoginData)
}
}, failure:
{ (error) in
CommonMethods.showAlertWithError(Constants.kALERT_TITLE_Error, strMessage: error.localizedDescription,withTarget: (AppDelegate.getDelegate().window!.rootViewController)!)
})
}

Resources