AWS S3 iOS - Know when listing objects is finished - ios

I'm listing all the objects in an S3 bucket using the following function
let s3 = AWSS3.default()
let listObjectsRequest = AWSS3ListObjectsV2Request()
s3.listObjectsV2(listObjectsRequest!) { (result, error) in
...
}
It's an asynchronous function. How can I know when the listing is done using Swift 3? I have an activity indicator that is running and stops when the listing is done.

Turns out it had a completion handler.
I just added the code to stop the animation at the bottom of the listObjectsV2.
Just remember to update the UI inside a main queue
DispatchQueue.global(qos: .userInitiated).async {
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
// Stop animation
}
}

Related

Cancel UICollectionView updates when view is being dismissed

I have recently reported a crash in my app, and I've found out what is happening and I need some help/best practices/best approach to this issue.
I have a pushed UICollectionViewController that on viewDidLoad queries the server to fetch some data to fill the UICollectionView.
My problem here is, if I push this UICollectionViewController and then tap the back button fast - the background thread still continues to fetch the server data, but when the data is fetched I update the UICollectionView with the performBatchUpdates() and my app crashes.
Here it happens because the app is attempting to reload data on a view that's not visible anymore.
What's the best practice here?
Is there any way to "abort" collection view updates if I'm moving back to the previous VC?
something like:
if self.isMovingFromParentViewController { /* abort any update here? */ }
Thanks
You can use DispatchWorkItem for achieving this as follows
let backgroundQueue = DispatchQueue.global()
var backgroundTask: DispatchWorkItem!
backgroundTask = DispatchWorkItem { [weak self] in
// Perform background task
if !backgroundTask.isCancelled {
return to main Queue
}
backgroundTask = nil // resolve strong reference cycle
}
backgroundQueue.async(execute: backgroundTask)
// When you want to cancel the task
backgroundQueue.async { [weak backgroundTask] in
backgroundTask?.cancel()
}
This is desirable in many cases where we should abort all the Server request. I prefer to perform all the clean up in the
deinit() {
// Abort all your APIs and asynchronous call
// Release all dependency
}
Along with this, always have a weak reference of your controllers and then perform optional binding in the response of the Asynchronous call.
Almofire.request(reqData: param, method: get.....) {
[weak self] response in
guard let safeSelfRef = self, let safeCollectionView =
safeSelfRef.collectionView else { return }
//Update view here
}

API calls blocks UI thread Swift

