How to open file in iOS? - ios

I'm trying to open a .pdf file after download which is downloaded with Alamofire. But I've seen only using a "webview". Thus the application consumes lots of memory and is not viable.
What I want is to open it with the native device application. Any suggestions? Thank you.
Edit: This is my code for download file:
var localPath: NSURL?
Alamofire.download(.GET, url, destination: { (temporaryURL, response) in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
localPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
return localPath!
})
.response { (request, response, _, error) in
if error != nil
{
// got an error in getting the data, need to handle it
print("Error: \(error!)")
}
//print(response)
print("Download file en:\(localPath!)")
self.view.hideToastActivity()
//self.actioncall()
}
}
I need open file from localpath...

You should use UIDocumentInteractionController. You can read about it on this Apple documentation page.
By doing some Googling you should see even some example implementations. For example here you can see some code about this done by "mattneub".
I let you one more code that you can use:
var documentInteractionController: UIDocumentInteractionController!
#IBAction func openDocument(sender: UIButton) {
let URL: NSURL = NSBundle.mainBundle().URLForResource("yourPDF", withExtension: "pdf")!
if (URL != "") {
// Initialize Document Interaction Controller
self.documentInteractionController = UIDocumentInteractionController(URL: URL)
// Configure Document Interaction Controller
self.documentInteractionController.delegate = self
// Present Open In Menu
self.documentInteractionController.presentOptionsMenuFromRect(sender.frame, inView: self.view, animated: true)
//presentOpenInMenuFromRect
}
}
// UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController {
return self
}

Related

UIDocumentInteractionController: cannot use/save files

In my app, I display some remote images or PDF files and want to give the user the ability to download them. In order to do so, I try to save them locally first in .documentDirectory before opening a UIDocumentInteractionController to handle the file.
However, I am having an issue, which is that even if the action sheet opens fine and proposes all the expected options, in the end I can never use the file because of an error. Specifically:
If I try to use the file in a mail, the mail opens but empty,
If I try to use it in Whatsapp, I get an error saying "The item cannot be shared. Please selected a different item."
And if I choose "Save to Files", the files action sheet briefly opens but closes immediately afterwards with an error in the console saying: [ShareSheet] cancelled request - error: The operation couldn’t be completed. Invalid argument
Here is the code I use to cache the remote file, then to open it with UIDocumentInteractionController:
URLSession.shared.downloadTask(with: url) { localUrl, response, error in
if let localUrl = localUrl {
do {
let imageData = try Data(contentsOf: localUrl)
let d = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
if let docUrl = d?.appendingPathComponent(url.lastPathComponent) {
try imageData.write(to: docUrl)
self.download(docUrl)
}
} catch {
print("Oops: \(error)")
}
}
}.resume()
func download(_ url: URL) {
DispatchQueue.main.async {
let documentInteractionController = UIDocumentInteractionController()
documentInteractionController.delegate = self
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentOptionsMenu(from: self.view.frame, in: self.view, animated: true)
}
}
Thank you for your help
I can't tell you why it doesn't work with a UIDocumentInteractionController, but it does work with a UIActivityViewController.
private func download(_ url: URL)
{
DispatchQueue.main.async {
let avc = UIActivityViewController(activityItems: [url], applicationActivities: nil)
self.present(avc, animated: true, completion: nil)
}
}

How to load a JSON tableView and save data even on dismiss?

