Hi, I am now having problems viewing the correct pdf documents I have stored in local cash.
I have tried to describe the code below by functionality to help understand what I am doing. As background, I have written an app to track and store aircraft inspection and maintenance history including storage of documents tied to specific inspection records. The user can view the records and the documents attached and add and delete documents. The code works fine uploading pdf documents, attaching them to records and displaying the thumbnail image in a tableview. It also works fine deleting the selected thumbnail image and re-displaying the tableview. It was also working properly displaying the deleted pdf after clicking on the thumbnail image (but now it doesn’t seem to do that properly), it only displays the last document saved (in the .cacheDirectory within the .userDomain) when the thumbnail image is clicked on.
The pdf document data is downloaded (using the URL session in the extension function) from “cloud firestore” and converted into a thumbnail image to display in a tableview. The urls downloaded are saved into the “Document.passedUrl” array.
This part of the code is as per below:
func displayDoc() {//1
//check that there are documents to display
if Document.passedUrl == [] {//2
return
}//2
else {//3
//check if image is in cache
if let cachedImage = ImageCacheService.getImage(url: Document.urlPicked) {
// use the cached image
self.pdfDoc.image = cachedImage
//image found in cache so we no longer need to download the image
return
} else {
//image not found in cache so download the image
guard let url = URL(string: Document.urlPicked)
else {//5
return
}//5
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
//get the data
let downloadTask = urlSession.downloadTask(with: url)
guard let document = PDFDocument(url: url) else {
return
}
guard let page = document.page(at: 0) else {
return
}
let pageRect = page.bounds(for: .mediaBox)
let renderer = UIGraphicsImageRenderer(size: pageRect.size)
let image = renderer.image { ctx in
UIColor.white.set()
ctx.fill(CGRect(x: 0, y: 0, width: pageRect.width, height: pageRect.height))
ctx.cgContext.translateBy(x: -pageRect.origin.x, y: pageRect.size.height - pageRect.origin.y)
ctx.cgContext.scaleBy(x: 1.0, y: -1.0)
page.draw(with: .mediaBox, to: ctx.cgContext)
}//image rendere
//save image to cache using the PhotoImageCacheService
ImageCacheService.saveImage(url: url.absoluteString, image: image)
print("There are \(ImageCacheService.imageCache.count) thumbnails in cache")
DocumentCacheService.saveDoc(url: url)
//set the image view
//DispatchQueue.main.async {
self.pdfDoc.image = image
// }
downloadTask.resume()
}// else 3
}//else no cached image
}//display document 1
The pdf data is saved into the “.cacheDirectoy” in the “.userDoman” inside the extension function, as per below and I believe this is where the problem is:
extension DocumentTableViewCell: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
Document.pdfUrlArray = []
//create destination URL folder
//save the data
for url in DocumentCacheService.docCache {
let docsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationPath = docsPath.appendingPathComponent(url.lastPathComponent)
do {
try FileManager.default.removeItem(at: destinationPath)
} catch {
print("The file did not get deleted")
}
do {
try FileManager.default.copyItem(at: location, to: destinationPath)
Document.pdfUrlArray.append(destinationPath)
} catch let error {
print("Copy Error: \(error.localizedDescription)")
}//catchlet
} //for
}//func
When the user clicks on a thumbnail image they are taken to another view controller that displays the pdf document as per the code below:
class DocDetailViewController: UIViewController, WKUIDelegate {
@IBOutlet weak var docDetail: WKWebView!
var urlToDisplay: URL?
let userId = Auth.auth().currentUser?.uid
let db = Firestore.firestore()
override func viewDidLoad() {
super.viewDidLoad()
//docDetail.uiDelegate = self
if urlToDisplay != nil {
self.docDetail.loadFileURL(urlToDisplay!, allowingReadAccessTo: urlToDisplay!)
print("The document to display is \(urlToDisplay)")
}//if
}//func
NOTE: The “urlToDisplay” property, in the code above, is obtained from the didselectRowAt method as per below:
___________________________________-
if let detailvc = storyboard?.instantiateViewController(withIdentifier: “DocDetail”) as? DocDetailViewController {
detailvc.urlToDisplay = Document.pdfUrlArray[indexPath.row]
Document.docToDelete = Document.passedUrl[indexPath.row]
Document.urlIndexPath = indexPath.row
self.present(detailvc, animated: true, completion: nil)
}//iflet
The app gives the user the option of deleting the document from this second view controller as per the code below:
@IBAction func deleteDoc(_ sender: Any) {
let dialogMessage = UIAlertController(title: "Confirm", message: "Are you sure you want to delete the document as this action cannot be reversed!", preferredStyle: .alert)
//Create OK button with action handler
let ok = UIAlertAction(title: "Ok", style: .default, handler: { (action) -> Void in
//delete database reference
let docField = self.db.collection("Challenger").document("Engine Inspection & Maintenance Requirements").collection(self.userId!).document(PlaneInfo.plane).collection("\(EngFrReq.engFrReq[Document.num])").document(EngineHistory.passedDocId!)
docField.updateData(["Documents": FieldValue.arrayRemove([Document.docToDelete])]) { (error) in
if error != nil {
self.showMessage(message: "There was a problem deleting the document. Please try again. If the problem re-occurs, sgutdown and restart the app.")
} else {
return
}
}//updata data
ImageCacheService.deleteImage(url: Document.passedUrl[Document.urlIndexPath])
DocumentCacheService.deleteDOC(url: URL(string: Document.passedUrl[Document.urlIndexPath])!)
Document.passedUrl.remove(at: Document.urlIndexPath)
Document.pdfUrlArray.remove(at: Document.urlIndexPath)
//if Documents field is empty delete the Documents field as well
if Document.passedUrl == [] {
docField.updateData(["Documents" : FieldValue.delete()]) { (error) in
if error == nil {
return
} else {
self.showMessage(message: "There was a problem deleting the document. Please try again. If the problem re-occurs, close and restart the app.")
}
}//delete docfield
}//docfield is empty
//delete storage of document
let docRef = Storage.storage()
let deleteref = docRef.reference(forURL: "\(Document.docToDelete)")
deleteref.delete { (error) in
if error == nil {
return
} else {
self.showMessage(message: "There was a problem deleting the document. Please try again. If the problem re-occurs, close and restart the app.")
}
}//delete ref
self.performSegue(withIdentifier: "goBack", sender: self)
})
let cancel = UIAlertAction(title: "Cancel", style: .cancel) { (action) -> Void in
}
//Add ok and Cncel button to dialog message
dialogMessage.addAction(ok)
dialogMessage.addAction(cancel)
//present dialog message to user
self.present(dialogMessage, animated: true, completion: nil)
}
The “goBack” segue above takes the user back to the view controller that displays the thumbnail
images in the tableview. It has a listener to repopulate the cells when there is a database change as per below:
db.collection(“Challenger”).document(“Engine Inspection & Maintenance Requirements”).collection(self.userId!).document(PlaneInfo.plane).addSnapshotListener { (documentSnapshot, error) in
if error != nil || documentSnapshot == nil {
self.showMessage(message: "A problem occured. Please try again. If the problem persists, close and restart the app.")
} else {
let document = documentSnapshot
let source = document!.metadata.hasPendingWrites ? "Local" : "Server"
self.photoTable.reloadData()
self.docTableView.reloadData()
return
}
}//snapshot
When a thumbnail image is selected, the document displayed doesn’t match the thumbnail image. It appears to only display the last document saved.