Completion Handler in API never runs - arrays

I have an APICall as below
static func getLinkToken() {
// Prepare URL
let url = URL(string: "https://sandbox.plaid.com/link/token/create")
guard let requestUrl = url else { fatalError() }
// Prepare URL Request Object
var request = URLRequest(url: requestUrl)
request.httpMethod = "POST"
let prePostString : [String : Any] = [
"client_id" : clientID,
"secret" : secret,
"user" : ["client_user_id": uniqueID],
"client_name": "Plaid App",
"products": ["transactions"],
"country_codes": ["US"],
"language": "en",
"account_filters": [
"depository": [
"account_subtypes": ["checking"]
]
]
]
let postString = try? JSONSerialization.data(withJSONObject: prePostString)
// Set HTTP Request Body
request.httpBody = postString;
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
// Perform HTTP Request
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
// Check for Error
if let error = error {
print("Error took place \(error)")
return
}
do {
//Parse response into a dict
let parsedData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
//Add link token to user class
User.linkToken = (parsedData["link_token"] as! String)
} catch let error as NSError {
print(error)
}
// Convert HTTP Response Data to a String
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print("Response data string:\n \(dataString)")
}
}
task.resume()
}
For some reason this code never works if I call it unless it is in #main.init.
It appears that when I call the function in any class/method other than the initializer for main, the completion handler never runs. An example of how I call it is shown below.
class Practice {
func getAPI() {
getLinkToken()
}
}
TLDR; completion handler in API call never runs.
Thanks

Related

how convert JSON response to Array in swift?

func llamadaApiDos(postData: (Data),empresa: String,boundary: String) -> [String] {
var request = URLRequest(url: URL(string: "https://www.something.com")!,timeoutInterval: Double.infinity)
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
var success = false
var serviceResponse = [""]
let semaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
if let error = error {
print("Error while trying to re-authenticate the user: \(error)")
} else if let response = response as? HTTPURLResponse,
300..<600 ~= response.statusCode {
print("Error while trying to re-authenticate the user, statusCode: \(response.statusCode)")
} else if let data = data {
let loginDataModel = try! JSONDecoder().decode(responseLogin.self,from: data)
serviceResponse = JSONDecoder.decode(loginDataModel)
success = true
}else{
success = true
}
semaphore.signal()
})
task.resume()
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
if success
{
return serviceResponse
}else
{
return ["Error"]
}
}
Cannot assign value of type '(T.Type, Data) throws -> T' to type '[String]'
I need to convert the JSON response into an array that can be validated in another function, but I don't know what type of data I should return, I'm sorry if it's not well understood, my English isn't very good either.
To convert the JSON response to an array in Swift, you can use the JSONSerialization class to convert the response data to a dictionary, then access the array stored in the dictionary using the key for the array.
Here's an example:
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let array = json["keyForArray"] as? [String] {
// Use the array here
}
In your code, you can use this approach to convert the JSON response to an array, then return the array from the llamadaApiDos function.
Here's how your function would look like with this change:
func llamadaApiDos(postData: (Data),empresa: String,boundary: String) -> [String] {
var request = URLRequest(url: URL(string: "https://www.something.com")!,timeoutInterval: Double.infinity)
request.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
request.httpMethod = "POST"
request.httpBody = postData
var success = false
var serviceResponse = [""]
let semaphore = DispatchSemaphore(value: 0)
let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
if let error = error {
print("Error while trying to re-authenticate the user: \(error)")
} else if let response = response as? HTTPURLResponse,
300..<600 ~= response.statusCode {
print("Error while trying to re-authenticate the user, statusCode: \(response.statusCode)")
} else if let data = data {
// Convert the JSON response to an array
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let array = json["keyForArray"] as? [String] {
// Use the array here
serviceResponse = array
success = true
}
}else{
success = true
}
semaphore.signal()
})
task.resume()
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
if success
{
return serviceResponse
}else
{
return ["Error"]
}
}

How to pass Data object in Post Api in swift

