Download file from Firebase Storage using Alamofire in swift - ios

I'm very new in iOS development. So I'm trying to download pdf or image from firebase storage with URL using Alamofire and then I want to display with quicklook. I tried this example but no luck. Present preview of downloaded file directly in app
import UIKit
import Alamofire
import QuickLook
class DocumentViewer: UIViewController{
var previewItem = URL?.self
var refrenceDocURL: URL? // here i get the url from another view controller but never use it because I don't know where should I use it.
func downloadFile(fileUrl: URL) {
let destination: DownloadRequest.DownloadFileDestination = { _, _ in //ERROR: 'DownloadFileDestination' is not a member type of class 'Alamofire.DownloadRequest'
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(documentFilename)
return (fileURL, [.removePreviousFile])
}
Alamofire.download(fileUrl, to: destination) //ERROR: Module 'Alamofire' has no member named 'download'
.response(completionHandler: { (downloadResponse) in
let previewController = QLPreviewController()
previewController.dataSource = self
self.previewItem = downloadResponse.destinationURL
self.present(previewController, animated: true, completion: nil)
})
}
}
extension DocumentViewer: QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.previewItem as! QLPreviewItem
}
}

Looks like you're using old definitions.
DownloadRequest.DownloadFileDestination is DownloadRequest.Destination.
Alamofire.download is AF.download.

Related

Swift IOS Display AR QuickLook preview of downloaded usdz model

this is my first time trying to add an AR preview into an app (also my first time using the file system). I have been trying to implement a solution similar to that explained here https://developer.apple.com/forums/thread/126377 however one key difference is that my usdz model is not in my main bundle as it is generated and downloaded from an external source at run time. I was wondering if it is possible to display a file stored in the apps documents or cache directory and how it is done.
The file is downloaded and stored in the caches directory as follows:
class ModelFetcher: NSObject{
var modelUrl: URL?
func generateModel() {
guard let url = URL(string: "http://127.0.0.1:5000/model.usdz") else {return}
let urlSession = URLSession(configuration: .default, delegate: self, delegateQueue: OperationQueue())
var request = URLRequest(url: url)
request.httpMethod = "POST"
let downloadTask = urlSession.downloadTask(with: request)
downloadTask.resume()
}
}
extension ModelFetcher: URLSessionDownloadDelegate {
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("File Downloaded Location- ", location)
guard let url = downloadTask.originalRequest?.url else {
return
}
let docsPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]
let destinationPath = docsPath.appendingPathComponent(url.lastPathComponent)
try? FileManager.default.removeItem(at: destinationPath)
do {
try FileManager.default.copyItem(at: location, to: destinationPath)
self.modelUrl = destinationPath
print("File moved to: \(modelUrl!.absoluteURL)")
} catch let error {
print("Copy Error: \(error.localizedDescription)")
}
}
}
I then try to display the model using ARQuicklookPreview as follows:
import SwiftUI
import QuickLook
import ARKit
struct ARQuickLookView: UIViewControllerRepresentable {
var allowScaling: Bool = true
func makeCoordinator() -> ARQuickLookView.Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> QLPreviewController {
let controller = QLPreviewController()
controller.dataSource = context.coordinator
return controller
}
func updateUIViewController(_ controller: QLPreviewController,
context: Context) {
// nothing to do here
}
class Coordinator: NSObject, QLPreviewControllerDataSource {
let parent: ARQuickLookView
let destinationPath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].appendingPathComponent("model.usdz")
private lazy var fileURL: URL = destinationPath
init(_ parent: ARQuickLookView) {
self.parent = parent
super.init()
}
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(
_ controller: QLPreviewController,
previewItemAt index: Int
) -> QLPreviewItem {
let fileURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].appendingPathComponent("model.usdz")
print(fileURL)
let item = ARQuickLookPreviewItem(fileAt: fileURL)
print(item)
item.allowsContentScaling = parent.allowScaling
return item
}
}
}
struct ARQuickLookView_Previews: PreviewProvider {
static var previews: some View {
ARQuickLookView()
}
}
However, I receive the error "Unhandled item type 13: contentType is: (null) #PreviewItem" in the console and in the UI it reads unsupported file type, I have performed tests to make sure the file is in the location of the URL and I can even open it in preview on my mac so it's not like the file format or location is wrong.
Any help on displaying a model from a location other than the main bundle would be helpful, or perhaps a workaround to first move the file then display it.
Edit: After changing the preview controller function to
let fileURL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0].appendingPathComponent("model.usdz")
return fileUrl as QLPreviewItem
Now in the UI it just says "model universal scene description Package"
And in the console I still get an error however now it reads: Unhandled item type 13: contentType is: com.pixar.universal-scene-description-mobile #PreviewItem
Which doesn't make sense as that it one of the only types it supports
Thanks,
Louis