I currently have a button that opens a TableViewController and loads the data using JSON like the following:
private func JSON() {
print(facility)
guard let url = URL(string: "https://example/example/example"),
let sample = value1.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "example1=\(example)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
self.JStruct = try JSONDecoder().decode([exampleStruct].self,from:data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}
Then after I am done looking at the tableview I close it by doing:
self.dismiss(animated: true, completion: nil)
using a BarButtonItem.
The issue is every time the UIView opens it takes some time to load the data. Is there anyway to have the tableView load just once and when dismissed and re-opened just have the same data show that was already loaded before?
The best thing you can do is to store the data locally. Either use a local database or a plain text file to store the data. When you open the page check whether data is already present. If it is already present load it, and call the API in background silently to update the existing data. If data is not saved, call the API, load the data and save it locally.
func getFileURL() -> URL {
let fileName = "CacheData"
let documentDirURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = documentDirURL.appendingPathComponent(fileName).appendingPathExtension("json")
return fileURL
}
func createFile(data: Data) {
let fileURL = getFileURL()
do {
try data.write(to: fileURL)
} catch let e {
print(e.localizedDescription)
}
}
func loadData() -> Data? {
let fileURL = getFileURL()
do {
let data = try Data(contentsOf: fileURL)
return data
} catch let e {
print(e.localizedDescription)
}
return nil
}
In your viewDidLoad method do something like:
let fileURL = getFileURL()
if FileManager.default.fileExists(atPath: fileURL.path) {
if let data = loadData() {
do {
self.JStruct = try
JSONDecoder().decode([exampleStruct].self,from:data)
DispatchQueue.main.async {
self.tableView.reloadData()
} catch {
print(error)
}
}
}
JSON()
And call the createFile when you get data from the API. You may need to write the file and load the file using a background queue to avoid overloading and freezing of your main thread.

Present preview of downloaded file directly in app

My application have file download option which downloads the file using alamofire download method. When the download completes i need to present the preview of the file without saving it to internal/cloud storage. How can i achieve this whatsapp like function that shows the preview after downloading the file.
func downloadFile(fileUrl: URL) {
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
Alamofire.download(fileUrl, to: destination)
.response(completionHandler: { (downloadResponse) in
self.dic.url = downloadResponse.destinationURL
self.dic.uti = downloadResponse.destinationURL!.uti
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
self.dic.presentOpenInMenu(from: rect, in: self.view, animated: true)
})
}
To present a preview of a file use Apple’s QuickLook framework that lets you embed previewing for a huge range of file types, including iWork documents, Microsoft Office documents, PDFs, images, and more, all without writing much code.
First, import the QuickLook framework, then make your view controller conform to the QLPreviewControllerDataSource protocol.
Reference:
https://www.hackingwithswift.com/example-code/libraries/how-to-preview-files-using-quick-look-and-qlpreviewcontroller
https://github.com/gargsStack/QLPreviewDemo
https://www.appcoda.com/quick-look-framework/
Code:
class ViewController: UIViewController {
var previewItem = URL!
func downloadFile(fileUrl: URL) {
let destination = DownloadRequest.suggestedDownloadDestination(for: .documentDirectory)
Alamofire.download(fileUrl, to: destination)
.response(completionHandler: { (downloadResponse) in
let previewController = QLPreviewController()
previewController.dataSource = self
self.previewItem = downloadResponse.destinationURL
self.present(previewController, animated: true, completion: nil)
})
}
}
extension ViewController: QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.previewItem as QLPreviewItem
}
}
Here is one solution using Alamofire. Someone may help.
Steps:
Alamofire has excellent staff to direct download and also save/write
your file into disc.
Return a path where downloaded file saved.
Using UIDocumentInteractionController pass the file path
Then present this view
import Alamofire
extension UIViewController : UIDocumentInteractionControllerDelegate{
func downloadFileForPreview(fileName: String, fileExtension: String, filePath: String ) {
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileWithExtension = "file.\(fileExtension)"
let fileURL = documentsURL.appendingPathComponent(fileWithExtension)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
//UtilitySwift.showUniversalLoadingView(true)
Alamofire.download(filePath, to: destination).response { response in
debugPrint(response)
//UtilitySwift.showUniversalLoadingView(false)
if response.error == nil, let storeFilePath = response.destinationURL?.path {
//let image = UIImage(contentsOfFile: imagePath)
self.previewDocument(withFilePath: response.destinationURL)
print(storeFilePath)
}
else{
UtilitySwift.showErrorMessage(message: response.error?.localizedDescription ?? "Error occured when downloading" )
print(response.error?.localizedDescription ?? "")
}
}
}
// Converted to Swift 5.1 by Swiftify v5.1.29672 - https://objectivec2swift.com/
func previewDocument(withFilePath filePath: URL?) {
var documentInteractionController: UIDocumentInteractionController?
if filePath != nil {
// Initialize Document Interaction Controller
if let filePath = filePath {
documentInteractionController = UIDocumentInteractionController(url: filePath)
}
// Configure Document Interaction Controller
documentInteractionController?.delegate = self as UIDocumentInteractionControllerDelegate
//if not possible to open
if !documentInteractionController!.presentPreview(animated: true) {
documentInteractionController?.presentOptionsMenu(from: CGRect.zero, in: self.view, animated: true)
}
} else {
// error
print("file preview error")
}
}
//UIDocumentInteractionControllerDelegate
public func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
self
}
}
Call from any UIViewController
self.downloadFileForPreview(fileName: "file", fileExtension: fileExt ?? "", filePath: REPLACE_WITH_DOWNLOAD_URL)