I am new in swift and trying to send Data to Api which is in the form of Data object and i am trying to pass that data in JSON
let val:Data = myFile.DataRepresentation()
let json: [String: Any] = ["file": val,
"dict": ["1":"First", "2":"Second"]]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
it giving me error "Invalid type in JSON.write"
or help me how can i pass Data in NSMUtableURLRequest
Try to implement this code example, it should work:
let json: [String: Any] = ["name": "morpheus",
"job": "leader"]
let jsonData = try? JSONSerialization.data(withJSONObject: json)
// create post request
let url = URL(string: "https://reqres.in/api/users")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()

How to take a function result and turn it into a key value pair array

I am fairly new to Swift but I have a function that returns a key value pair result of numbers
func dataPostRequest(_ url:String,_ paramString:String)
{
let url:NSURL = NSURL(string: url)!
let session = URLSession.shared
let request = NSMutableURLRequest(url: url as URL)
request.httpMethod = "POST"
request.httpBody = paramString.data(using: String.Encoding.utf8)
let task = session.dataTask(with: request as URLRequest) {
(
data, response, error) in
guard let _:NSData = data as NSData?, let _:URLResponse = response, error == nil else {
print("error")
return
}
if let dataString = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
{
print(dataString)
}
}
task.resume()
}
I am able to call the function like this:
dataPostRequest("https://myserver.com/","user_email=emailtest.com")
This works correctly but I want to now use the data that I've pulled and display it in a Table View. I have my tableView set up correctly but I am wondering how I can take my function and turn it into a key value pair array or a dictionary that I can use. I have tried creating an empty dictionary and set my function call to it but I get an error:
var emptyDictionary = [Int: Int]()
emptyDictionary = dataPostRequest("https://myserver.com/","user_email=emailtest.com")
And no matter what variation I try I keep getting:
Cannot assign value of type '()' to type '[Int : Int]'
Any guidance would be greatly appreciated.
dataPostRequest has no return value (aka ()). You can decode the received data in the completion handler and assign it to the dictionary. See the comment line below.
If you need to proceed in another function you have to add a completion handler described here.
Basically don't use NS... classes in Swift at all if there are native counterparts. And don't annotate types the compiler can infer.
The native Swift version is
func dataPostRequest(with url:String, paramString : String)
{
let url = URL(string: url)!
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = paramString.data(using: .utf8)
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
print(error)
return
}
let dataString = String(data: data!, encoding: .utf8)!
print(dataString)
// here decode the data to the desired type and assign it to emptyDictionary
}
task.resume()
}

Pass array of string in parameter request - swift 5

