What will my PDF location be in local app files? - SwiftUI - swift

I have sourced a function that converts a view to a PDF from another stack overflow post - https://stackoverflow.com/a/60753437/12969913. With this I am using another view to try and display the PDF inside a new view for the user to inevitably share and view as they wish.
How do I get my PDF to display, I can't figure out how the URL works and what I have to put into the new view.
The URL is created in the exportToPDF() function:
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let outputFileURL = documentDirectory.appendingPathComponent("SwiftUI.pdf")
I have then sourced some code to display the PDF from another stack overflow post and have implemented that - https://stackoverflow.com/a/61480852/12969913, but in order for this to work I have to put in my file URL, how do I do that, and what will the file URL be?
My PDFKitView() Code - to display the new .pdf:
struct PDFKitView: View {
var url: URL
var body: some View {
PDFKitRepresentedView(url)
}
}
struct PDFKitRepresentedView: UIViewRepresentable {
let url: URL
init(_ url: URL) {
self.url = url
}
func makeUIView(context: UIViewRepresentableContext<PDFKitRepresentedView>) -> PDFKitRepresentedView.UIViewType {
let pdfView = PDFView()
pdfView.document = PDFDocument(url: self.url)
pdfView.autoScales = true
return pdfView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFKitRepresentedView>) {
// Update the view.
}
}
I then go to display this using:
HStack{
PDFKitView(url: ????????)
Text("Hello World")
}

If you are droping the PDF file directly to your Xcode project, this is how you will be able to retrive the URL:
guard let path = Bundle.main.path(forResource: "SwiftUI", ofType: "pdf") else { return }
let url = URL(fileURLWithPath: path)

Related

Open URL Scheme through Widget Tap (iOS 16 Widget Kit)

Here is the code I currently have however, it does not seem to be working. This example says I want to open the calc app. My goal is to open an app once a widget is clicked.
#main App Code:
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
print("Received deep link: \(url)")
}
}
}
Widget Code:
Gauge(value: 50), in: 0.0...100.0) {
} currentValueLabel: {
Text(Open App)
}
.gaugeStyle(.accessoryCircularCapacity)
.widgetURL(URL(string: "calc://")!)
Then you need to do this in a 2 step process. First, you need to set up your app to receive custom URLs from your widget. This is shockingly well explained by Apple here. Once you have your app's custom url scheme set up, it is time to set up your widget. Essentially what you are going to do is send a URL with a query that is the URL you want to open. Back in your app, you receive that URL, parse it out, and then call openUrl() with the URL you want to open, and that app will open.
Your code above is close. Following Apple's example above, try this:
In your widget create a deep link URL:
func createDeeplinkForCalc() -> URL {
var components = URLComponents()
components.scheme = "myphotoapp"
components.host = "com.example.myphotoapp"
components.path = "/calc"
components.queryItems = [
URLQueryItem(name: "open", value: "calc://")
]
return components.url!
}
Then, in .widgetURL, pass this:
.widgetURL(createDeeplinkForCalc())
In your main app:
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
handleURL(url: URL)
}
}
}
func handleURL(_ url:URL) {
// This makes sure you got the correct URL
guard url.scheme == "myphotoapp",
url.host == "com.example.myphotoapp"
else { return }
let query = parseQuery(url: url)
guard let urlString = query["open"],
!urlString.isEmpty else { return } // make sure something exists in the value
if let calcURL = URL(string: urlString) {
openURL(calcURL) // this calls "calc://" and opens the calculator
}
private func parseQuery(url: URL) -> Query {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
let queryItems = components.queryItems
else { return ["":""] }
return queryItems.reduce(into: Query()) { (result, item) in
result[item.name] = item.value
}
}
}
The above has not been tested, but should work.

Use Image in SwiftUI with either URL or Data without UIKit