How to view PDF from Firebase Storage - SwiftUI PDFVIEW

I am having some issues trying to display a PDF which is stored in Firebase Storage in my SwiftUI app.
I have successfully done the following;
Uploaded a PDF file to Firebase Storage
Get the URL from the directory path
Create a PDFView and pass the URL
Currently the PDFKITVIEW appears but with nothing to show, and no errors. What am I missing ?
Show PDFKITVIEW
.sheet(isPresented: $isShowingDoc) {
PDFKitView(path: model.object.docURL) // Path to firebase storage is correct
}
PDFKITVIEW
import SwiftUI
import PDFKit
struct PDFKitView : UIViewRepresentable {
private var url: URL?
init(path: String) {
// Get url from path
let dm = DataManager()
url = dm.urlForDocPath(path: path)
}
func makeUIView(context: Context) -> UIView {
let pdfView = PDFView()
if let url = url {
pdfView.document = PDFDocument(url: url)
}
return pdfView
}
func updateUIView(_ uiView: UIView, context: Context) {
// Empty
}
}
DataManager
func getURLFromFirestore(path: String, success:#escaping (_ docURL:URL)->(),failure:#escaping (_ error:Error)->()){
let storage = Storage.storage().reference(withPath: path)
storage.downloadURL { (url, error) in
if let _error = error{
print(_error)
failure(_error)
} else {
if let docURL = url {
success(docURL)
}
}
}
}
func urlForDocPath(path: String) -> URL? {
var url: URL?
getURLFromFirestore(path: path, success: { (docURL) in
url = docURL
}) { (error) in
print(error)
}
return url
}
I cannot post this as a comment since I haven't got enough reputation to do so but seems like the problem can be in your urlForDocPath function, which tries to do an async operation but returning synchronously.
Could you check if url is not nil on the PDFKitView? Because if it is, it will prove my point.
Didn't tried yet in Xcode but needs to be something like following:
func urlForDocPath(path: String, success: #escaping (_ docURL:URL?)->(), failure: #escaping (_ error:Error)->()) {
var url: URL?
getURLFromFirestore(path: path, success: { (docURL) in
success(docURL)
}) { (error) in
failure(error)
}
}

How can I write a file in a folder located at Apple's Files App in SwiftUI?