Pass array of string in parameter request - swift 5. I want to send an array in a parameter from an alamofire request.
This is the request on Postman:
This is my solution:
var Token : String?
var tasksMO = [NSManagedObject]()
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "ConfirmActivationEntity")
do {
let results = try pe.context.fetch(request)
tasksMO = results as! [NSManagedObject]
for taskmo in tasksMO {
Token = (taskmo.value(forKey: "access_token") as! String )
}
print("assbil")
} catch {
print("fild")
}
print(" Token :\(Token!)")
let levels : [String] = ["+972569431827","+972598110437","+972592923343","3555656","00970567163651","258258"]
let paramsJSON = JSON(levels)
debugPrint(paramsJSON)
let parameters : [String : Any] = ["contacts":paramsJSON,"platform":2]
let headers : HTTPHeaders = ["Authorization":"Bearer \(Token!)","Accept-Language" : Locale.current.languageCode ?? "ar", "Content-Type" : "application/x-www-form-urlencoded","Accept" : "application/json"]
guard let url = URL(string: "\(UrlApi.url)\(UrlApi.contactsPost)") else { return }
sdLoader.startAnimating(atView: self.view)
Alamofire.request(url, method: .post, parameters: parameters, headers: headers).responseJSON { response in
if let error = response.error {
print(error)
return
}
if let status = response.response?.statusCode {
print(status)
print(response.result.value!)
switch(status){
case 200:
self.sdLoader.stopAnimation()
do{
let decoder = JSONDecoder()
let userResultDec = try decoder.decode(ContactsJSON.self, from: response.data!)
decoder.keyDecodingStrategy = .convertFromSnakeCase
if userResultDec.status == true {
print(userResultDec.items)
} else if userResultDec.status == false {
self.showAlert(title: "رسالة", message: "هناك خطا بالمعلومات المدخلة", style: .alert)
}
} catch let parsingError {
print("Error", parsingError)
MessageBox.Show(Message: " \(parsingError) , خطأ في السيرفير ", MyVC: self)
}
case 401 :
self.sdLoader.stopAnimation()
print("error with response status: \(status)")
print("خطأ في السيرفير")
MessageBox.Show(Message: "error with response status: \(status) , خطأ في السيرفير ", MyVC: self)
case 404 :
self.sdLoader.stopAnimation()
print("error with response status: \(status)")
print("خطأ في السيرفير")
MessageBox.Show(Message: "error with response status: \(status) , خطأ في السيرفير ", MyVC: self)
case 500:
MessageBox.Show(Message: "error with response status: \(status) , خطأ في السيرفير ", MyVC: self)
default:
print("Error")
}
}
}
The problem is:
The items in the array do not appear in the reference response from the server
I am using the Alamofire pod and the SwiftyJson pod with iOS 13, Swift 5, and Xcode 11.
Something is wrong with application/x-www-form-urlencoded conversations in Alamofire/Swift. It does work in Postman way. Postman automatically converts your array but swift does not.
struct ArrayEncoding: ParameterEncoding {
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var request = try URLEncoding().encode(urlRequest, with: parameters)
request.url = URL(string: request.url!.absoluteString.replacingOccurrences(of: "%5B%5D=", with: "="))
return request
}
}
Please try this example :
var apiRequest = URLRequest(url: url)
apiRequest.httpMethod = "POST"
apiRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
let levels = ["+972569431827","+972598110437","+972592923343","3555656","00970567163651","258258"]
apiRequest.httpBody = try! JSONSerialization.data(withJSONObject: values)
Alamofire.request(apiRequest)
.responseJSON { response in
switch response.result {
case .failure(let error):
print(error)
if let data = response.data, let responseStr = String(data: data, encoding: .utf8) {
print(responseStr)
}
case .success(let responseValue):
print(responseValue)
}
}
This is how I make the request post by sending an array: (Alamofire v5)
let apiInputs = [
"input1": "example1",
"input2": "example2",
"input3": "example3",
"inputN": "exampleN",
]
AF.upload(multipartFormData: { multipartFormData in
for (key, value) in apiInputs {
multipartFormData.append(value.data(using: String.Encoding.utf8)!, withName: key) } // End for multipart form data.
}, to: "https://example.com/api")
.responseJSON { response in
debugPrint(response)
}
I leave you the official documentation:
Alamofire/MultipartFormData

Swift passing same array value to PHP/MySQL

