AWSS3 Mobile SDK cancel file download in swift - ios

I have a file download code in my application using AWS Mobile SDK.
That shows an alert with download progress and a cancel button. It is working but I need to cancel download when user hit on alert cancel button. I have tried to cancel the task using the object of AWSS3TransferUtilityDownloadTask and AWSS3TransferUtilityTask.But it seems not working it gives me progress and download completion.
here Is my working code of file download.
Please suggest me how can I Cancel my Download request.
let credentialProvider = AWSStaticCredentialsProvider(accessKey: "MyAccessKey", secretKey: "MySecretKey")
let configuration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: credentialProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let expression = AWSS3TransferUtilityDownloadExpression()
expression.progressBlock = {(task, progress) in DispatchQueue.main.async(execute: {
// Do something e.g. Update a progress bar.
print("File Progress:",Float(progress.fractionCompleted))
let progress = Float(progress.fractionCompleted)
DispatchQueue.main.async {
if progress == 1.0 {
self.globalAlert.dismiss(animated: true, completion: nil)
}else{
self.globalProgressView!.progress = progress
self.msgProgress = String(Int(progress*100))
}
}
})
}
var completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock?
completionHandler = { (task, URL, data, error) -> Void in
DispatchQueue.main.async(execute: {
// Do something e.g. Alert a user for transfer completion.
// On failed downloads, `error` contains the error object.
//My other code stuff execution
})
}
var refUploadTask: AWSS3TransferUtilityTask?
let transferUtility = AWSS3TransferUtility.default()
transferUtility.downloadData(
fromBucket: Bucket_Name,
key: fileKey,
expression: expression,
completionHandler: completionHandler
).continueWith {
(task) -> AnyObject? in if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let _ = task.result {
// Do something with downloadTask.
print("download started..")
DispatchQueue.main.async(execute: {
self.globalAlert = UIAlertController(title: "Downloading...", message: "\(String(describing: self.msgProgress))% Completed", preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertAction.Style.default, handler: {
(action : UIAlertAction!) -> Void in
self.refUploadTask.cancel()
})
// Show it to your users
self.globalAlert.addAction(cancelAction)
self.present(self.globalAlert, animated: true, completion: {
// Add your progressbar after alert is shown (and measured)
let margin:CGFloat = 8.0
let rect = CGRect(x: margin, y: 72.0, width: self.globalAlert.view.frame.width - margin * 2.0 , height: 2.0)
self.globalProgressView = UIProgressView(frame: rect)
self.globalProgressView!.tintColor = .blue
self.globalAlert.view.addSubview(self.globalProgressView!)
})
})
}
return nil;
}

You can get the AWSS3TransferUtilityDownloadTask object and cancel the ongoing download.
let awsTask = transferUtility.getDownloadTasks()
if let taskArray = awsTask.result {
for idx in taskArray {
if let task = idx as? AWSS3TransferUtilityDownloadTask {
task.cancel()
}
}
}

Related

dispatchgroup executes task in different order in testflight compared to simulator

