Swift: How to rm Alamofire get request One by One in loop - arrays

I am a beginner in swift and I have come across a case where I need to run AlamoFire GET request in a loop. But As far as i know , the Alamofire Get request is an Asynchronous call and calling it in loop will create a number of threads.
The requirement is :
I have an array of URLs
The array has to be traversed in a loop
URL on each index has to be called through AlamoFire GET request
The received data through the request will be added to an array of
data
After the last data is saved in the array, a
CollectionView.reload call should be called
Pseudo code is as follows:
for bookmarkURL in bookmarks
Alamofire.request(.GET, bookmarkURL ).responseJSON
{ response in switch response.result {
case .Success(let JSON):
x[i] = JSON as! [String : AnyObject] // saving data
case .Failure(let error):
print("the error for \(self.bookmarkURL) is \(error) ")
}
i++
if i == bookmarks.count{
collectionView.reload()
break}
}
can anyone tell me how should I do it in sequence?

Make bookmarks array as class property.
Add a class property index to store index value (Int) while traversing the bookmarks array
Add a function to make API call and call it recursively.
Here is the code:
func getData() {
var x = [[String: AnyObject]]()
Alamofire.request(.GET, bookmarks[index]).responseJSON { response in
switch response.result {
case .Success(let JSON):
x[self.index] = JSON as! [String : AnyObject] // saving data
self.index = self.index + 1
if self.index < self.bookmarks.count {
self.getData()
}else {
self.collectionView.reloadData()
}
case .Failure(let error):
print("the error for \(self.bookmarks[self.index]) is \(error) ")
if self.index < self.bookmarks.count {
self.getData()
}else {
self.collectionView.reloadData()
}
}
}
}
In this way your request will be made only when previous is completed thus making it sequentially instead of parallel and you can also reload the collectionView only after the last request is completed.

If you want to reload your collection view only after receiving whole the data, you can use dispatch_group like,
let group = dispatch_group_create() // create a group.
dispatch_group_enter(group) // enter the group just before create the request
dispatch_group_leave(group)//leave the group on completion closure
dispatch_group_notify(group, group) {
//reload your collection view
}
Full code
for bookmarkURL in bookmarks {
dispatch_group_enter(group)
Alamofire.request(.GET, bookmarkURL ).responseJSON { response in
switch response.result {
case .Success(let JSON):
x[i] = JSON as! [String : AnyObject] // saving data
case .Failure(let error):
print("the error for \(self.bookmarkURL) is \(error) ")
}
dispatch_group_leave(group)
}
}
dispatch_group_notify(group, group) {
collectionView.reload()
}
Note: if your bookmarks array is too large, better not to do it in a loop.
For that you can use following code
func loadBookmarkAtIndex(index: Int) {
if index >= bookmarks.count {
collectionView.reload()
return
}
let bookmarkURL = bookmarks[index]
Alamofire.request(.GET, bookmarkURL ).responseJSON { response in
switch response.result {
case .Success(let JSON):
x[index] = JSON as! [String : AnyObject] // saving data
case .Failure(let error):
print("the error for \(bookmarkURL) is \(error) ")
}
self.loadBookmarkAtIndex(index+1)
}
}
And call self.loadBookmarkAtIndex(0) from where you are initiating the data retrieve.

