Swift xcode macos PDFDocument(url: URL) does not create document - swift

I am trying to learn how to display a .pdf file in Swift in an xcode project. I have code that works if the target file is in the project's bundle, but I cannot display a .pdf that is elsewhere in my user space. In the code that follows (lower half), "pdfURL2" prints out and is the correct URL. The code prints out "File Exists", so I know that the target file is accessible. It is a valid .pdf file that I can open with Preview. Using the xcode debugger, "test" is "nil". Following that, "pdfView.document = test" opens a view, but it is, of course, empty. If I surround the "test = PDFDocument(url: pdfURL2)" declaration with a guard" statement, the thread crashes. Hence, I have left that out.
How do I display a .pdf file that is not explicitly in xcode"s project bundle?
CODE FOLLOWS:
import Cocoa
import Quartz
class PDFViewController: NSViewController {
#IBOutlet weak var pdfView: PDFView!
override func viewDidLoad() {
super.viewDidLoad()
let fm = FileManager.default
// THIS Code does WORK (for test.pdf in bundle)
guard let pdfURL = Bundle.main.url(forResource: "test", withExtension: "pdf")
else { fatalError("PDF not found") }
guard let pdfDocument = PDFDocument(url: pdfURL)
else { fatalError("PDF document not created") }
pdfView.document = pdfDocument
// END of code that does WORK (for test.pdf in bundle)
// THis Code does NOT WORK for buildFile.pdf NOT in bundle
let pdfPath2 = "/Users/myaccount/Desktop/Problematic/buildFile.pdf"
let pdfURL2 = URL(fileURLWithPath: pdfPath2)
print("pdfURL2 = \(pdfURL2)")
if fm.fileExists(atPath: pdfPath2){
print("File Exists")
var test = PDFDocument(url: pdfURL2)
pdfView.document = test
}
// Do any additional setup after loading the view.
} // End ViewDidLoad
} // End PDFViewController

Related

How do I create and save a simple text file to my iCloud Drive Documents folder in Swift?

I have built a very simple iOS app which should create a sample text file called test.txt in my Documents folder on my iCloud Drive but when I run the app on an iPhone 12 Pro simulator and check my iCloud Drive on my mac it isn't there.
Here is my Capabilities Tab;
Capabilities tab
Here is my Info.plist file;
Info.plist file
Here is my ViewController code;
//
// ViewController.swift
// InfoPill
//
// Created by Stephen Learmonth on 01/08/2022.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let driveURL = FileManager.default.url(forUbiquityContainerIdentifier: nil)?.appendingPathComponent("Documents")
if driveURL != nil {
print("iCloud available")
let fileURL = driveURL!.appendingPathComponent("test.txt")
try? "Hello world".data(using: .utf8)?.write(to: fileURL)
} else {
print("iCloud not available")
}
}
}
my code prints iCloud available

How to pass a local file URL in WKWebView so it treats it as a dropped file

I am trying to upload a local file with drag and drop and WKWebView. I have only the URL of the file. The MacOS app provides an extension to Final Cut Pro. Therefore what I can actually drop in the WKWebView is an XML from FCP. I obtain the URL of the file from there. In the web project, loaded in the WKWebView, there is already implementation of upload, which I am trying to use.
What is tried and achieved so far:
A custom subclass of WKWebView loads the web project
An XML file is dragged and dropped from FCP in the webview of the extension.
I parse the XML string and gather the URL of the file location from the XML
I pass the URL to the WKWebView and the web view plays the video from the url.
Is it possible to pass the URL in such a way that the WKWebView treats it as a dropped file and how? This is the current implementation of performDragOperation in the WKWebView subclass:
override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
guard let pasteboardObjects = sender.draggingPasteboard.readObjects(forClasses: [NSString.self, NSURL.self], options: nil), pasteboardObjects.count > 0 else {
return false
}
pasteboardObjects.forEach { (object) in
if let XMLString = object as? NSString {
NSLog("It's a string: %#: ", XMLString)
//Acquire URL here …
let urlString = "file:///Users/someuser/SomeLocation/SomeVideo.MP4"
let urlData = urlString.data(using: .utf8)
sender.draggingPasteboard.clearContents()
sender.draggingPasteboard.setData(urlData, forType: .fileURL)
//uncomment the next line and comment the previous 2 and the same result will be achieved
//self.loadFileURL(url, allowingReadAccessTo: url)
}
if let url = object as? NSURL {
NSLog("It's a URL %#", url.absoluteString!)
}
}
return super.performDragOperation(sender)
}

How to programmatically export 3D mesh as USDZ using ModelIO?

