I've got this function to fetch a JSON API, but I need to set custom parameters.
Here is the function:
func fetchTipsJson(completion: #escaping (Result<Root, Error>) -> ()) {
let urlString = "http://telemedapi_dev.assistcard.com/Api/Config/GetConfigGroupList"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, resp, err) in
// Error
if let err = err {
completion(.failure(err))
return
}
// Successful
do {
let tips = try JSONDecoder().decode(Root.self, from: data!)
completion(.success(tips))
print(tips)
} catch let jsonError {
completion(.failure(jsonError))
}
}.resume()
}
And here are the parameters:
{
"Parameters": {
"CountryCode": 540,
"ConfigurationPath": "TelemedGlobalConfig>Tips",
"LogApiCallsStatus": 2
},
"VisitorIp": null,
"ApplicationName": null,
"CurrentUICulture": "es-ES"
}
How can I do this? I can't find anything. Notice that I'm using the new Result from Swift 5, don't know if that changes anything.
Related
so i wanna parse json api, but i the way i get that param to parse i need to fetch another json (which is working), and since i cant put that data param for my 2nd json api into global var so i can just put it into another func, i have this idea that i parse my 2nd json api inside the 1st urlSession, but i always get a nil callback,
override func viewDidLoad() {
super.viewDidLoad()
getRoom()
}
func getRoom() {
guard let url = URL(Some url) else {return}
print(url)
URLSession.shared.dataTask(with: url) { data, resp, err in
guard let data = data else {return}
do{
let decoder = JSONDecoder()
let room = try decoder.decode(User.self, from: data)
self.dataClient = [room].compactMap{$0!.data}
self.DATA = [room]
print("ini dataClient 🪕\(self.dataClient)")
let roomid = self.dataClient[0].RoomID
self.roomId = roomid
print(self.roomId)
DispatchQueue.main.async {
checkRoom()
}
}catch{
print(err!)
}
}.resume()
}
func checkRoom() {
if self.roomId == 0 {
print("roomId nil")
}else if self.roomId != 0{
print("ini room id \(self.roomId)")
guard let urlRoom = URL(some url) else {return
URLSession.shared.dataTask(with: urlRoom) { (data, resp, err) in
guard let data = data else {return}
do{
let decoder = JSONDecoder()
let roomAv = try decoder.decode(User.self, from: data)
self.DATA = [roomAv]
print("ini boolnya 🎸 \(self.DATA[0].success)")
print("Success")
}catch{
print("damn😭") // this line always get called
}
}.resume()
}
}
can anyone tell me any ideas? the reason i put the 2nd urlsession inside 1st urlsession because i need that (self.roomId) for my param in my 2nd Json api.
and when i try to separate both urlsession func in my checkRoom() alwasy called "roomId Nil"
I wouldn't make a call within a call personally. That's asking for trouble. Just call the first endpoint, get the data from it and pass in whatever you needed from that into the second call in your logic controller.
Quasi code:
import Foundation
class Test {
func getRoom() {
getFirstCall { [weak self] (foo) in
self?.getSecondCall(someArg: foo) {
// Handle data here.
}
}
}
func getFirstCall(completion: #escaping (_ somethingToReturn: String) -> ()) {
guard let url = URL(string: "Some URL") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
// Logic to ingest data.
completion("foo")
}.resume()
}
func getSecondCall(someArg: String, completion: #escaping () -> ()) {
guard let url = URL(string: "Some URL 2") else { return }
// Use "someArg" however you need in this call. queryParam, body, etc.
URLSession.shared.dataTask(with: url) { data, response, error in
// Logic to ingest data.
completion()
}.resume()
}
}
I performed this same request on Postman, and it works fine. However here the data is empty when I run this URLSession (see DATA EMPTY tag):
import Foundation
struct PhotoResponse: Decodable {
let results: [Photo]
}
class PhotoInteractor {
static var shared = PhotoInteractor()
var error: Error?
func getPhotos(query: String, completionHandler: #escaping ([Photo], Error?) -> Void) {
guard let url = URL(string: "https://api.unsplash.com/search/photos?query=o&page=1&per_page=30&") else {
return
}
var request = URLRequest(url: url)
request.setValue("Client-ID \(Config.shared.accessKey)", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: request) { data, response, error in
//*** DATA EMPTY!!! ***
if let data = data, let photos = self.photosFromJSONResponse(data) {
completionHandler(photos, nil)
}
}.resume()
}
func photosFromJSONResponse(_ data: Data) -> [Photo]? {
do {
let photoResponse = try JSONDecoder().decode(PhotoResponse.self, from: data)
return photoResponse.results
} catch {
self.error = error
}
return nil
}
}
I have a URL which my app fetches. it prints a dictionary with two keys but inside one of the keys is a lot of information I would like to get for my app.
The URL gets lots of information but not as a conventional dictionary.
this is a VERY simplified version:
["person":
name: John
height: 187, "fruit": colour: red
]
etc...
so I would just want to get the name of the person inside the key person but I am having trouble finding this.
Is there any way to do this? I have been trying JSON Parsing, for loops and I am stuck.
Edit:
it isn't a dictionary inside a dictionary. If you would like to see what I am working with. Just copy and paste this link. It is an example of what I am using. http://itunes.apple.com/lookup?bundleId=com.burbn.instagram
I would need just the seller name or just the currency etc.
Code to read the link and print it:
override func viewDidLoad() {
super.viewDidLoad()
fetchData { (dict, error) in
print(dict!)
}
}
func fetchData(completion: #escaping ([String:Any]?, Error?) -> Void) {
let url = URL(string: link)!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
do {
if let array = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String:Any]{
completion(array, nil)
}
} catch {
print(error)
completion(nil, error)
}
}
task.resume()
}
The data you are fetching is JSON. In order to use it, you will have to decode it. The recommended way is using JSONDecoder in Swift.
First you will have to define your model, which correspond to the data model, and make it conform to Codable protocol:
struct App: Codable {
var sellerName: String
// Alternatively, if you don't want to use an enum, you can use a String.
var currency: Currency
enum Currency: String, Codable {
case australianDollar = "AUD",
case britishPound = "GBP",
case euro = "EUR",
case hongKongDollar = "HKD",
case usDollar = "USD"
// Complete this with all the currency…
}
}
struct JSONResult: Codable {
var resultCount: Int
var results: [App]
}
Once this is done, you only have to edit your fetchData method so it returns an array App populated with the data you fetched.
Swift 4 version:
func fetchData(completion: #escaping (JSONResult?, Error?) -> Void) {
guard let url = URL(string: link) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
completion(nil, error)
return
} else if let data = data {
do {
let decoder = JSONDecoder()
let result = try decoder.decode(JSONResult.self, from: data)
completion(result, nil)
} catch {
print(error)
completion(nil, error)
}
}
}
task.resume()
}
Swift 5 version using Result type:
func fetchData(completion: #escaping (Result<JSONResult, Error>) -> Void) {
guard let url = URL(string: link) else { return }
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
completion(.failure(error))
return
} else if let data = data {
do {
let decoder = JSONDecoder()
let result = try decoder.decode(JSONResult.self, from: data)
completion(.success(result))
} catch {
print(error)
completion(.failure(error))
}
}
}
task.resume()
}
More information about JSONDecoder
Dictionary data is:
let dict = ["person": ["name": "John", "height": "187"], "fruit": ["colour": "red"]]
Suppose you need name of the person. So you can do it by the following way.
if let person = dict["person"], let name = person["name"] as? String {
print (name)
}
Here is Stripe's example code for retrieving a customer (https://github.com/stripe/stripe-ios/blob/master/Example/Stripe%20iOS%20Example%20(Simple)/MyAPIClient.swift):
#objc func retrieveCustomer(_ completion: #escaping STPCustomerCompletionBlock) {
guard let key = Stripe.defaultPublishableKey() , !key.contains("#") else {
let error = NSError(domain: StripeDomain, code: 50, userInfo: [
NSLocalizedDescriptionKey: "Please set stripePublishableKey to your account's test publishable key in CheckoutViewController.swift"
])
completion(nil, error)
return
}
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString) else {
// This code is just for demo purposes - in this case, if the example app isn't properly configured, we'll return a fake customer just so the app works.
let customer = STPCustomer(stripeID: "cus_test", defaultSource: self.defaultSource, sources: self.sources)
completion(customer, nil)
return
}
let path = "/customer"
let url = baseURL.appendingPathComponent(path)
let request = URLRequest.request(url, method: .GET, params: [:])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
let deserializer = STPCustomerDeserializer(data: data, urlResponse: urlResponse, error: error)
if let error = deserializer.error {
completion(nil, error)
return
} else if let customer = deserializer.customer {
completion(customer, nil)
}
}
}
task.resume()
}
Stripe has a customer deserializer that specifies that "STPCustomerDeserializer expects the JSON response to be in the exact same format as the Stripe API." The Stripe API is here in Nodejs:
// Retrieve Stripe Customer
app.get('/customer', function(request, response) {
// Load the Stripe Customer ID for your logged in user
var customer = 'cus_abc...';
stripe.customers.retrieve(customerId, function(err, customer) {
if (err) {
response.status(402).send('Error retrieving customer.');
} else {
response.json(customer);
}
});
The response I get is an error: The data could not be read because it isn't in the correct format. I think it wants me to return JSON but I tried that several different ways to no avail. Such as:
func retrieveCustomer(_ completion: #escaping STPCustomerCompletionBlock) {
guard let key = Stripe.defaultPublishableKey() , !key.contains("#") else {
let error = NSError(domain: StripeDomain, code: 50, userInfo: [
NSLocalizedDescriptionKey: "Set PubKey"])
completion(nil, error)
return
}
guard let baseURLString = baseURLString, let baseURL = URL(string: baseURLString) else {
let customer = STPCustomer(stripeID: "", defaultSource: self.defaultSource, sources: self.sources)
completion(customer, nil)
return
}
let path = "/customer"
let url = baseURL.appendingPathComponent(path)
let request = URLRequest.request(url, method: .GET, params: [:])
let task = self.session.dataTask(with: request) { (data, urlResponse, error) in
DispatchQueue.main.async {
let deserializer = STPCustomerDeserializer(data: data, urlResponse: urlResponse, error: error)
if let error = deserializer.error {
completion(nil, error)
return
} else if let customer = deserializer.customer {
do {
let parser = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as Any
print(parser)
} catch {
print(error)
}
completion(customer, nil)
}
}
}
task.resume()
}
What am I missing!?
I am a beginner in iOS development with Swift language. I have a JSON file contains the data as below.
{
"success": true,
"data": [
{
"type": 0,
"name": "Money Extension",
"bal": "72 $",
"Name": "LK_Mor",
"code": "LK_Mor",
"class": "0",
"withdraw": "300 $",
"initval": "1000 $"
},
{
},
{
},
]
}
I want to parse this file and have to return the dictionary which contain the data in the JSON file. This is the method I wrote.
enum JSONError: String, ErrorType {
case NoData = "ERROR: no data"
case ConversionFailed = "ERROR: conversion from JSON failed"
}
func jsonParserForDataUsage(urlForData:String)->NSDictionary{
var dicOfParsedData :NSDictionary!
print("json parser activated")
let urlPath = urlForData
let endpoint = NSURL(string: urlPath)
let request = NSMutableURLRequest(URL:endpoint!)
NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
guard let dat = data else {
throw JSONError.NoData
}
guard let dictionary: NSDictionary = try NSJSONSerialization.JSONObjectWithData(dat, options:.AllowFragments) as? NSDictionary else {
throw JSONError.ConversionFailed
}
print(dictionary)
dicOfParsedData = dictionary
} catch let error as JSONError {
print(error.rawValue)
} catch {
print(error)
}
}.resume()
return dicOfParsedData
}
When I modify this method to return a dictionary, it always return nil. How can I modify this method.
You can not return for an asynchronous task. You have to use a callback instead.
Add a callback like this one:
completion: (dictionary: NSDictionary) -> Void
to your parser method signature:
func jsonParserForDataUsage(urlForData: String, completion: (dictionary: NSDictionary) -> Void)
and call the completion where the data you want to "return" is available:
func jsonParserForDataUsage(urlForData: String, completion: (dictionary: NSDictionary) -> Void) {
print("json parser activated")
let urlPath = urlForData
guard let endpoint = NSURL(string: urlPath) else {
return
}
let request = NSMutableURLRequest(URL:endpoint)
NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) -> Void in
do {
guard let dat = data else {
throw JSONError.NoData
}
guard let dictionary = try NSJSONSerialization.JSONObjectWithData(dat, options:.AllowFragments) as? NSDictionary else {
throw JSONError.ConversionFailed
}
completion(dictionary: dictionary)
} catch let error as JSONError {
print(error.rawValue)
} catch let error as NSError {
print(error.debugDescription)
}
}.resume()
}
Now you can use this method with a trailing closure to get the "returned" value:
jsonParserForDataUsage("http...") { (dictionary) in
print(dictionary)
}