How to add image to QuickLook that is stored in variable - ios

I would like to add a couple of images that are stored in variables to a QLPreviewController. The QuickLook datasource requires a fileURL which I'm not sure how to get for an image that is stored in a variable and not in disk or in the project.
Any pointers as to how I can solve this issue?

Below code can be used to to display image file from local and from URL in QLPreviewController.
import UIKit
import QuickLook
class ViewController: UIViewController {
lazy var previewItem = NSURL()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func displayLocalFile(_ sender: UIButton){
let previewController = QLPreviewController()
// Set the preview item to display
self.previewItem = self.getPreviewItem(withName: "bull.png")
previewController.dataSource = self
self.present(previewController, animated: true, completion: nil)
}
#IBAction func displayFileFromUrl(_ sender: UIButton){
// Download file
self.downloadfile(completion: {(success, fileLocationURL) in
if success {
// Set the preview item to display======
self.previewItem = fileLocationURL! as NSURL
// Display file
DispatchQueue.main.async {
let previewController = QLPreviewController()
previewController.dataSource = self
self.present(previewController, animated: true, completion: nil)
}
}else{
debugPrint("File can't be downloaded")
}
})
}
func getPreviewItem(withName name: String) -> NSURL{
// Code to diplay file from the app bundle
let file = name.components(separatedBy: ".")
let path = Bundle.main.path(forResource: file.first!, ofType: file.last!)
let url = NSURL(fileURLWithPath: path!)
return url
}
func downloadfile(completion: #escaping (_ success: Bool,_ fileLocation: URL?) -> Void){
let itemUrl = URL(string: "https://developer.apple.com/wwdc20/images/hero/memoji/large/L7_2x.jpg")
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent("filename.jpg")
// to check if it exists before downloading it
if FileManager.default.fileExists(atPath: destinationUrl.path) {
debugPrint("The file already exists at path")
completion(true, destinationUrl)
// if the file doesn't exist
} else {
// you can use NSURLSession.sharedSession to download the data asynchronously
URLSession.shared.downloadTask(with: itemUrl!, completionHandler: { (location, response, error) -> Void in
guard let tempLocation = location, error == nil else { return }
do {
// after downloading your file you need to move it to your destination url
try FileManager.default.moveItem(at: tempLocation, to: destinationUrl)
print("File moved to documents folder")
completion(true, destinationUrl)
} catch let error as NSError {
print(error.localizedDescription)
completion(false, nil)
}
}).resume()
}
}
}
//MARK:- QLPreviewController Datasource
extension ViewController: QLPreviewControllerDataSource {
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
return 1
}
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
return self.previewItem as QLPreviewItem
}
}

Related

WebView returning empty after file exported successfully

Working on webView and exported pdf functionality working fine.All functionality regarding export PDF in separate extension WKWebView.When exportPDf downloading complete sending events to javascript but webview returning nill.Calling the object of homeController in WKWebView extension for passing the parameter url in exportPDFUrl method.In HomeViewController there is a method callJavaScript where webview returning nill and go to else block.
extension WKWebView {
func saveWebViewPdf(data: NSMutableData) -> String {
var homeVC = HomeViewController()
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let docDirectoryPath = paths[0]
let pdfPath = docDirectoryPath.appendingPathComponent("\(UUID().uuidString).pdf")
if data.write(to: pdfPath, atomically: true) {
return homeVC.exportPDFUrl(pdfPath.path)
} else {
return ""
}
}
}
class HomeViewController {
// MARK: - Export PDF Download Completed EVENTS
func exportPDFUrl(_ url: String) -> String {
return "\(exportPDFDownload(url))"
}
func exportPDFDownload(_ url: String) {
callJavaScript(script: makeJSForExportPDFDownload(url))
}
func makeJSForExportPDFDownload(_ url: String) -> String {
return "window.dispatchEvent(new CustomEvent('pdfDownloadComplete',{detail:'\(url)'}))"
}
func callJavaScript(script: String) {
DispatchQueue.main.async { [weak self] in
guard let self = self else {print("something wrong"); return}
self.webview!.evaluateJavaScript(script) { (result, error) in
if error == nil {
print(result as Any)
}
}
}
}
}