Is it possible to programmatically export 3D mesh as .usdz file format using ModelIO and MetalKit frameworks?
Here's a code:
import ARKit
import RealityKit
import MetalKit
import ModelIO
let asset = MDLAsset(bufferAllocator: allocator)
asset.add(mesh)
let filePath = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
let usdz: URL = filePath.appendingPathComponent("model.usdz")
do {
try asset.export(to: usdz)
let controller = UIActivityViewController(activityItems: [usdz],
applicationActivities: nil)
controller.popoverPresentationController?.sourceView = sender
self.present(controller, animated: true, completion: nil)
} catch let error {
fatalError(error.localizedDescription)
}
When I press a Save button I get an error.
The Andy Jazz answer is correct, but needs modification in order to work in a SwiftUI Sandboxed app:
First, the SCNScene needs to be rendered in order to export correctly. You can't create a bunch of nodes, stuff them into the scene's root node and call write() and get a correctly rendered usdz. It must first be put on screen in a SwiftUI SceneView, which causes all the assets to load, etc. I suppose you could instantiate a SCNRenderer and call prepare() on the root node, but that has some extra complications.
Second, the Sandbox prevents a direct export to a URL provided by .fileExporter(). This is because Scene.write() works in two steps: it first creates a .usdc export, and zips the resulting files into a single .usdz. The intermediate files don't have the write privileges the URL provided by .fileExporter() does (assuming you've set the Sandbox "User Selected File" privilege to "Read/Write"), so Scene.write() fails, even if the target URL is writeable, if the target directory is outside the Sandbox.
My solution was to write a custom FileWrapper, which I return if the WriteConfiguration UTType is .usdz:
public class USDZExportFileWrapper: FileWrapper {
var exportScene: SCNScene
public init(scene: SCNScene) {
exportScene = scene
super.init(regularFileWithContents: Data())
}
required init?(coder inCoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func write(to url: URL,
options: FileWrapper.WritingOptions = [],
originalContentsURL: URL?) throws {
let tempFilePath = NSTemporaryDirectory() + UUID().uuidString + ".usdz"
let tempURL = URL(fileURLWithPath: tempFilePath)
exportScene.write(to: tempURL, delegate: nil)
try FileManager.default.moveItem(at: tempURL, to: url)
}
}
Usage in a ReferenceFileDocument:
public func fileWrapper(snapshot: Data, configuration: WriteConfiguration) throws -> FileWrapper {
if configuration.contentType == .usdz {
return USDZExportFileWrapper(scene: scene)
}
return .init(regularFileWithContents: snapshot)
}
08th January 2023
At the moment iOS developers still can export only .usd, .usda and .usdc files; you can check this using canExportFileExtension(_:) type method:
let usd = MDLAsset.canExportFileExtension("usd")
let usda = MDLAsset.canExportFileExtension("usda")
let usdc = MDLAsset.canExportFileExtension("usdc")
let usdz = MDLAsset.canExportFileExtension("usdz")
print(usd, usda, usdc, usdz)
It prints:
true true true false
However, you can easily export SceneKit's scenes as .usdz files using instance method called: write(to:options:delegate:progressHandler:).
let path = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask)[0]
.appendingPathComponent("file.usdz")
sceneKitScene.write(to: path,
options: nil,
delegate: nil,
progressHandler: nil)

How to load Microsoft file like docx in swift via UIWebView

I have been working on ios app which now i am facing some problems. I want to load some files like docx, xls using UIWebView. It gets me white screen on my actual device.
#IBOutlet weak var fileServerWebView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
if let path = Bundle.main.path(forResource: "easychair", ofType: "docx"){
let targetUrl: URL = URL(fileURLWithPath: path)
let request = URLRequest(url: targetUrl as URL)
fileServerWebView.delegate = self
fileServerWebView.scalesPageToFit = true
fileServerWebView.loadRequest(request)
}else{
print("cannot get path")
}
// Do any additional setup after loading the view.
}
That is code that i tried to load that file from local. Thank you for your help.

reference to local file is invalid

This view is to display a local html file. When I launch it, the view never displays, the screen just hangs on the calling view. The exception is a nil value for let fileURL =.
import UIKit
import WebKit
class ViewController2: UIViewController, WKNavigationDelegate {
var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
let fileURL = URL(fileURLWithPath: Bundle.main.path(forResource:"about", ofType: "html")!)
webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
}
}
You don´t need to cast the Bundle.main.path(forResource:"about", ofType: "html") to URL. Do it like this instead:
let fileURL = Bundle.main.url(forResource: "about", withExtension: "html")!
webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
Update:
Remember to also add the file in your Project settings > Your project target > Build Phases > Copy bundle resources otherwise it won´t work. When you add the file to your project remeber to:
Check "copy items if needed"
Select "create folder references"