I think you should reload item of collection view each time you get the response from the sever regarding to the response received.
Here is the solution:
let x : NSMutableArray = NSMutableArray.init(capacity: bookmarks.count)
for var i in 0 ..< x.count {
Alamofire.request(.GET, bookmarkURL ).responseJSON
{ response in switch response.result {
case .Success(let JSON):
x.insertObject(JSON as! [String : AnyObject], atIndex:i)
// x[i] = // saving data
// <reload relative item cell of collectionView>
case .Failure(let error):
print("the error for \(self.bookmarkURL) is \(error) ")
}
i+=1
}
Hope above code will help you.

Related

How to convert json array to objects array Alamofire?

I have such array in from api:
[{
data = "";
datetime = "23.07.2020 12:09";
id = 340593;
status = "My current task is";
},...]
I have created such struct:
struct RemarksModel {
let id:Int
let status,datetime:String
let data:String?
}
And here I make request:
AF.request(URLRequest(url:Pathes.init(endpoint: "notepad/\(noteModel?.id ?? 0)/documentation").resourseUrl),
interceptor: CallInterceptor.init(method:HTTPMethod.get)).responseJSON(completionHandler: { (response) in
print(response.description)
switch response.result{
case .success(let array):
let remarksData = array as? [RemarksModel]
let json = response.data as? [RemarksModel]
print(json?.count)
// if remarksData?.count ?? 1 > 0{
// self.remarksArray += remarksData!
// self.tableView.reloadData()
// }
case .failure(let error):
print(error.localizedDescription)
}
})
the problem is that I can't convert this array to array of my model objects. when I try to add all received data to array my app crashes because my array is nil, despite of json in logs. Maybe I have to use another way of converting received json array to objects array?
You can use directly .responseDecodable function instead of .responseData or .responseJSON after confirming RemarksModel to Codable (or just Decodable) protocol
.responseDecodable(of: [RemarksModel].self, queue: .main, decoder: JSONDecoder()) { (response) in
switch response.result {
case let .success(data):
// success
print(data)
case let .failure(error):
// error
print(error.localizedDescription)
}
}
You can add Codable protocol to RemarksModel and use .responseData instead of .responseJSON
.responseData { response in
switch response.result {
case let .success(data):
do {
let result = try JSONDecoder().decode([RemarksModel].self, from: data)
// success
} catch {
print("decoding error:\n\(error)")
// error
}
case let .failure(error):
// error
}
}

Swift UIImage Array from JSON URL

I try to create an Array of UIImages from a URL Array received from a JSON Request to show them afterwards in a UITableView.
But somehow my UIImage Array stays Empty and is not receiving any Data.
The other Arrays for example memeURL are receiving all Data correct but memePics.count stays on 0.
Would be great if someone could show me what i am doing wrong.
Also if for this task there is a better way on how to do it - it would be also appreciated!
Var:
var memePics: [UIImage] = []
Loop to add Images to Array:
while(i < memeURL.count) {
MemeAPI.requestAPIImageFile(url: memeURL[i]) { (image, error) in
guard let image = image else {
print("PIC IS NIL")
return
}
self.memePics.append(image)
i+=1
}
}
RequestAPIImageFile Function:
class func requestAPIImageFile(url: URL, completionHandler: #escaping (UIImage?, Error?) -> Void) {
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {
completionHandler(nil, error)
return}
let downloadedImage = UIImage(data: data)
completionHandler(downloadedImage, nil)
}
task.resume()
}
Add plus line out of callback
self.memePics.append(image)
}
i+=1
and use DispatchGroup to be notified on finish like
let g = DispatchGroup()
memeURL.forEach {
g.enter()
MemeAPI.requestAPIImageFile(url:$0) { (image, error) in
guard let image = image else {
print("PIC IS NIL")
return
}
self.memePics.append(image)
g.leave()
}
}
g.notify(queue:.main) {
print("All done")
}

Swift ObjectMapper keeps returning ambiguous without more context