share extension loading a WhatsApp Zip attachment is not working

I am trying to read Export chat Zip file but share extension loading a WhatsApp Zip attachment is not working.
I am using this code -:
override func viewDidLoad() {
super.viewDidLoad()
getURL()
}
private func getURL() {
let extensionItem = extensionContext?.inputItems.first as! NSExtensionItem
let itemProvider = extensionItem.attachments?.first
let zip_type = String(UTType.zip.identifier)
if itemProvider!.hasItemConformingToTypeIdentifier(zip_type) {
itemProvider!.loadItem(forTypeIdentifier: zip_type, options: nil, completionHandler: { (item, error) -> Void in
guard let url = item as? NSURL else { return }
OperationQueue.main.addOperation {
print("url\(url)")
self.path = url as URL
do {
let unzipDirectory = try Zip.quickUnzipFile(self.path)
print("unzipDirectory\(unzipDirectory)")
if let dir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = dir.appendingPathComponent(unzipDirectory.lastPathComponent)
print("fileURL\(fileURL)")
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
print(text2)
}
catch {/* error handling here */}
}
}
catch {
print("Something went wrong")
}
}
})
} else {
print("error")
}
}
override func isContentValid() -> Bool {
print("Hiii")
// Do validation of contentText and/or NSExtensionContext attachments here
return true
}
override func didSelectPost() {
print("hello")
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return []
}
in console error is:
[core] SLComposeServiceViewController got attachment coarseType 0
[core] SLComposeServiceViewController made no attachment for itemProvider conforming to public.file-url
Can anyone help please?

How to copy selected file to app's directory