I followed the Swift project UsingFiles github and its video to write files in my SwiftUI project. The project UsingFiles can write files to Files APP and then the writing files can be seen in the Files APP. But I followed the code as following, the files cannot be seen in Files APP.
let file = "\(UUID().uuidString).txt"
let contents = "Some text..."
let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = dir.appendingPathComponent(file)
do {
try contents.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {
print("Error: \(error)")
}
The UsingFiles writing files is file:///Users/fmac/Library/Developer/CoreSimulator/Devices/11111111-881E-488A-9571-E61B83EB6062/data/Containers/Data/Application/11111111-AF89-4B15-B3B5-E13A63A19F8D/Documents/E018F056-83EA-4D70-87C4-16F755AA404A.txt.
My writing files is file:///Users/fmac/Library/Developer/CoreSimulator/Devices/11111111-881E-488A-9571-E61B83EB6062/data/Containers/Data/Application/11111111-B191-4ACD-98B1-004E619C2EC7/Documents/C9E0F52E-040F-4647-94A3-88E0DA171AB5.txt
I can find the writing files of UsingFiles in the directory UsingFiles as following:
But I cannot find the writing file in Files APP in my SwiftUI project. Is there something wrong of the code in SwiftUI? Why I cannot find the writing file in Files APP?
I used UIActivityViewController to save a file on my phone.
It's simple and works.
import SwiftUI
struct ContentView: View {
var body: some View {
Button(action: wirtieFile) {
Image(systemName: "square.and.arrow.up")
}
}
func wirtieFile() -> Void{
let file = "test.txt"
let dir = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(file)
let contents = "test..."
do {
try contents.write(to: dir!, atomically: true, encoding: .utf8)
} catch {
print(error.localizedDescription)
}
var filesToShare = [Any]()
filesToShare.append(dir!)
let av = UIActivityViewController(activityItems: filesToShare, applicationActivities: nil)
UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil)
}
}
As I have already posted in comments you can NOT programmatically save a file out of your APP Bundle. You can use a UIDocumentInteractionController and ask the user to choose the location where the file is supposed to be written.
So if you are working with SwiftUI this gets a bit more complicated than the regular Storyboard approach as you can see in this post because you need to implement UIViewControllerRepresentable for UIDocumentInteractionController:
struct DocumentInteractionController: UIViewControllerRepresentable {
fileprivate var isExportingDocument: Binding<Bool>
fileprivate let viewController = UIViewController()
fileprivate let documentInteractionController: UIDocumentInteractionController
init(_ isExportingDocument: Binding<Bool>, url: URL) {
self.isExportingDocument = isExportingDocument
documentInteractionController = .init(url: url)
}
func makeUIViewController(context: UIViewControllerRepresentableContext<DocumentInteractionController>) -> UIViewController { viewController }
func updateUIViewController(_ controller: UIViewController, context: UIViewControllerRepresentableContext<DocumentInteractionController>) {
if isExportingDocument.wrappedValue && documentInteractionController.delegate == nil {
documentInteractionController.uti = documentInteractionController.url?.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = documentInteractionController.url?.localizedName
documentInteractionController.presentOptionsMenu(from: controller.view.frame, in: controller.view, animated: true)
documentInteractionController.delegate = context.coordinator
documentInteractionController.presentPreview(animated: true)
}
}
func makeCoordinator() -> Coordintor { .init(self) }
}
And its Coordinator:
class Coordintor: NSObject, UIDocumentInteractionControllerDelegate {
let documentInteractionController: DocumentInteractionController
init(_ controller: DocumentInteractionController) {
documentInteractionController = controller
}
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController { documentInteractionController.viewController }
func documentInteractionControllerDidDismissOptionsMenu(_ controller: UIDocumentInteractionController) {
controller.delegate = nil
documentInteractionController.isExportingDocument.wrappedValue = false
}
}
Now you can create your DocumentInteraction View and its previews:
struct DocumentInteraction: View {
#State private var isExportingDocument = false
var body: some View {
VStack {
Button("Export Document") { self.isExportingDocument = true }
.background(DocumentInteractionController($isExportingDocument,
url: Bundle.main.url(forResource: "sample", withExtension: "pdf")!))
}
}
}
struct DocumentInteraction_Previews: PreviewProvider {
static var previews: some View { DocumentInteraction() }
}
You will need those helpers as well:
extension URL {
var typeIdentifier: String? { (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier }
var localizedName: String? { (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName }
}
Sample project
Try add “Application supports iTunes file sharing” and “Supports opening documents in place” to “info.plist”. And change them value to true.

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)

QLPreviewController not work on iOS11

class ViewController: UIViewController {
let quickLookController = QLPreviewController()
override func viewDidLoad() {
super.viewDidLoad()
quickLookController.dataSource = self
quickLookController.delegate = self
}
#IBAction func buttonAction(_ sender: Any) {
present(quickLookController, animated: true, completion: nil)
quickLookController.reloadData()
quickLookController.refreshCurrentPreviewItem()
}
}
extension ViewController: QLPreviewControllerDataSource,QLPreviewControllerDelegate {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
let path = Bundle.main.path(forResource: "AppCoda-Ppt.ppt", ofType: nil)
let url = NSURL(fileURLWithPath: path!)
return url
}
}
This is a bug in iOS 11.2. Please file it at bugreport.apple.com if you want to be kept up to date on its status. A workaround is not to store your ppt file in your app bundle. Use a different location somewhere in your container, such as your Application Support directory.
Answer from #Thomas is correct (THANK YOU!) For Swift 3 you can do something like this in the previewItemAt:index method to cache the file on demand:
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
guard let CacheDirURL = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) else {
print("Can't get cache dir URL")
return NSURL(fileURLWithPath: "FILE_NOT_FOUND")
}
let fileUrl = CacheDirURL.appendingPathComponent("cachedFileName.pdf")
if !FileManager.default.fileExists(atPath: fileUrl.path) {
if let sourceUrl = Bundle.main.url(forResource: "Pioneer_UAS_Policy_Aug_2016", withExtension: "pdf") {
print("Copying file from bundle \(sourceUrl.path)")
try? FileManager.default.copyItem(at: sourceUrl, to: fileUrl)
}
}
return NSURL(fileURLWithPath: fileUrl.path)
}
You have to copy the file to the i.e. temporary directory and read the file from there.

Resources