I have downloaded an image in bytes (Data). I stored it with FileManager in the cache of the user's device. I was wondering if I could create an Image with either Data or a local URL, I am currently using UIImage but I want to support macOS.
Is there a full SwiftUI way of creating an Image with URL or Data without UIKit/UIImage?
if your question is about showing and image in macos, that you already have downloaded (and is in the cachesDirectory), then try something like this, to create an image from your image in bytes: (using NSImage for macos)
struct ContentView: View {
#State var img = NSImage()
var body: some View {
Image(nsImage: img)
.onAppear {
do {
// local file
let url = try FileManager.default
.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("kat") // <--- your file name here
.appendingPathExtension("png")
print("----> url: \(url)")
// even from the net
// let url = URL(string: "https://upload.wikimedia.org/wikipedia/commons/e/ef/Red-billed_gull%2C_Red_Zone%2C_Christchurch%2C_New_Zealand.jpg")!
if let nsimg = NSImage(contentsOf: url) { img = nsimg }
} catch {
print("\(error)")
}
}
}
}

Add an environment object to UIDocumentPickerViewController

I'm trying to import mp3 files into my app from outside the app's sandbox using the file browser. I have a "store" environment object where i have the folder URL to which I'd like to move the selected file. I want to add that store as an environment object to the file picker.
Here's how I call the document picker from a view (here's just the relevant code):
struct libraryView: View {
#State var filePicker : DocumentPicker
#EnvironmentObject var store : MusicStore
#State var showPicker = false
func presentDocumentPicker() {
let viewController = UIApplication.shared.windows[0].rootViewController!
let controller = filePicker.viewController
viewController.present(controller, animated: true)
}
var body: some View {
Button(action: {showPicker = true
presentDocumentPicker()
}, label: {
Image(systemName: "plus").imageScale(.large)
})
}}
And here's the document picker code:
final class DocumentPicker: NSObject, ObservableObject {
#EnvironmentObject var store : MusicStore
lazy var viewController: UIDocumentPickerViewController = {
let vc = UIDocumentPickerViewController(documentTypes: types, in: .import)
vc.delegate = self
vc.allowsMultipleSelection = self.allowsMultipleSelection
return vc
}()
}
extension DocumentPicker: UIDocumentPickerDelegate {
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
callback(urls)
do {
let filePath = try FileManager.default.contentsOfDirectory(at: url.deletingLastPathComponent(), includingPropertiesForKeys: nil, options: [])[0]
let audioFile = try Data(contentsOf: filePath)
// let destinationURL = store.folderURL!.appendingPathComponent(filePath.lastPathComponent)
// try FileManager.default.moveItem(at: filePath, to: destinationURL)
// print("File moved to documents folder")
}
catch {
print(error)
}
}
}
Here's the store code:
class MusicStore : ObservableObject {
var folderURL : URL?
init(){
do{
self.folderURL = try FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: false
)} catch(let error) {
print(error.localizedDescription)
}
}
So I want to import the file to my app's sandbox within the documentPicker function, like in the commented code. But I can't add an environment object to the view controller. I don't know whether it's possible at all to do it because I'm using the root view controller to show the document picker.

View pdf documents