I need to sync web database in my coredata, for which I perform service api calls. I am using Alamofire with Swift 3. There are 23 api calls, giving nearly 24k rows in different coredata entities.
My problem: These api calls blocks UI for a minute, which is a long time for a user to wait.
I tried using DispatchQueue and performing the task in background thread, though nothing worked. This is how I tried :
let dataQueue = DispatchQueue.init(label: "com.app.dataSyncQueue")
dataQueue.async {
DataSyncController().performStateSyncAPICall()
DataSyncController().performRegionSyncAPICall()
DataSyncController().performStateRegionSyncAPICall()
DataSyncController().performBuildingRegionSyncAPICall()
PriceSyncController().performBasicPriceSyncAPICall()
PriceSyncController().performHeightCostSyncAPICall()
// Apis which will be used in later screens are called in background
self.performSelector(inBackground: #selector(self.performBackgroundTask), with: nil)
}
An API call from DataSyncController:
func performStateSyncAPICall() -> Void {
DataSyncRequestManager.fetchStatesDataWithCompletionBlock {
success, response, error in
self.apiManager.didStatesApiComplete = true
}
}
DataSyncRequestManager Code:
static func fetchStatesDataWithCompletionBlock(block:#escaping requestCompletionBlock) {
if appDelegate.isNetworkAvailable {
Util.setAPIStatus(key: kStateApiStatus, with: kInProgress)
DataSyncingInterface().performStateSyncingWith(request:DataSyncRequest().createStateSyncingRequest() , withCompletionBlock: block)
} else {
//TODO: show network failure error
}
}
DataSyncingInterface Code:
func performStateSyncingWith(request:Request, withCompletionBlock block:#escaping requestCompletionBlock)
{
self.interfaceBlock = block
let apiurl = NetworkHttpClient.getBaseUrl() + request.urlPath!
Alamofire.request(apiurl, parameters: request.getParams(), encoding: URLEncoding.default).responseJSON { response in
guard response.result.isSuccess else {
block(false, "error", nil )
return
}
guard let responseValue = response.result.value else {
block (false, "error", nil)
return
}
block(true, responseValue, nil)
}
}
I know many similar questions have been already posted on Stackoverflow and mostly it is suggested to use GCD or Operation Queue, though trying DispatchQueues didn't work for me.
Am I doing something wrong?
How can I not block UI and perform the api calls simultaneously?
You can do this to run on a background thread:
DispatchQueue.global(qos: .background).async {
// Do any processing you want.
DispatchQueue.main.async {
// Go back to the main thread to update the UI.
}
}
DispatchQueue manages the execution of work items. Each work item submitted to a queue is processed on a pool of threads managed by the system.
I usually use NSOperationQueue with Alamofire, but the concepts are similar. When you set up an async queue, you allow work to be performed independently of the main (UI) thread, so that your app doesn't freeze (refuse user input). The work will still take however long it takes, but your program doesn't block while waiting to finish.
You really have only put one item into the queue.
You are adding to the queue only once, so all those "perform" calls wait for the previous one to finish. If it is safe to run them concurrently, you need to add each of them to the queue separately. There's more than one way to do this, but the bottom line is each time you call .async {} you are adding one item to the queue.
dataQueue.async {
DataSyncController().performStateSyncAPICall()
}
dataQueue.async {
DataSyncController(). performRegionSyncAPICall l()
}

iOS best way to download bulk images

In my app, I have to download nearly 500 images as I open a ViewController. Downloading all 500 images at a time is not a right idea. I'd like to keep 5 active asynchronous downloads at a time. When any one in five is completed, it should start the next.
I also have a refresh control which will restart downloading all the images from first.
Which technique I could go for to implement this modal?
Here is what I tried so far,
Semaphore is created in property declaration
private var semaphore = dispatch_semaphore_create(5)
After getting web service response,
private func startDownloadingImages() {
for place in places {
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER)
self.downloadImageForPlace(place)
}
}
private func downloadImageForPlace(place: Place) {
ApplicationControls.getImageForPlace(place, withCompletion: { (image, error) -> () in
// error checks
dispatch_async(dispatch_get_main_queue(), {
// UI update
dispatch_semaphore_signal(self.semaphore)
})
})
}
But when I tap refresh control, app locks at dispatch_semaphore_wait and I could able to find a way to reset semaphore.
I would use an OperationQueue like this
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 5;
for url in imageUrls {
queue.addOperationWithBlock { () -> Void in
let img1 = Downloader.downloadImageWithURL(url)
NSOperationQueue.mainQueue().addOperationWithBlock({
//display the image or whatever
})
}
}
you can stop your operations with this
queue.cancelAllOperations();
and then just restart the whole thing.
The only thing that you have to change is that your requests have to be synchronous then. Because this approach wont work with callbacks.

Is it normal that CPU usage exceeds 100% using dispatch async in Xcode 7

I'm a beginner in swift 2, and I'm trying to make my program blocks while showing only a progress spinner until some operation finishes, I made that code snippet in a button with the action "touch up inside", my problem is that while debugging,Xcode 7 CPU usage jumps to 190 % once I tap my button and keeps high until the flag changes its value, Is it normal that CPU usage jumps like that?, also Is it a good practice to use the following snippet or shud i use sleep or some other mechanism inside my infinite loop?
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(self.queue2) { () -> Void in
while(flag == true)
{
//wait until flag sets to false from previous func
}
self.dispatch_main({
//continue after the flag became false
})
This is a very economical completion handler
func test(completion:() -> ())
{
// do hard work
completion()
}
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
}
}
or with additional dispatch to the main queue to update the UI
let queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue2) {
test() {
print("completed")
dispatch_async(dispatch_get_main_queue()) {
// update UI
}
}
}
This is totally wrong approach as you are using while loop for waiting. You should use Completion Handler to achieve this kind of stuff.
Completion handlers are callbacks that allow a client to perform some action when a framework method or function completes its task. Often the client uses a completion handler to free state or update the user interface. Several framework methods let you implement completion handlers as blocks (instead of, say, delegation methods or notification handlers).
Refer Apple documentation for more details.
I suppose you have a sort of class which manages these "some operation finishes".
When you finish your operations you can comunicate by completion handler or delegation. In the meanwhile you can disable the user interaction of your UI until the end of these operations.
If you provide more informations about your background operations I can add some snippets of code.

Swift changing text of a label after downloading content of a web page

I have this code and I want to change content of a label with a text from web
var url = NSURL(string: "SOME_URL");
var task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: {
(data, response, error) -> Void in
if error == nil {
self.label.text = "SOME_TEXT" // not working
dispatch_async(dispatch_get_main_queue()){
self.label.text = "SOME_TEXT" // working
}
}
else{
println("Error")
}
})
task.resume()
Why do I have to use dispatch_async(dispatch_get_main_queue()){ ... } to change the content of the label ?
The principe of iOS: you have only 1 thread that can modify your UI. It's called UI Thread. Whenever you want to change the UI content, all functions that change your UI content must be called in the UI Thread. In your case, the handler is executed in a background thread, hence you have to put the self.label.text = "SOME_TEXT" in the UI Thread.
It's a very common pattern in GUI programming.
Basically the main message loop runs single threaded without the cost of synchronizing everything. That means that if you want to interact with your window you need to do it on the main GUI thread -- that's what dispatch does, it posts a message on the message queue and the loop interprets it, bringing your code on its thread.
It's both for simplicity and for performance.

Resources