So my goal is to have congruent functionality both on the iOS simulator in Xcode and as well as a physical device on TestFlight. So currently, I have a function that handles refunds in my app. On the simulator the function runs perfectly fine in the order I expect it to, but the print statements execute in the wrong order which I'm assuming is the reason for misbehaviour on TestFlight simulations.
Here is the method:
#IBAction func cancelPurchasePressed(_ sender: UIButton) {
guard let nameOfEvent = selectedEventName else { return }
guard let user = Auth.auth().currentUser else { return }
let alertForCancel = UIAlertController(title: "Cancel Purchase", message: "Are you sure you want to cancel your purchase of a ticket to \(nameOfEvent)? You will receive full reimbursement of what you paid within 5 - 10 days.", preferredStyle: .alert)
let cancelPurchase = UIAlertAction(title: "Cancel Purchase", style: .default) { (purchaseCancel) in
self.viewPurchaseButton.isHidden = true
self.cancelPurchaseButton.isHidden = true
self.refundLoading.alpha = 1
self.refundLoading.startAnimating()
self.makeRefundRequest()
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
let group = DispatchGroup()
self.db.collection("student_users/\(user.uid)/events_bought/\(nameOfEvent)/guests").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("The guests couldn't be fetched.")
return
}
guard querySnapshot?.isEmpty == false else {
print("The user did not bring any guests.")
return
}
for guest in querySnapshot!.documents {
let name = guest.documentID
group.enter()
self.db.document("student_users/\(user.uid)/events_bought/\(nameOfEvent)/guests/\(name)").delete { (error) in
guard error == nil else {
print("The guests couldn't be deleted.")
return
}
print("Guests deleted with purchase refund.")
group.leave()
}
}
}
group.notify(queue: .main) {
self.db.document("student_users/\(user.uid)/events_bought/\(nameOfEvent)").delete { (error) in
guard error == nil else {
print("Error trying to delete the purchased event.")
return
}
print("The purchased event was succesfully removed from the database!")
}
self.refundLoading.stopAnimating()
self.refundLoading.alpha = 0
self.ticketFormButton.isHidden = false
self.cancelPurchaseButton.isHidden = true
self.viewPurchaseButton.isHidden = true
}
}
}
alertForCancel.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alertForCancel.addAction(cancelPurchase)
present(alertForCancel, animated: true, completion: nil)
}
Basically what I have going on is a simple refund request being made to Stripe and a second after I have an asyncAfter code block with some database cleanup in it. I have to do the asyncAfter or else the refund request gets beat out by the other async tasks by speed.
So I took my knowledge of DispatchGroups and decided to implement it since I have an async task in a for loop that I need to be completed before every other task. So I expected this to work fine, despite the order of the print statements being incorrect, but when I ran the exact block of code on my phone via TestFlight, I made a refund and the cell was still showing up in the tableview, meaning the document wasn't deleted from the database properly.
I've been having some terrifying experience recently with DispatchGroups and TestFlight and I just honestly hope to fix all this and have these problems come to an end temporarily. Any suggestions on how I can fix this method to prevent incorrect order on TestFlight?
UPDATE Decided to use a completion handler instead to do the same functionality:
func makeRefundRequest(refundMade: #escaping ((Bool) -> ())) {
let backendURLForRefund = "https://us-central1-xxxxxx-41f12.cloudfunctions.net/createRefund"
getStripePaymentIntentID { (paymentid) in
guard let id = paymentid else { return }
let url = URL(string: backendURLForRefund)!
let json: [String: Any] = [
"payment_intent": id
]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: json)
let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
guard let taskError = error?.localizedDescription else { return }
guard let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data,
let _ = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
self?.showAlert(title: "Refund Request Error", message: "There was an error making the refund request. \(taskError)")
refundMade(false)
return
}
}
task.resume()
refundMade(true)
}
}
And then I just slapped this method in the actual refund process method itself:
#IBAction func cancelPurchasePressed(_ sender: UIButton) {
guard let nameOfEvent = selectedEventName else { return }
guard let user = Auth.auth().currentUser else { return }
let alertForCancel = UIAlertController(title: "Cancel Purchase", message: "Are you sure you want to cancel your purchase of a ticket to \(nameOfEvent)? You will receive full reimbursement of what you paid within 5 - 10 days.", preferredStyle: .alert)
let cancelPurchase = UIAlertAction(title: "Cancel Purchase", style: .default) { (purchaseCancel) in
self.viewPurchaseButton.isHidden = true
self.cancelPurchaseButton.isHidden = true
self.refundLoading.alpha = 1
self.refundLoading.startAnimating()
self.makeRefundRequest { (response) in
if response == false {
return
} else {
let group = DispatchGroup()
self.db.collection("student_users/\(user.uid)/events_bought/\(nameOfEvent)/guests").getDocuments { (querySnapshot, error) in
guard error == nil else {
print("The guests couldn't be fetched.")
return
}
guard querySnapshot?.isEmpty == false else {
print("The user did not bring any guests.")
return
}
for guest in querySnapshot!.documents {
let name = guest.documentID
group.enter()
self.db.document("student_users/\(user.uid)/events_bought/\(nameOfEvent)/guests/\(name)").delete { (error) in
guard error == nil else {
print("The guests couldn't be deleted.")
return
}
print("Guests deleted with purchase refund.")
group.leave()
}
}
}
group.notify(queue: .main) {
self.db.document("student_users/\(user.uid)/events_bought/\(nameOfEvent)").delete { (error) in
guard error == nil else {
print("Error trying to delete the purchased event.")
return
}
print("The purchased event was succesfully removed from the database!")
}
self.refundLoading.stopAnimating()
self.refundLoading.alpha = 0
self.ticketFormButton.isHidden = false
self.cancelPurchaseButton.isHidden = true
self.viewPurchaseButton.isHidden = true
}
}
}
}
alertForCancel.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alertForCancel.addAction(cancelPurchase)
present(alertForCancel, animated: true, completion: nil)
}
This actually does not work fine, yes the refund goes through on Stripe and the database is cleaned up for 3 minutes, but the print statements print in incorrect order and also the document magically reappears in the Firestore database 3 minutes after physically seeing it be deleted, how can I prevent this and make sure they print in correct order and execute in correct order to work properly on TestFlight? Is this an issue in my DispatchGroup implementation? Or is it something completely different?
I think it's important that no matter what you end up doing you should learn how to do everything anyway. I would advise against this approach below but it's what you originally started so let's finish it regardless, so you know how dispatch grouping works. Once you've gotten a handle on this, refine it by replacing the dispatch group with a Firestore transaction or batch operation. The point of the transaction or batch operation is so all of the documents are deleted atomically, meaning they all go or none go. This simplifies things greatly and they are very basic! And the documentation for them is very clear.
The final thing I would suggest is perhaps integrating some recursion, meaning that if something fails it can retry automatically. Recursive functions are also very basic so just learn how to write one in Playground first and then apply it here. Just take it one step at a time and you'll get it down within a day or two. But this is the first step so carefully read what I wrote and understand why I did what I did.
func makeRefundRequest(refundMade: #escaping (_ done: Bool) -> Void) {
getStripePaymentIntentID { (paymentid) in
guard let id = paymentid,
let url = URL(string: "https://us-central1-xxxxxx-41f12.cloudfunctions.net/createRefund") else {
refundMade(false) // always call completion when you exit this function
return
}
let json: [String: Any] = ["payment_intent": id]
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: json)
let task = URLSession.shared.dataTask(with: request) { [weak self] (data, response, error) in
if let response = response as? HTTPURLResponse,
response.statusCode == 200,
let data = data,
let _ = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
refundMade(true) // completion: true
} else {
if let error = error {
print(error)
}
refundMade(false) // completion: false
}
}
task.resume()
// do not call the completion of this function here because you just made a network call
// so call the completion of this function in that call's completion handler
}
}
#IBAction func cancelPurchasePressed(_ sender: UIButton) {
guard let nameOfEvent = selectedEventName,
let user = Auth.auth().currentUser else {
return
}
let alertForCancel = UIAlertController(title: "Cancel Purchase", message: "Are you sure you want to cancel your purchase of a ticket to \(nameOfEvent)? You will receive full reimbursement of what you paid within 5 - 10 days.", preferredStyle: .alert)
let cancelPurchase = UIAlertAction(title: "Cancel Purchase", style: .default) { (purchaseCancel) in
// put the UI in a loading state
self.viewPurchaseButton.isHidden = true
self.cancelPurchaseButton.isHidden = true
self.refundLoading.alpha = 1
self.refundLoading.startAnimating()
self.makeRefundRequest { (done) in
if done {
self.db.collection("student_users/\(user.uid)/events_bought/\(nameOfEvent)/guests").getDocuments { (snapshot, error) in
guard let snapshot = snapshot,
!snapshot.isEmpty else {
if let error = error {
print(error)
}
return
}
let group = DispatchGroup() // instatiate the dispatch group outside the loop
for doc in snapshot.documents {
group.enter() // enter on each loop iteration
let name = doc.documentID
self.db.document("student_users/\(user.uid)/events_bought/\(nameOfEvent)/guests/\(name)").delete { (error) in
if let error = error {
print(error)
}
group.leave() // leave no matter what the outcome, error or not
// what do you do when this document didn't delete?
// by doing all your deleting in a transaction or batch
// you can ensure that they all delete or none delete
}
}
group.notify(queue: .main) { // done with loop, make final network call
self.db.document("student_users/\(user.uid)/events_bought/\(nameOfEvent)").delete { (error) in
if let error = error {
print(error)
}
// put the UI back to normal state
self.refundLoading.stopAnimating()
self.refundLoading.alpha = 0
self.ticketFormButton.isHidden = false
self.cancelPurchaseButton.isHidden = true
self.viewPurchaseButton.isHidden = true
}
}
}
} else { // refund was not made, put the UI back into normal state
self.refundLoading.stopAnimating()
self.refundLoading.alpha = 0
self.ticketFormButton.isHidden = false
self.cancelPurchaseButton.isHidden = true
self.viewPurchaseButton.isHidden = true
}
}
}
alertForCancel.addAction(UIAlertAction(title: "Cancel", style: .cancel))
alertForCancel.addAction(cancelPurchase)
present(alertForCancel, animated: true, completion: nil)
}

