Jack Jack - 7 days ago 5
Swift Question

Swift - Complete action after NSURLSession resumes

I am trying to complet an an action after the URLSession resumes.

So I am downloading several images from my server with the url, which all works good. But now I am trying to save those images to the disk after I have finished downloading them.

Problem

Now I can save them inside the same query while downloading them but I would prefer not too as it makes my query slower.

So I have added a completion handler to my func with the query, but when I save the images to the disk in that block it works but I cannot do anything with my screen as the query has not resumed yet it is blocked from touches I guess...

Now I would like to be able to call my func to save the images to the disk straight after the query has been resumed.... Anyone have any idea?

If someone needs more explanation or to see code just drop a comment below

Many thanks in advance to anyone that can help!

Code for downloading

func loadPosts(completionHandler: @escaping (Bool) -> ()) {

pageNumber = 1
appDelegate.setNetworkActivityIndicatorVisible(true)

let id = user!["id"] as! String
let url = URL(string: "http://************/Files/Posts.php")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
let body = "id=\(id)&caption=&uuid=&page="
request.httpBody = body.data(using: String.Encoding.utf8)
let task = URLSession.shared.dataTask(with: request, completionHandler: { (data:Data?, response:URLResponse?, error:Error?) in

DispatchQueue.global(qos: .background).async {
if error == nil {

let oldImageArray = self.cellContentArray
self.cellContentArray.removeAll(keepingCapacity: false)


do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? NSDictionary

guard let parseJSON = json else {
print("Error while parsing")
return
}
guard let posts = parseJSON["Posts"] as? [AnyObject] else {
print("Error while parseJSONing")
return
}
for element in posts {
// here I download the stuff and it to my Array, too long and no point to show here

}

let oldImageSet = Set(oldImageArray.map({return $0.uuid}))
let newImageSet = Set(self.cellContentArray.map({return $0.uuid}))
let addedImages = newImageSet.subtracting(oldImageSet)
let addedImageSections = Array(addedImages).map{ self.cellContentArray.map({return $0.uuid}).index(of: $0)! }
let addedImageIndexSet = IndexSet(addedImageSections)
let removedImages = oldImageSet.subtracting(newImageSet)
let removedImageSections = Array(removedImages).map{ oldImageArray.map({return $0.uuid}).index(of: $0)! }
let removedImageIndexSet = IndexSet(removedImageSections)


if !addedImageIndexSet.isEmpty {
if oldImageArray.count >= 5 {
self.lastUUIDImage = oldImageArray[4].uuid
} else {

}
self.coreDataShit()
}
DispatchQueue.main.async{
print(placeholderImage.count)
if placeholderImage.count > 5 {
placeholderImage.removeFirst(placeholderImage.count - 5)
}
print("finished")
self.customView.isHidden = true
if posts.count >= 5 {
self.tableView.addInfiniteScroll { [weak self] (scrollView) -> Void in
self?.loadMore()
}}
self.activityView.stopAnimating()
self.internetView.removeFromSuperview()



self.tableView.beginUpdates()
if !addedImageIndexSet.isEmpty {
self.tableView.insertSections(addedImageIndexSet, with: .top)
}
if !removedImageIndexSet.isEmpty {
self.tableView.deleteSections(removedImageIndexSet, with: .bottom)
}
self.tableView.endUpdates()
self.tableView.finishInfiniteScroll()
self.refresher.endRefreshing()
appDelegate.setNetworkActivityIndicatorVisible(false)
completionHandler(true)

}
} catch {
DispatchQueue.main.async {
self.tableView.removeInfiniteScroll()
self.customView.isHidden = false
self.refresher.endRefreshing()
self.tableView.reloadData()
}
}


} else {
DispatchQueue.main.async(execute: {
let message = error!.localizedDescription
appDelegate.infoView(message: message, color: smoothRedColor)
})
}
}
})
task.resume()

}


Saving Image

self.loadPosts(completionHandler: { (true) in
print("completion")

let sections = self.tableView.numberOfSections
for i in 0..<sections {
self.rows += self.tableView.numberOfRows(inSection: i)
}

print(self.rows)

if self.rows <= 5 {
print("less than 5")
print(self.rows)
var i = 0

for element in self.cellContentArray {

let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let dirPath = "\(path)/images"
let url = NSURL(fileURLWithPath: dirPath)
let filePath = url.appendingPathComponent("\(element.uuid).jpg")?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
print("File exsists")

} else {
print("File doesn't exsist")
DispatchQueue.main.async {

let url = NSURL(string: element.fullImage!)! // convert path str to url
let imageData = NSData(contentsOf: url as URL) // get data via url and assigned imageData
let imageName = element.uuid
let saveImages = FileSaveHelper(fileName: imageName, fileExtension: .JPG, subDirectory: "images", directory: .documentDirectory)
do {
guard let image = UIImage.sd_image(with: imageData as Data!) else {
print("Error getting image")
return
}
try saveImages.saveFile(image: image)
self.saveNewImagePath(imageLink: imageName, uuid: imageName)
self.removeImage(itemName: "file\(i)", fileExtension: "jpg")
self.removeImage(itemName: self.lastUUIDImage, fileExtension: "jpg")

i += 1

} catch {
print(error)
}
}
}

}
}

})


Image in tableView Cell

self.postImage.sd_setImage(with: URL(string: content.fullImage!), placeholderImage: placeHolder, options: .retryFailed) { (image:UIImage?, error:Error?, cached:SDImageCacheType, url:URL?) in

}

Answer

In your code of saving image this line of code is blocking you UI

let imageData = NSData(contentsOf: url as URL) // get data via url and assigned imageData

This is not proper way to download image from server, you should download image asynchronously using URLSession

Comments