I have an app where a user reviews an array of users. Example:
["user1","user2","user3","user4"]
When the user reviews the first user the app should present the second user for voting. Unfortunately when a user votes on the user2, user3, user4; the value for user1 is what's put in the database. I tested the PHP via postman and there's no issue there, so it has to be in the swift code. Here is my code:
func loadCards()->[String] {
let username = user!["username"] as! String
let url = URL(string: "http://localhost/shotsCenter.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "username=\(username)"
request.httpBody = body.data(using: .utf8)
// launch session
URLSession.shared.dataTask(with: request) { data, response, error in
// getting main queue of proceeding inf to communicate back, in another way it will do it in background
// and user will no see changes :)
DispatchQueue.main.async(execute: {
if error == nil {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: AnyObject]
// clean up
self.valueArray.removeAll(keepingCapacity: false)
self.circleArray.removeAll(keepingCapacity: false)
// delcare new secure var to store json
guard let parseJSON = json else {
print("Error while parsing")
return
}
// declare new secure var to store $returnArray["users"]
guard let parseUSERS = parseJSON["users"] else {
print(parseJSON["message"])
return
}
self.valueArray = parseUSERS as! [AnyObject]
if self.valueArray.count > 0 {
let num_currentLoadedCardsArrayCap = (self.valueArray.count > MAX_BUFFER_SIZE) ? MAX_BUFFER_SIZE : self.valueArray.count
for (i, value) in self.valueArray.enumerated() {
let ava = self.valueArray[i]["ava"]
let id = self.valueArray[i]["id"]
let age = (NSString(format: "%#", self.valueArray[i]["age"] as! CVarArg))
let city = self.valueArray[i]["city"]
let state = self.valueArray[i]["state"]
self.age.append(age as AnyObject)
self.city.append(city as AnyObject)
self.state.append(state as AnyObject)
let url = NSURL(string: ava! as! String)!
let imageData = try? Data(contentsOf: url as URL)
let image = UIImage(data: imageData!)!
self.circleArray.append(image)
let reviewed = self.valueArray[i]["username"]
self.reviewed.append((reviewed as AnyObject) as! String)
print("reviewed user", reviewed! as Any)
let newCard = self.createDraggableViewWithData(at: i, value:value as! NSDictionary)
self.allCardsArray.append(newCard)
if i < num_currentLoadedCardsArrayCap {
self.currentLoadedCardsArray.append(newCard)
}
}
for (i,_) in self.currentLoadedCardsArray.enumerated() {
if i > 0 {
self.viewTinderBackGround.insertSubview(self.currentLoadedCardsArray[i], belowSubview: self.currentLoadedCardsArray[i - 1])
}
else {
self.viewTinderBackGround.addSubview(self.currentLoadedCardsArray[i])
}
self.currentIndex += 1
}
self.animateCardAfterSwiping()
self.perform(#selector(self.createDummyCard), with: nil, afterDelay: 1.0)
}
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = "\(error)"
})
return
}
} else {
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
})
return
}
})
} .resume()
return reviewed
}
func insertShot(_ rating: String, _ reviewed2: NSDictionary) {
let reviewer = user!["username"] as! String
let reviewed2 = reviewed[index]
let url = URL(string: "http://localhost/shotsCenter.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// param to be passed to php file
let param = [
"user" : reviewer,
"revieweduser" : reviewed2,
"rating" : rating
] as [String : Any]
// body
let boundary = "Boundary-\(UUID().uuidString)"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// ... body
request.httpBody = createBodyWithParams(param as? [String : String], boundary: boundary)
// launch session
URLSession.shared.dataTask(with: request) { data, response, error in
// get main queu to communicate back to user
DispatchQueue.main.async(execute: {
if error == nil {
do {
// json containes $returnArray from php
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary
// declare new var to store json inf
guard let parseJSON = json else {
print("Error while parsing")
return
}
// get message from $returnArray["message"]
let message = parseJSON["message"]
//print(message)
// if there is some message - post is made
if message != nil {
// reset UI
// self.msgTxt.text = ""
// switch to another scene
//self.tabBarController?.selectedIndex = 3
//_ = self.navigationController?.popViewController(animated: true)
}
} catch {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = "\(error)"
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
} else {
// get main queue to communicate back to user
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
appDelegate.infoView(message: message, color: colorSmoothRed)
})
return
}
})
}.resume()
}
No idea what currentIndex is for. Sth about the view hierarchy?
index is an interesting candidate. The only times it is used is in
let reviewed2 = reviewed[index]
and I see no modification to it. Since usernames are stored in there with
let reviewed = self.valueArray[i]["username"]
I think you are sending the same username for all four ratings, since reviewed2 is then made of the .httpBody. That's a guess from what I can see.
Tip: Codable
Codable may save you a lot of the ugliness of juggling around with AnyObject & co, since you'll have a [User] array instead of.. 4 separate arrays which you access with [index]. https://app.quicktype.io will get you started quickly by providing you with the parsing code for the given JSON.

Resources