After update my app from app store, my app cannot open

I try trigger new version available at app store, then i will redirect user to app store. Then inside there i update the app. After finish download, i click open inside app store then my app will open a splash screen then it will auto close. I not get any crash log. This is my code.
DispatchQueue.global().async {
do {
let update = try self.needsUpdate()
print("update",update)
DispatchQueue.main.async {
if update{
self.popupUpdateDialogue();
}
}
} catch {
print(error)
}
}
func popupUpdateDialogue(){
var versionInfo = ""
do {
versionInfo = try self.getAppStoreVersion()
}catch {
print(error)
}
let alertMessage = "Please update this app to version "+versionInfo;
let alert = UIAlertController(title: "New Version Available", message: alertMessage, preferredStyle: UIAlertControllerStyle.alert)
let okBtn = UIAlertAction(title: "Update", style: .default, handler: {(_ action: UIAlertAction) -> Void in
if let url = URL(string: "itms-apps://itunes.apple.com/sg/app/myApp/idxxxx"),
UIApplication.shared.canOpenURL(url){
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url)
}
}
})
let noBtn = UIAlertAction(title:"Skip this Version" , style: .destructive, handler: {(_ action: UIAlertAction) -> Void in
})
alert.addAction(okBtn)
alert.addAction(noBtn)
self.present(alert, animated: true, completion: nil)
}
func needsUpdate() -> Bool {
let infoDictionary = Bundle.main.infoDictionary
let appID = infoDictionary!["CFBundleIdentifier"] as! String
let url = URL(string: "http://itunes.apple.com/sg/lookup?bundleId=\(appID)")
let data = try? Data(contentsOf: url!)
let lookup = (try? JSONSerialization.jsonObject(with: data! , options: [])) as? [String: Any]
if let resultCount = lookup!["resultCount"] as? Int, resultCount == 1 {
if let results = lookup!["results"] as? [[String:Any]] {
if let appStoreVersion = results[0]["version"] as? String{
let currentVersion = infoDictionary!["CFBundleShortVersionString"] as? String
if !(appStoreVersion == currentVersion) {
print("Need to update [\(appStoreVersion) != \(currentVersion)]")
return true
}
}
}
}
return false
}
I really dont know why the app will auto terminate/close. I also already checked on this link App Crash when after updating, https://stackoverflow.com/questions/17795920/ios-app-goes-crash-on-startup-after-updating-from-the-app-store,https://stackoverflow.com/questions/15409323/ios-app-cannot-be-opened-after-update but still not get what solution for this issue.

