I am working with AWS S3 in an iOS app utilizing Swift 6. I’ve a singleton class AWSS3Manager that handles importing and deleting photographs, movies, and different recordsdata. Nonetheless, after migrating to Swift 6, I am experiencing random crashes when importing or deleting a number of photographs. The difficulty appears to be associated to completion blocks being referred to as inappropriately or a number of occasions, particularly throughout picture add or when iterating by means of uploadImages.
@MainActor
class AWSS3Manager{
static let shared = AWSS3Manager() // 4
non-public init () {
initializeS3()
}
let bucketName = "Bucket_Name" //5
func initializeS3() {
if S3Key == "" || S3SecretKeyName == "" {
// if let topVC = UIApplication.topViewController() {
// topVC.showError("One thing went unsuitable!")
// }
debugPrint("AWS initialisation Error")
return
}
let credentials = AWSStaticCredentialsProvider(accessKey: S3Key, secretKey: S3SecretKeyName)
let configuration = AWSServiceConfiguration(area: AWSRegionType.USEast1, credentialsProvider: credentials)
AWSServiceManager.default().defaultServiceConfiguration = configuration
}
func uploadImages(photographs: [UIImage], paths: [String], completion: @escaping(_ response: Any?, _ error: Error?) -> Void){
if photographs.rely == 0 || InternetConnectionManager.isConnectedToNetwork() == false {
completion(nil, nil)
}
if AWSServiceManager.default().defaultServiceConfiguration == nil {
initializeS3()
let error = NSError(area: "AWS", code: 1001, userInfo: [NSLocalizedDescriptionKey: "AWS is not initialized."])
completion(nil, error)
return
}
var counter: Int = 0
photographs.enumerated().forEach { i,picture in
let imageName: String = String(paths[i].cut up(separator: "/").final ?? "(UUID().uuidString).jpeg")
if let imageData = picture.jpegData(compressionQuality: 0.3),
let localUrl = attempt? saveDataToTempFolder(information: imageData, fileName: imageName){
self.uploadfile(fileUrl: localUrl, fileName: paths[i], contenType: "picture", progress: nil) { response, error in
counter += 1
if counter == paths.rely {
completion(nil, error)
}
}
}else{
counter += 1
if counter == paths.rely {
completion(nil, nil)
}
}
}
}
func deleteImage(path: String){
if AWSServiceManager.default().defaultServiceConfiguration == nil {
initializeS3()
if let topVC = UIApplication.topViewController() {
topVC.showError("One thing went unsuitable!")
}
return
}
let s3 = AWSS3.default()
guard let deleteObjectRequest = AWSS3DeleteObjectRequest() else {
return
}
deleteObjectRequest.bucket = S3BucketName
deleteObjectRequest.key = path
s3.deleteObject(deleteObjectRequest).continueWith { (process:AWSTask) -> AnyObject? in
if let error = process.error {
debugPrint("Error occurred: (error)")
return nil
}
debugPrint("Deleted efficiently.")
return nil
}
}
func deleteAllImagesForUser(userID: String) {
if AWSServiceManager.default().defaultServiceConfiguration == nil {
initializeS3()
if let topVC = UIApplication.topViewController() {
topVC.showError("One thing went unsuitable!")
}
return
}
let s3 = AWSS3.default()
let folderPath = "Receipts/(userID)/"
let listObjectsRequest = AWSS3ListObjectsRequest()
listObjectsRequest?.bucket = S3BucketName
listObjectsRequest?.prefix = folderPath
s3.listObjects(listObjectsRequest!).continueWith { (process) -> AnyObject? in
if let error = process.error {
debugPrint("Error occurred whereas itemizing objects: (error)")
return nil
}
if let listObjectsOutput = process.outcome, let contents = listObjectsOutput.contents {
for object in contents {
let deleteObjectRequest = AWSS3DeleteObjectRequest()
deleteObjectRequest?.bucket = S3BucketName
deleteObjectRequest?.key = object.key
s3.deleteObject(deleteObjectRequest!).continueWith { (deleteTask) -> AnyObject? in
if let error = deleteTask.error {
debugPrint("Error occurred whereas deleting object (object.key ?? ""): (error)")
} else {
debugPrint("Deleted (object.key ?? "") efficiently.")
}
return nil
}
}
} else {
debugPrint("No objects present in folder (folderPath)")
}
return nil
}
}
// Add video from native path url
func uploadVideo(videoUrl: URL, progress: progressBlock?, completion: completionBlock?) {
let fileName = self.getUniqueFileName(fileUrl: videoUrl)
self.uploadfile(fileUrl: videoUrl, fileName: fileName, contenType: "video", progress: progress, completion: completion)
}
// Add auido from native path url
func uploadAudio(audioUrl: URL, progress: progressBlock?, completion: completionBlock?) {
let fileName = self.getUniqueFileName(fileUrl: audioUrl)
self.uploadfile(fileUrl: audioUrl, fileName: fileName, contenType: "audio", progress: progress, completion: completion)
}
// Add recordsdata like Textual content, Zip, and so on from native path url
func uploadOtherFile(fileUrl: URL, conentType: String, progress: progressBlock?, completion: completionBlock?) {
let fileName = self.getUniqueFileName(fileUrl: fileUrl)
self.uploadfile(fileUrl: fileUrl, fileName: fileName, contenType: conentType, progress: progress, completion: completion)
}
// Get distinctive file identify
func getUniqueFileName(fileUrl: URL) -> String {
let strExt: String = "." + (URL(fileURLWithPath: fileUrl.absoluteString).pathExtension)
return (ProcessInfo.processInfo.globallyUniqueString + (strExt))
}
//MARK:- AWS file add
// fileUrl : file native path url
// fileName : identify of file, like "myimage.jpeg" "video.mov"
// contenType: file MIME sort
// progress: file add progress, worth from 0 to 1, 1 for 100% full
// completion: completion block when uplaoding is end, you'll get S3 url of add file right here
non-public func uploadfile(fileUrl: URL, fileName: String, contenType: String, progress: progressBlock?, completion: completionBlock?) {
if AWSServiceManager.default().defaultServiceConfiguration == nil {
initializeS3()
if let topVC = UIApplication.topViewController() {
topVC.showError("One thing went unsuitable!")
}
let error = NSError(area: "AWS", code: 1001, userInfo: [NSLocalizedDescriptionKey: "AWS is not initialized."])
completion?(nil, error)
return
}
// Add progress block
let expression = AWSS3TransferUtilityUploadExpression()
expression.progressBlock = {(process, awsProgress) in
guard let uploadProgress = progress else { return }
DispatchQueue.major.async {
uploadProgress(awsProgress.fractionCompleted)
}
}
// Completion block
var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
completionHandler = { (process, error) -> Void in
DispatchQueue.major.async(execute: {
if error == nil {
let url = AWSS3.default().configuration.endpoint.url
let publicURL = url?.appendingPathComponent(self.bucketName).appendingPathComponent(fileName)
let presignedURL = self.getPreSignedURL(S3DownloadKeyName: fileName)
if let completionBlock = completion {
completionBlock(fileName, nil)
}
} else {
if let completionBlock = completion {
completionBlock(nil, error)
}
}
})
}
// Begin importing utilizing AWSS3TransferUtility
let awsTransferUtility = AWSS3TransferUtility.default()
awsTransferUtility.uploadFile(fileUrl, bucket: bucketName, key: fileName, contentType: contenType, expression: expression, completionHandler: completionHandler).continueWith { (process) -> Any? in
if let error = process.error {
debugPrint("error is: (error.localizedDescription)")
}
if let _ = process.outcome {
// your uploadTask
}
return nil
}
}
}