Alamofire and SwiftyJSon get value outside request function - json

Hey I am new here and I am trying to get value from request outside the request function in VC, but I cant do that I get errors I tried few ways, but I keep getting different errors, now I get Type Any has no subscript members, could you help me how to get string from request and find an array and take a value from it.
I need to get value from Json strin in VC so I am trying this way:
let retur = Json()
retur.login(userName: userName.text!, password: password.text!) { (JSON) in
print(JSON)
let json = JSON
let name = json["ubus_rpc_session"].stringValue
print(name)
Response:
{"jsonrpc":"2.0","id":1,"result":[0,{"ubus_rpc_session":"70ea230f29057f54459814459b5a316e","timeout":300,"expires":300,"acls":{"access-group":{"superuser":["read","write"],"unauthenticated":["read"]},"ubus":{"":[""],"session":["access","login"]},"uci":{"*":["read","write"]}},"data":{"username":"root"}}]}
My request:
private func makeWebServiceCall (urlAddress: String, requestMethod: HTTPMethod, params:[String:Any], completion: #escaping (_ JSON : Any) -> ()) {
Alamofire.request(urlAddress, method: requestMethod, parameters: params, encoding: JSONEncoding.default).responseString { response in
switch response.result {
case .success:
if let jsonData = response.result.value {
completion(jsonData)
}
case .failure( _):
if let data = response.data {
let json = String(data: data, encoding: String.Encoding.utf8)
completion("Failure Response: \(json)")
}
Function which call request method:
public func login(userName: String, password: String, loginCompletion: #escaping (Any) -> ()) {
let loginrequest = JsonRequests.loginRequest(userName: userName, password: password)
makeWebServiceCall(urlAddress: URL, requestMethod: .post, params: loginrequest, completion: { (JSON : Any) in
loginCompletion(JSON)
})
Updated:

You can't subscript with Any and after you converting JSON to [String:Any] if you are trying .stringValue with subscripting Dictionary then Dictionary doesn't have any property stringValue you are mixing two things here SwiftyJSON and Swift native type. I will access your JSON response this way.
First get clear about how you get value of ubus_rpc_session from your JSON response. You can't directly get value of ubus_rpc_session from your JSON response because it is inside the 2nd object in your result array,so to get the ubus_rpc_session try like this way.
retur.login(userName: userName.text!, password: password.text!) { (json) in
print(json)
if let dic = json as? [String:Any], let result = dic["result"] as? [Any],
let subDic = result.last as? [String:Any],
let session = subDic["ubus_rpc_session"] as? String {
print(session)
}
}
If you want to work with SwiftyJSON then you can get value of ubus_rpc_session this way.
retur.login(userName: userName.text!, password: password.text!) { (json) in
print(json)
let jsonDic = JSON(json)
print(jsonDic["result"][1]["ubus_rpc_session"].stringValue)
}

Related

Swift - JSON decoding

I have a JSON response from an api call. The problem is I get different JSON responses depending on whether the user has entered the correct credentials or not. My question is how do I read and decode these responses to a useable struct and what is the best way to go about decoding these different responses. one thing I noticed is both response have a common "isSuccess" that may be useful. I have little to no experience with swift or reading JSON so this is all a learning experience for me.
This is the response for successful login
{"result":{"login":{"isAuthorized":true,"isEmpty":false,"userName":{"isEmpty":false,"name":{"firstName":"Jason","lastName":"Test","displayName":"Test, Jason","isEmpty":false,"fullName":"Jason Test"},"canDelete":false,"id":5793,"canModify":false},"username":"test#testable.com"},"parameters":{"isEmpty":false,"keep_logged_in_indicator":false,"username":"test#testable.com"}},"isAuthorized":true,"version":{"major":"2021","minor":"004","fix":"04","display":"2021.004.04","isEmpty":false},"isSystemDown":false,"timestamp":"2021-07-28T02:47:33Z","isSuccess":true}
This is the response for failure
{"isAuthorized":true,"version":{"major":"2021","minor":"004","fix":"04","display":"2021.004.04","isEmpty":false},"isSystemDown":false,"errors":[{"password":"Unable to login as 'test#testable.com'"}],"timestamp":"2021-07-28T02:47:05Z","isSuccess":false}
This is the code I have written for my api calls
func request<T: Decodable>(endPoint: EndPoint, method: Method, parameters: [String: Any]? = nil, completion: #escaping(Result<T, Error>) -> Void) {
// Creates a urlRequest
guard let request = createRequest(endPoint: endPoint, method: method, parameters: parameters) else {
completion(.failure(AppError.invalidUrl))
return
}
let session = URLSession.shared
session.dataTask(with: request) { data, response, error in
var results: Result<Data, Error>?
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
completion(.failure(AppError.badStatusCode))
return
}
if let response = response {
// Gets the JSESSIONID
let cookieName = "JSESSIONID"
if let cookie = HTTPCookieStorage.shared.cookies?.first(where: { $0.name == cookieName }) {
debugPrint("\(cookieName): \(cookie.value)")
}
print(response)
}
if let data = data {
results = .success(data)
// Converts data to readable String
let responseString = String(data: data, encoding: .utf8) ?? "unable to convert to readable String"
print("Server Response: \(responseString.description)")
} else if let error = error {
results = .failure(error)
print("Server Error: \(error.localizedDescription)")
}
DispatchQueue.main.async {
self.handleResponse(result: results, completion: completion)
}
}.resume()
}
private func handleResponse<T: Decodable>(result: Result<Data, Error>?, completion: (Result<T, Error>) -> Void) {
guard let result = result else {
completion(.failure(AppError.unknownError))
return
}
switch result {
case .success(let data):
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print("Server JsonObject response: \(json)")
} catch {
completion(.failure(AppError.errorDecoding))
}
let decoder = JSONDecoder()
// Decodes that json data
do {
} catch {
}
case .failure(let error):
completion(.failure(error))
}
}
Im mostly interesting in being able to display the json error that occurs when credentials are incorrect. The deadline for my project Is slowing approaching and any help or suggestions would be much appreciated.
You can use Swift's Result type to differentiate a successful result from a failed result.
The Result type is not decodable by default so you will need to write a custom decoder like this:
struct Response: Decodable {
let result: Swift.Result<Result, Errors>
enum CodingKeys: String, CodingKey {
case isSuccess
case errors
case result
}
struct Result: Codable {
let login: Login
struct Login: Codable {
let isAuthorized: Bool
}
}
struct Errors: Error {
let contents: [[String: String]]
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if try container.decode(Bool.self, forKey: .isSuccess) {
result = .success(try container.decode(Result.self, forKey: .result))
} else {
result = .failure(
Errors(contents: try container.decode([[String: String]].self, forKey: .errors))
)
}
}
}

How to add parameters to JSON in function in swift

I have created one function for JSON parsing, which I am calling in every view controller, but i am unable to pass parameters from that function
i have created function in NSObject class:
func serviceCall(_ url: String, _ params:[String : Any], completion: #escaping (Data?, Error?) -> Void) {
let url = URL(string: url)!
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST" //set http method as POST
do {
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: params, options: .prettyPrinted) // pass dictionary to nsdata object and set it as request body
} catch let error {
print(error.localizedDescription)
}
URLSession.shared.dataTask(with: urlRequest) { data, response, error in
if let error = error {
completion(nil, error)
return
}
guard let data = data else {
preconditionFailure("No error was received but we also don't have data...")
}
completion(data, nil)
}.resume()
}
}
in registrationVC how to add parameters to this function
my parameters for registration service:
struct RegData: Codable {
var jsonrpc: String
var params: PostReg
}
struct PostReg: Codable{
var email: String
var password: String
var device_id: String
}
while calling serviceCall function how to add parameters to it
if i call like this in button action
let url = "https://e/api/reg"
let jsonpostParameters: [String: Any] = RegData(jsonrpc: "2.0", params: (PostLogin(email: nameTf.text!, password: passwordTf.text!, device_id: "2")))
self.fetch(url, jsonpostParameters) { (data: Data?, error: Error?) in
guard let dt = data else { return }
// convert data to JSON
print(dt)
error:
cannot convert a value [String:Any] to RegData
how to add RegData to serviceCall, shall i change serviceCall params type? if yes how..
how add RegData to serviceCall to parse JSON
Kindly try this for decode data
//Here ResponceData is your codable class
let dictData = try JSONSerialization.data(withJSONObject: data, options: .prettyPrinted)
let obj= try JSONDecoder().decode([ResponseData].self, from: dictData)

Swift ObjectMapper: How to parse JSON with backslash

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.

How do I use JSON element with alamofire

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)

How should I go about parsing a JSON response from an API with Alamofire and SwiftyJSON?

I am trying to parse a JSON from an API using Alamofire and SwiftyJSON, but am having trouble trying to access the information in the JSON. I need to simply parse the JSON for an item called "ask_price": and also "time_coinapi" but I am not sure how I manage the response, or if I have to use a different method. here is what I have at the moment:
class CoinAPIManager {
var prices: [String] = []
var times: [String] = []
static let shared = CoinAPIManager()
func getReq() {
let headers: HTTPHeaders = [
"X-CoinAPI-Key": "Key"
]
Alamofire.request("https://rest.coinapi.io/v1/quotes/BITSTAMP_SPOT_BTC_USD/history?time_start=2018-08-21T00:00:00&time_end=2018-08-22T00:00:00&limit=100", headers: headers).responseJSON { response in
debugPrint(response)
if let data = try? String(contentsOf: response) {
let json = JSON(parseJSON: data)
parse(json: json)
}
}
func parse(json: JSON) {
for result in json[].arrayValue {
let price = result["ask_price"].stringValue
}
}
}
}
and I have also tried this:
func getReq() {
let headers: HTTPHeaders = [
"X-CoinAPI-Key": "Key"
]
Alamofire.request("https://rest.coinapi.io/v1/quotes/BITSTAMP_SPOT_BTC_USD/history?time_start=2018-08-21T00:00:00&time_end=2018-08-22T00:00:00&limit=100", headers: headers).responseJSON { response in
debugPrint(response)
switch response.result {
case .failure(let error):
// Do whatever here
return
case .success(let data):
// First make sure you got back a dictionary if that's what you expect
guard let json = data as? [String : AnyObject] else {
print("Failed to get expected response from webserver.")
return
}
// Then make sure you get the actual key/value types you expect
guard var price = json["ask_price"] as? Double else {
print("Failed to get data from webserver")
return
}
}
What am I doing wrong? this is how the JSON looks:
[
{
"symbol_id": "BITSTAMP_SPOT_BTC_USD",
"time_exchange": "2013-09-28T22:40:50.0000000Z",
"time_coinapi": "2017-03-18T22:42:21.3763342Z",
"ask_price": 770.000000000,
"ask_size": 3252,
"bid_price": 760,
"bid_size": 124
},
{
"symbol_id": "BITSTAMP_SPOT_BTC_USD",
"time_exchange": "2013-09-28T22:40:50.0000000",
"time_coinapi": "2017-03-18T22:42:21.3763342",
"ask_price": 770.000000000,
"ask_size": 3252,
"bid_price": 760,
"bid_size": 124
}
]
previous question deleted and reposted due to large mistake
you need to change your response to SwiftyJSON object like this
Alamofire.request("https://rest.coinapi.io/v1/quotes/BITSTAMP_SPOT_BTC_USD/history?time_start=2018-08-21T00:00:00&time_end=2018-08-22T00:00:00&limit=100", headers: headers).responseJSON { response in
debugPrint(response)
switch response.result {
case .failure(let error):
// Do whatever here
return
case .success:
// First make sure you got back a dictionary if that's what you expect
let responseJSON = JSON(response.result.value!)
if responseJSON.count != 0 {
print(responseJSON)
//do whatever you want with your object json
}
}
}
i suggest in your ApiManager you can use completion blocks to manage asyncronous request, check the next code.
class func getRequestWithoutParams(didSuccess:#escaping (_ message: JSON) -> Void, didFail: #escaping (_ alert:UIAlertController)->Void){
Alamofire.request("http://foo.bar"),method: .post,parameters: parameters,encoding: JSONEncoding.default,headers:nil).responseJSON { response in
switch response.result{
case .success:
let res = JSON(response.result.value!)
didSuccess(res)
break
case .failure(let error):
let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
let done = UIAlertAction(title: "OK", style: .default, handler: nil)
alert.addAction(done)
didFail(alert)
}
}
}
From Swift 4, you should be able to use codable to solve it:
struct YourStructure: Codable {
let symbol_id: String?
let time_exchange: String?
let ask_price: String?
private enum CodingKeys: String, CodingKey {
case symbol_id = "symbol_id"
case time_exchange = "time_exchange"
case ask_price = "ask_price"
}
}
And then parse it with JSONDecoder
let decoder = JSONDecoder()
let parsedData = decoder.decode(YourStructure.self, from: "YourJsonData")