S3 stop uploading files iOS

I have 2 application, one in swift and the other in objective-c, I'm using cognito to authenticate with s3 but the last days this feature stop working I down know why, the console of my application say "request timed out" but I don't change anything. the application start uploading but always stops in 39% or 14% and freeze the upload, this is my code:
App delegate:
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.USEast1,
identityPoolId:poolId)
AWSLogger.defaultLogger().logLevel = .Verbose
let configuration = AWSServiceConfiguration(region:.SAEast1, credentialsProvider:credentialsProvider)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration
PhotoViewController
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest.bucket = "bucket"
uploadRequest.ACL = AWSS3ObjectCannedACL.PublicRead
uploadRequest.key = "\(self.userLogin.customer_subdomain)/photos/\(imageName)"
uploadRequest.contentType = "image/png"
uploadRequest.body = url
uploadRequest.uploadProgress = { (bytesSent:Int64, totalBytesSent:Int64, totalBytesExpectedToSend:Int64) -> Void in
dispatch_sync(dispatch_get_main_queue(), {() -> Void in
self.updateLabel(totalBytesSent,totalBytesExpectedToSend: totalBytesExpectedToSend)
})
}
AWSS3TransferManager.defaultS3TransferManager().upload(uploadRequest).continueWithBlock { (task) -> AnyObject! in
if (task.error != nil) {
//failed
dispatch_async(dispatch_get_main_queue()) {
self.dismissViewControllerAnimated(false, completion: nil)
let refreshAlert = UIAlertController(title: "Error", message: "Error al intentar subir el archivo", preferredStyle: UIAlertControllerStyle.Alert)
refreshAlert.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self.presentViewController(refreshAlert, animated: true, completion: nil)
}
print(task.error!)
} else {
let dic=["file":"\(self.userLogin.customer_subdomain)/photos/\(imageName)"]
let jsonData = try! NSJSONSerialization.dataWithJSONObject(dic, options: [])
let jsonString = NSString(data: jsonData, encoding: NSUTF8StringEncoding)! as String
let con:URLConnection
con=URLConnection.init()
con.delegate=self
let headers=["X-Auth-Token":self.userLogin.api_token]
con.connect(self.userLogin.customer_url+"/rest/activities/\(self.activity.id)/photos",method:"POST",json: jsonString, headers: headers)
self.imgName = "\(self.userLogin.customer_subdomain)/photos/\(imageName)"
}
return nil
}
thank you!
was the datetime of the device, always need to be the correct hour

