how convert JSON response to Array in swift? - arrays

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"]
}
}

Related

Completion Handler in API never runs

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

Escaping closure captures mutating 'self' parameter while decode JSON Swift

I try to parse JSON with mvc pattern.I'm trying for the first time. But I'm getting "Escaping closure captures mutating 'self' parameter error " I mutated functions and put self where necessary.I can access and use required protocols in ViewController. Everything seems normal What's the problem here ?
import Foundation
protocol ItunesManagerDelegate {
func didUpdateSearchin(_ ItunesManager:ItunesManager, product: [ItunesModel])
func didFailWithError(error:Error)
}
struct ItunesManager {
let baseURL = "https://itunes.apple.com/search?term="
var delegate : ItunesManagerDelegate?
var itunesDataArray = [ItunesModel]()
mutating func getSearchResult(for terms:String){
let urlString = "\(baseURL)\(terms)"
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
self.delegate?.didFailWithError(error: error!)
return
}
if let searchResult = self.parseJSON(data!){
self.delegate?.didUpdateSearchin(ItunesManager(), product: searchResult)
}
}
task.resume()
}
}
mutating func parseJSON(_ itunesData:Data) -> [ItunesModel]? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([ItunesData].self, from: itunesData)
decodedData.forEach { (ItunesData) in
ItunesData.results.forEach { (SearchResults) in
let type = SearchResults.wrapperType
let name = SearchResults.collectionName
let url = SearchResults.artworkUrl100
let price = SearchResults.collectionPrice
let date = SearchResults.releaseDate
let itunesProductInfo = ItunesModel(collectionName: name, collectionPrice: price, artWorkUrl100: url, wrapperType: type, releaseDate: date)
itunesDataArray.append(itunesProductInfo)
}
}
return itunesDataArray
}catch{
delegate?.didFailWithError(error: error)
return nil
}
}
}

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.

How to wait the function execution until json loaded in Swift?

I am getting json file from url, then, all json output will be added in a array. When I print the array, it says that array is empty, but actually array is not empty I know. If I use like: DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { }, then, I can print the array.
My code to get json and adding its values to the array:
var allTags:Array<String> = []
func getJSON(getUrl:URL){
let url:URL = getUrl
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
let dataString = String(data: data, encoding: String.Encoding.utf8)!
do {
let data = Data(dataString.utf8)
let dictionaries = (try? JSONSerialization.jsonObject(with: data)) as? [[String:Any]] ?? []
for dict in dictionaries {
let tags = dict["Tags"] as? [String] ?? []
for tag in tags {
allTags.append(tag)
}
}
}
}task.resume()
}
getJSON(getUrl: self.jsonUrl())
print(self.allTags.count) //it prints 0, but there are some data I know
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
print(self.allTags.count) //it prints 168
}
Use a callback:
var allTags:Array<String> = []
func getJSON(getUrl:URL, callback: #escaping () -> Void) {
let url:URL = getUrl
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
let dataString = String(data: data, encoding: String.Encoding.utf8)!
do {
let data = Data(dataString.utf8)
let dictionaries = (try? JSONSerialization.jsonObject(with: data)) as? [[String:Any]] ?? []
for dict in dictionaries {
let tags = dict["Tags"] as? [String] ?? []
for tag in tags {
allTags.append(tag)
}
}
callback()
}
}
task.resume()
}
getJSON(getUrl: self.jsonUrl()) {
print(self.allTags.count) // Should work now
}
var allTags:Array<String> = []
func getJSON(getUrl:URL, completion: #escaping () -> ()) {
let url:URL = getUrl
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
let task = session.dataTask(with: request as URLRequest) {
(data, response, error) in
let dataString = String(data: data, encoding: String.Encoding.utf8)!
do {
let data = Data(dataString.utf8)
let dictionaries = (try? JSONSerialization.jsonObject(with: data)) as? [[String:Any]] ?? []
for dict in dictionaries {
let tags = dict["Tags"] as? [String] ?? []
for tag in tags {
allTags.append(tag)
}
}
completion()
}
}task.resume()
}
getJSON(getUrl: self.jsonUrl()) { [weak self] in
guard let strongSelf = self else { return }
print(strongSelf.allTags.count)
}

Resources