I'm new to FileManager in iOS.
I'm trying to create a simple app: there's a list of files, and when the user press a cell the the file opens. The user can also add new files.
This is what I currently have:
class TableViewController: UITableViewController, UIDocumentPickerDelegate {
var urls: [URL] = []
override func viewDidLoad() {
super.viewDidLoad()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
#IBAction func newButtonPressed(_ sender: Any) {
let types = UTType(tag: "pdf", tagClass: .filenameExtension, conformingTo: nil)!
let controller = UIDocumentPickerViewController(forOpeningContentTypes: [types])
controller.delegate = self
self.present(controller, animated: true, completion: nil)
}
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
for url in urls {
self.storeAndShare(withURL: url)
}
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return urls.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell(style: .default, reuseIdentifier: nil)
cell.textLabel?.text = urls[indexPath.row].localizedName
return cell
}
lazy var documentInteractionController: UIDocumentInteractionController = {
let vc = UIDocumentInteractionController()
vc.delegate = self
return vc
}()
func share(url: URL) {
documentInteractionController.url = url
documentInteractionController.uti = url.typeIdentifier ?? "public.data, public.content"
documentInteractionController.name = url.localizedName ?? url.lastPathComponent
documentInteractionController.presentPreview(animated: true)
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
share(url: self.urls[indexPath.row])
}
func storeAndShare(withURL url: URL) {
/// START YOUR ACTIVITY INDICATOR HERE
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
let direcotryURL = URL(string: FileManager.default.currentDirectoryPath)!
let newURL = direcotryURL.appendingPathComponent(response?.suggestedFilename ?? "fileName.pdf")
do {
try data.write(to: newURL)
} catch let err {
print("ERROR: \(err).")
// Error Domain=NSCocoaErrorDomain Code=518 "The file couldn’t be saved because the specified URL type isn’t supported." UserInfo={NSURL=/doc.pdf}
return
}
DispatchQueue.main.async {
/// STOP YOUR ACTIVITY INDICATOR HERE
self.urls.append(newURL)
self.share(url: newURL)
self.tableView.reloadData()
}
}.resume()
}
}
extension TableViewController: UIDocumentInteractionControllerDelegate {
/// If presenting atop a navigation stack, provide the navigation controller in order to animate in a manner consistent with the rest of the platform
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
guard let navVC = self.navigationController else {
return self
}
return navVC
}
}
extension URL {
var typeIdentifier: String? {
return (try? resourceValues(forKeys: [.typeIdentifierKey]))?.typeIdentifier
}
var localizedName: String? {
return (try? resourceValues(forKeys: [.localizedNameKey]))?.localizedName
}
}
I have problems with the storeAndShare() function. I'm trying to copy the selected file to the app's directory, and then add the new URL to the urls list (self.urls).
But it doesn't work, and gives me an error in data.write:
Error Domain=NSCocoaErrorDomain Code=518 "The file couldn’t be saved because the specified URL type isn’t supported." UserInfo={NSURL=/doc.pdf}
Why is this happening? and how can I achieve what I need? thanks in advance
The most significant mistake is currentDirectoryPath. You must not use it to get the path to the Documents directory. You have to call url(for:in:appropriateFor:create: or urls(for:in:) of FileManager
And – if we are talking about local files – URLSession is the wrong way, too.
func storeAndShare(with url: URL) {
let directoryURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let newURL = directoryURL.appendingPathComponent(url.lastPathComponent)
do {
try FileManager.default.copyItem(at: url, to: newURL)
DispatchQueue.main.async {
self.urls.append(newURL)
self.share(url: newURL)
self.tableView.reloadData()
}
} catch {
print("ERROR: \(error).")
}
}
"/doc.pdf"
Is not writable for you.
Get the documents directory:
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
And save your file there (instead of using currentDirectoryPath).

How to observe shared app groups file changed

I have file which is shared between my app and extensions:
Write to file from extension:
func writeToFile()
{
let file = "file.txt"
let text = "data" //just a text
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.ml.test.apps")!
let fileURL = dir.appendingPathComponent(file)
do {
try text.write(to: fileURL, atomically: false, encoding: .utf8)
}
catch {/* error handling here */}
}
Read from the app:
func readFromFile()
{
let file = "file.txt"
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.ml.test.apps")!
let fileURL = dir.appendingPathComponent(file)
do {
let text2 = try String(contentsOf: fileURL, encoding: .utf8)
NSLog("Dan: \(text2)")
}
catch {/* error handling here */}
}
My question is how can I observe changes to this file. In case the extension writes to it and changes data so the app will get notification change and read the file.
Here is a simple demo of approach based on usage NSFileCoordinator/NSFilePresenter pattern.
Tested with Xcode 11.4 / iOS 13.4
Application part. Here is a ViewController plays a file presenter role, for simplicity (if one controller can manage many files, then it is better to create explicit presenters per-file)
class ViewController: UIViewController, NSFilePresenter {
var presentedItemURL: URL?
var presentedItemOperationQueue: OperationQueue = OperationQueue.main
#IBOutlet weak var userNameField: UILabel!
func presentedItemDidChange() { // posted on changed existed file only
readFromFile()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// register for presentedItemDidChange work
NSFileCoordinator.addFilePresenter(self)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// unregister - required !!
NSFileCoordinator.removeFilePresenter(self)
}
override func viewDidLoad() {
super.viewDidLoad()
let file = "file.txt"
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.test.apps")!
presentedItemURL = dir.appendingPathComponent(file)
readFromFile() // read previously stored data
}
private func readFromFile()
{
let coordinator = NSFileCoordinator(filePresenter: self)
coordinator.coordinate(readingItemAt: presentedItemURL!, options: [], error: nil) { url in
if let text2 = try? String(contentsOf: url, encoding: .utf8) {
userNameField.text = text2 // demo label in view for test
} else {
userNameField.text = "<no text>"
//just initial creation of file needed to observe following changes
coordinator.coordinate(writingItemAt: presentedItemURL!, options: .forReplacing, error: nil) { url in
do {
try "".write(to: url, atomically: false, encoding: .utf8)
}
catch { print("writing failed") }
}
}
}
}
}
Extension part (simple Today extension for demo having one button)
class TodayViewController: UIViewController, NCWidgetProviding, NSFilePresenter {
var presentedItemURL: URL?
var presentedItemOperationQueue: OperationQueue = OperationQueue.main
override func viewDidLoad() {
super.viewDidLoad()
let file = "file.txt"
let dir = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.test.apps")!
presentedItemURL = dir.appendingPathComponent(file)
}
#IBAction func post(_ sender: Any) { // action on button in extension
writeToFile()
}
func writeToFile()
{
let text = "new data" //just a text
let coordinator = NSFileCoordinator(filePresenter: self)
coordinator.coordinate(writingItemAt: presentedItemURL!, options: .forReplacing, error: nil) { url in
do {
try text.write(to: url, atomically: false, encoding: .utf8)
}
catch { print("writing failed") }
}
}
func widgetPerformUpdate(completionHandler: (#escaping (NCUpdateResult) -> Void)) {
completionHandler(NCUpdateResult.newData)
}
}

Array of multiple URL's NSFileManager Swift

How would I go about adding multiple URL's? I wonder how I can set that up into an array with swift.
if let audioUrl = NSURL(string: "http://freetone.org/ring/stan/iPhone_5-Alarm.mp3") {
println("LOADING AUDIO")
if let myAudioDataFromUrl = NSData(contentsOfURL: audioUrl){
println("AUDIO LOADED")
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as NSURL
let destinationUrl = documentsUrl.URLByAppendingPathComponent(audioUrl.lastPathComponent!)
println(destinationUrl)
if NSFileManager().fileExistsAtPath(destinationUrl.path!) {
println("The file already exists at path")
} else {
if myAudioDataFromUrl.writeToURL(destinationUrl, atomically: true) {
println("file saved")
} else {
println("error saving file")
}
}
}
}
In this case I think you should use NSURLSession.sharedSession().dataTaskWithURL to do multiple downloads from your links but keeping the download operation asynchronous. You need to add a callback block to it. You should do as follow:
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as NSURL
let musicArray:[String] = ["http://freetone.org/ring/stan/iPhone_5-Alarm.mp3","http://freetone.org/ring/stan2/Samsung_Galaxy_S4-SMS.mp3","https://www.sounddogs.com/sound-effects/25/mp3/235178_SOUNDDOGS__al.mp3"]
var musicUrls:[NSURL!]!
// create a function to start the audio data download
func getAudioDataFromUrl(audioUrl:NSURL, completion: ((data: NSData?) -> Void)) {
NSURLSession.sharedSession().dataTaskWithURL(audioUrl) { (data, response, error) in
completion(data: data)
}.resume()
}
// create another function to save the audio data
func saveAudioData(audio:NSData, destination:NSURL) -> Bool {
if audio.writeToURL(destination, atomically: true) {
println("The file \"\(destination.lastPathComponent!.stringByDeletingPathExtension)\" was successfully saved.")
return true
}
return false
}
// just convert your links to Urls
func linksToUrls(){
musicUrls = musicArray
.map() { NSURL(string: $0) }
.filter() { $0 != nil }
}
// create a loop to start downloading your urls
func startDownloadingUrls(){
for url in musicUrls {
let destinationUrl = documentsUrl.URLByAppendingPathComponent(url.lastPathComponent!)
if NSFileManager().fileExistsAtPath(destinationUrl.path!) {
println("The file \"\(destinationUrl.lastPathComponent!.stringByDeletingPathExtension)\" already exists at path.")
} else {
println("Started downloading \"\(url.lastPathComponent!.stringByDeletingPathExtension)\".")
getAudioDataFromUrl(url) { data in
dispatch_async(dispatch_get_main_queue()) {
println("Finished downloading \"\(url.lastPathComponent!.stringByDeletingPathExtension)\".")
println("Started saving \"\(url.lastPathComponent!.stringByDeletingPathExtension)\".")
if self.saveAudioData(data!, destination: self.documentsUrl.URLByAppendingPathComponent(url.lastPathComponent!) ) {
// do what ever if writeToURL was successful
} else {
println("The File \"\(url.lastPathComponent!.stringByDeletingPathExtension)\" was not saved.")
}
}
}
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
println("Begin of code")
linksToUrls()
startDownloadingUrls()
println("End of code")
}

Resources