I am attempting to use Swift 3 with (ObjectMapper) to map a players array from a given JSON response on a cut-down class object called "Player"; but am finding it difficult to map it.
// Sample JSON
{
"_meta": {
...
},
"fixtures": [{
...
}],
"players": [{
"name": "Smith",
"id": "15475-9524",
}]
}
However, I am finding it hard to get it to understand how to make it map properly as it always complains that it needs more context.
I am wanting my JSON consumer to get me the list of players then map all the players using Object Mapper into an array.
When I use
var players : Array<Player> = Mapper<Player>().mapArray(JSONArray: res)
It complains
Type of expression is ambiguous without more context
My class is as follows
class Player: NSObject, Mappable {
var name: String?
required init?(map: Map) {
super.init()
}
// Mappable
func mapping(map: Map) {
name <- map["name"]
}
}
I am using AlamoFire to consume the JSON.
Alamofire.request(url).responseJSON(completionHandler: {
response in
switch response.result {
case .success(let JSON):
guard let res = JSON as? [String:Any] else {
print ("Can't do this")
return
}
var players : Array<Player> = Mapper<Player>().mapArray(JSONArray: res)
print (players)
break
case .failure(let error):
print("** Request failed with error: \(error) **")
break
}
I don't quite understand how to use the ObjectMapper on the array I'm wanting to fetch.
Any assistance on this would be good.
I think you are confusing JSON Dictionary with Player's array.
Try this:
guard let res = JSON as? [String:Any] else {
print ("res:Can't do this")
return
}
guard let json_players = res["players"] as? [[String:Any]] else {
print ("json_players:Can't do this")
return
}
var players : Array<Player> = Mapper<Player>().mapArray(JSONArray: json_players)

Cannot pass JSON array to array

I am trying to pass my JSON array to an array called array so that I can then query the array with submission_id with value 27 to obtain the safety_rating_id, schedule_job_id, score and submission_id from this JSON https://codeshare.io/UqJMV but I'm being thrown this error
Cannot convert value of type '[JSON]' to expected argument type 'JSON'
Code to pass JSON to array:
var array: [JSON] = []
func getTask(onCompletion: () -> (), onError: ((NSError) -> ())? = nil) {
guard let endPoint = Data.sharedInstance.weeklyEndpoint
else { print("Empty endpoint"); return }
Alamofire.request(.GET, endPoint, encoding: .JSON)
.validate()
.responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
for (_,subJson):(String, JSON) in json {
if let date = subJson["start_date"].string{
self.date = date
}
if let building = subJson["building_name"].string{
self.building = building
}
if let jobId = subJson["schedule_job_id"].int {
self.jobIdArray.append(jobId)
}
if let tasks = subJson["tasks"].array{
Tasks.sharedInstance.datas = tasks
for building in tasks {
if let ratings = building["safety_ratings"].array{
print(ratings)
self.array.append(ratings)
}
}
}
}
onCompletion()
}
case .Failure(let error):
print("Request failed with error: \(error)")
onError?(error)
}
}
}
append() expects a single element (JSON), but ratings is an array ([JSON]).
That's what the error message says.
To append an array use appendContentsOf:
self.array.appendContentsOf(ratings)

How to wrapped JSON Handler in Function

So I'm using this function to pass the JSON Value (Any Object) in other class but it's full of issues so I can't get it working
func getSWPeopleAPI() -> NSMutableArray {
var JSON2: NSMutableArray
Alamofire.request(.GET, "http://swapi.co/api/people/1").responseJSON { Response in
print(Response.request)
print(Response.response)
print(Response.data)
print(Response.result)
if let JSON = Response.result.value{
JSON2 = JSON as! NSMutableArray
}
else{
return
}
}
return JSON2
}
Do you have a suggestion on how can I achieve this to pass it to my ApiManagerClass and what data type is best use when dealing with JSON?
You are doing in a wrong way! JSON must have a type of NSDictionary.
func getSWPeopleAPI(strUrl: String, completionHandler: (NSDictionary?, NSError?) -> ()) -> (){
Alamofire.request(.GET, strUrl).responseJSON { response in
switch response.result {
case .Success(let data):
let json = data as? NSDictionary
completionHandler(json, nil)
case .Failure(let error):
completionHandler(nil, error)
}
}
Here is the code with completion handler and error as specified!
How to Use:
postWebserviceWithURL(Url!) { (responseData, error) -> () in
//Code
}

Resources