I have a table with the names of pdf documents. Previously, there were 3 documents and each one has its own ViewController. How can I make it so that with hundreds of documents, I would select one from the table and show it on the View, if I select another document, then on the same View show another document.
while I have such a function, where I substituted the name of the documents in each class and showed it in different representations. But now I need to display everything on one ViewController when selecting any document
import UIKit
import PDFKit
class pdfViewClass {
class func filePDfFunc(nameFile: String, formatFile:String,
nameView:PDFView)
{
if let path = Bundle.main.path(forResource: nameFile,
ofType:formatFile) {
if let pdfDocument = PDFDocument(url: URL(fileURLWithPath:
path)) {
nameView.autoScales = true
nameView.displayDirection = .vertical
nameView.document = pdfDocument
}
}
}
}
You can use Native Apple UIDocumentInteractionController for viewing PDF file.
Create a function like below for View PDF
func viewPdf(urlPath: String, screenTitle: String) {
// open pdf for booking id
guard let url = urlPath.toUrl else {
print("Please pass valid url")
return
}
self.downloadPdf(fileURL: url, screenTitle: screenTitle) { localPdf in
if let url = localPdf {
DispatchQueue.main.sync {
self.openDocument(atURL: url, screenTitle: screenTitle)
}
}
}
}
Function for download PDF
// method for download pdf file
func downloadPdf(fileURL: URL, screenTitle: String, complition: #escaping ((URL?) -> Void)) {
// Create destination URL
if let documentsUrl: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let destinationFileUrl = documentsUrl.appendingPathComponent("\(screenTitle).pdf")
if FileManager.default.fileExists(atPath: destinationFileUrl.path) {
try? FileManager.default.removeItem(at: destinationFileUrl)
}
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url: fileURL)
let task = session.downloadTask(with: request) { tempLocalUrl, response, error in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
complition(destinationFileUrl)
} catch let writeError {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: \(error?.localizedDescription ?? "N/A")")
}
}
task.resume()
} else {
complition(nil)
}
}
Function for open documents
func openDocument(atURL url: URL, screenTitle: String) {
self.documentInteractionController.url = url
self.documentInteractionController.name = screenTitle
self.documentInteractionController.delegate = self
self.documentInteractionController.presentPreview(animated: true)
}
On tap of tableView pass the specific index URL
viewPdf(urlPath: "http://www.africau.edu/images/default/sample.pdf", screenTitle: "Tesing Document")
You can do it using WKWebView easily. Use WKWebView to load your pdf doc.

View pdf in swift 4

i have tried everything on internet to add a PDFViewer in my app. im working with ios 12. im asking you to help me understand what is the possible ways to add a pdf and a solution that can solve it in a easy way for my low experience with swift coding. thank you
We can use our native UIDocumentInteractionController for the same.
Follow below steps :
Step 1
var documentInteractionController = UIDocumentInteractionController()
Step 2
self.documentInteractionController.delegate = self
Step 3
func openDocument(atURL url: URL, screenTitle: String) {
self.documentInteractionController.url = url
self.documentInteractionController.name = screenTitle
self.documentInteractionController.delegate = self
self.documentInteractionController.presentPreview(animated: true)
}
Step 4 : Implement UIDocumentInteractionControllerDelegate
extension ViewController: UIDocumentInteractionControllerDelegate {
// when a document interaction controller needs a view controller for presenting a document preview.
func documentInteractionControllerViewControllerForPreview(_ controller: UIDocumentInteractionController) -> UIViewController {
return self.navigationController ?? UIViewController()
}
}
Some helper methods :
a) View Pdf
func viewPdf(urlPath: String, screenTitle: String) {
// open pdf for booking id
guard let url = urlPath.toUrl else {
print("Please pass valid url")
return
}
self.downloadPdf(fileURL: url, screenTitle: screenTitle) { localPdf in
if let url = localPdf {
DispatchQueue.main.sync {
self.openDocument(atURL: url, screenTitle: screenTitle)
}
}
}
}
b) function for download file
// method for download pdf file
func downloadPdf(fileURL: URL, screenTitle: String, complition: #escaping ((URL?) -> Void)) {
// Create destination URL
if let documentsUrl: URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let destinationFileUrl = documentsUrl.appendingPathComponent("\(screenTitle).pdf")
if FileManager.default.fileExists(atPath: destinationFileUrl.path) {
try? FileManager.default.removeItem(at: destinationFileUrl)
}
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url: fileURL)
let task = session.downloadTask(with: request) { tempLocalUrl, response, error in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
complition(destinationFileUrl)
} catch let writeError {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: \(error?.localizedDescription ?? "N/A")")
}
}
task.resume()
} else {
complition(nil)
}
}
here I am Downloading PDF and store on in File And Open That file in Quick Look
Here I am sharing screen
enter image description here
Reference link: https://www.hackingwithswift.com/example-code/libraries/how-to-preview-files-using-quick-look-and-qlpreviewcontroller
If you just need to present the PDF, you could use a WebView from WebKit and pass the data using the mimetype application/pdf.
like this:
webView.load(data, mimeType: "application/pdf", characterEncodingName: "UTF-8", baseURL: baseURL)