I am using activity indicator in closure one time it work correct for like but not for next

Kindly help me out for properly start activity indicator when button pressed and stop when process commpleted. My code is given below
startting animation
self.activityIndicator.startAnimating()
self.nextButton.enabled = false
self.activityIndicator.startAnimating()
self.newForMe()
self.finals = self.mids.filter { !self.vmids.contains($0) }
self.RandomNumber = Int(arc4random_uniform(UInt32(self.finals.count)))
let accesstoken = self.defaults.valueForKey("accesstoken")
if self.finals.count != 0
{
//self.activityIndicator.startAnimating()
self.like1Button.enabled = true
self.imgView.hidden = false
self.vview.hidden = true
if self.vc.isConnectedToNetwork() {
let jSon2 = JSON(data: NSData(contentsOfURL: NSURL(string: "https://api.instagram.com/v1/media/\(self.finals[self.RandomNumber])?access_token=\(accesstoken!)")!)!, options: NSJSONReadingOptions.AllowFragments, error: nil)
self.st = jSon2["data"]["images"]["low_resolution"]["url"].stringValue
self.firstCardString = NSURL(string: self.st)!
let con = jSon2["data"]["user_has_liked"].boolValue
if con == true {
self.nextPressed(self)
}
}else {
// print("No Connectivity")
}
// print(con)
let data = NSData(contentsOfURL: self.firstCardString)
if data != nil
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.imgView.image = UIImage(data: data!)
dispatch_async(dispatch_get_main_queue(), {
self.activityIndicator.stopAnimating()
})
})
}
self.nextButton.enabled = true
}
stoping animation
I am astoping it here then also it not starting exectly
else {
self.like1Button.enabled = false
self.imgView.image = UIImage(named: "download (1)")
let alert = UIAlertController(title: "No Photo to like", message: "There is no photo to like!!", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { (UIAlertAction) -> Void in
// self.like1Button.hidden = true
self.imgView.image = nil
self.activityIndicator.stopAnimating()
}))
self.presentViewController(alert, animated: true, completion: nil)
}

How to use closures to return a JSON value from a NSURLSession

I am trying to return a result from a JSON object but unable to do so. I am new to Swift so kindly explain me how to do so. In the below code I want to return json_level_number in the return of function fetchNumberOfSections () where i have hard coded as 5 right now return 5. If i declare a variable json_level_number just above the reachability code it sort of solves the problem but then it is returning '0' for the first time. The API returns 2 each time.
Code as below:
func fetchNumberOfSections () -> Int {
if Reachability.isConnectedToNetwork() == true {
// Below code to fetch number of sections
var urlAsString = "http://themostplayed.com/rest/fetch_challenge_sections.php"
urlAsString = urlAsString+"?apiKey="+apiKey
print (urlAsString)
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
print(error!.localizedDescription)
}
do {
let jsonResult = (try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
let json_level_number: String! = jsonResult["level_number"] as! String
//
dispatch_async(dispatch_get_main_queue(), {
// self.dateLabel.text = jsonDate
// self.timeLabel.text = jsonTime
print(json_level_number)
// self.activityIndicatorStop()
})
}
catch let errorJSON {
print (errorJSON)
// alert box code below
let alert = UIAlertController(title: "JSON Error!", message:"Error processing JSON.", preferredStyle: .Alert)
let action = UIAlertAction(title: "OK", style: .Default) { _ in
// Put here any code that you would like to execute when
self.dismissViewControllerAnimated(true, completion: {})
}
alert.addAction(action)
self.presentViewController(alert, animated: true, completion: nil)
// alert box code end
}
})
jsonQuery.resume()
// End
}
else {
print("Internet connection FAILED")
self.performSegueWithIdentifier("nointernet", sender: nil)
}
return 5
}

Resources