How download file with SwiftyDropbox? Error with path

I'm trying to download a file with SwiftyDropbox but I have problemas with the path. I have a file in mi Dropbox "prueba.txt":
Dropbox file
And this is the code that I use to download in my app.
import UIKit
import SwiftyDropbox
let clientDB = DropboxClientsManager.authorizedClient
class Controller: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
DropboxClientsManager.authorizeFromController(UIApplication.shared, controller: self, openURL: {
(url: URL) -> Void in UIApplication.shared.open(url)
})
let fileManager = FileManager.default
let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destURL = directoryURL.appendingPathComponent("/test.txt")
let destination: (URL, HTTPURLResponse) -> URL = { temporaryURL, response in
return destURL
}
clientDB?.files.download(path: "/prueba.txt", overwrite: true, destination: destination)
.response{ response, error in
if response != nil{
self.cargarDatosCliente()
//print (response)
} else if let error = error{
print (error)
}
}
.progress{ progressData in
print(progressData)
}
}
}
I try different ways but always obtain the same problem with "path", always the error is path/not_found/...
I try with other path but is the same problem.
Could you help me? Where is my mistake?
Thanks!
The problem is that "/prueba.txt" is a local file path. Dropbox expects you to give it a file path for their remote server.
You can retrieve those by using listFolder and listFolderContinue.
For example, if you want to retrieve the file paths in the root folder of your app or dropbox use:
var path = ""
clientDB?.files.listFolder(path: path).response(completionHandler: { response, error in
if let response = response {
let fileMetadata = response.entries
if response.hasMore {
// Store results found so far
// If there are more entries, you can use `listFolderContinue` to retrieve the rest.
} else {
// You have all information. You can use it to download files.
}
} else if let error = error {
// Handle errors
}
})
The fileMetadata contains the path you need. For example, you can get the path to the first file like this:
let path = fileMetadata[0].pathDisplay
If you're getting metadata about files from the API, this would be the "pathLower" property of a FileMetadata object.
client?.files.download(path: fileMetadata.pathLower!, overwrite: true, destination: destination)
.response { response, error in
if let response = response {
print(response)
} else if let error = error {
print(error)
}
}

Opening apps for corresponding file type [duplicate]

I'm trying to open a .pdf file after download which is downloaded with Alamofire. But I've seen only using a "webview". Thus the application consumes lots of memory and is not viable.
What I want is to open it with the native device application. Any suggestions? Thank you.
Edit: This is my code for download file:
var localPath: NSURL?
Alamofire.download(.GET, url, destination: { (temporaryURL, response) in
let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let pathComponent = response.suggestedFilename
localPath = directoryURL.URLByAppendingPathComponent(pathComponent!)
return localPath!
})
.response { (request, response, _, error) in
if error != nil
{
// got an error in getting the data, need to handle it
print("Error: \(error!)")
}
//print(response)
print("Download file en:\(localPath!)")
self.view.hideToastActivity()
//self.actioncall()
}
}
I need open file from localpath...
You should use UIDocumentInteractionController. You can read about it on this Apple documentation page.
By doing some Googling you should see even some example implementations. For example here you can see some code about this done by "mattneub".
I let you one more code that you can use:
var documentInteractionController: UIDocumentInteractionController!
#IBAction func openDocument(sender: UIButton) {
let URL: NSURL = NSBundle.mainBundle().URLForResource("yourPDF", withExtension: "pdf")!
if (URL != "") {
// Initialize Document Interaction Controller
self.documentInteractionController = UIDocumentInteractionController(URL: URL)
// Configure Document Interaction Controller
self.documentInteractionController.delegate = self
// Present Open In Menu
self.documentInteractionController.presentOptionsMenuFromRect(sender.frame, inView: self.view, animated: true)
//presentOpenInMenuFromRect
}
}
// UIDocumentInteractionControllerDelegate
func documentInteractionControllerViewControllerForPreview(controller: UIDocumentInteractionController) -> UIViewController {
return self
}

Resources