I Made a PDF an save it in my App, but I want let de user save the PDF document in a directory outside of my app. (Sorry for bad English, I am from Swiss.)
The markupText in the formatter, ist this something important, that I must use for something?
I think - UIGraphicsBeginPDFContextToFile(pathForPDF, rect, nil) - save the file in the document directory of my app, ist this correct?
Other question. Ist there any mistake in my code. I have taken a lot of code from answers here and hope that im make it right.
import UIKit
import Foundation
class PdfErstellung {
static func PdfErstellen(_ auswahlZeilen : [LebensmittelDataTV], _ vitalstoffWerteListe : [LebensmittelDataTV], _ heuteString : String) {
var html = "<html><head><meta charset='UTF-8'></head><body>Erstelle Vitalstoffwerte PDF</body></html"
// 1. Create Print Formatter with input text.
let formatter = UIMarkupTextPrintFormatter(markupText: html)
// 2. Add formatter with pageRender
let render = UIPrintPageRenderer()
render.addPrintFormatter(formatter, startingAtPageAt: 0)
// 3. Assign paperRect and printableRect
let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
let printable = page.insetBy(dx: 0, dy: 0)
let rect = CGRect.zero
render.setValue(NSValue(cgRect: page), forKey: "paperRect")
render.setValue(NSValue(cgRect: printable), forKey: "printableRect")
// 4. Create PDF context and draw
let fileName = "Vitalstoffwerte " + heuteString
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let pathForPDF = documentsDirectory.appendingFormat("/" + fileName)
UIGraphicsBeginPDFContextToFile(pathForPDF, rect, nil)
// Start Draw of page
var y = 0
var stringRechteckX50 = CGRect(x: 50, y: y, width: 100, height: 20)
var stringRechteckX160 = CGRect(x: 160, y: y, width: 30, height: 20)
var stringRechteckX170 = CGRect(x: 170, y: y, width: 10, height: 20)
var stringRechteckX220 = CGRect(x: 220, y: y, width: 100, height: 20)
var stringRechteckX330 = CGRect(x: 330, y: y, width: 30, height: 20)
var stringRechteckX340 = CGRect(x: 340, y: y, width: 10, height: 20)
var paragraphStyle = NSMutableParagraphStyle()
var font = UIFont(name: "Helvetica Bold", size: 20.0)
var text = ""
let attributes = [
NSAttributedString.Key.paragraphStyle: paragraphStyle,
NSAttributedString.Key.font: font,
NSAttributedString.Key.foregroundColor: UIColor.black
]
var attributedString = NSAttributedString(string: text, attributes: attributes)
// ÜBERSCHRIFT
font = UIFont(name: "Helvetica Bold", size: 20.0)
text = "Tagesbedarf in % vom " + heuteString
paragraphStyle.alignment = .left
var stringRechteck = CGRect(x: 50, y: 50, width: 300, height: 40)
attributedString.draw(in: stringRechteck)
// Vitalstoffwerte Liste
y = 80
for zeile in 0..<vitalstoffWerteListe.count {
let druckenVitalstoffWerte = vitalstoffWerteListe[zeile]
if druckenVitalstoffWerte.zeilenInfoID == 0 { // Überschrift
y = y + 10
font = UIFont(name: "Helvetica Bold", size: 14.0)
text = druckenVitalstoffWerte.name
attributedString.draw(in: stringRechteckX50)
} else { // Detail
font = UIFont(name: "Helvetica", size: 12.0)
text = druckenVitalstoffWerte.name
attributedString.draw(in: stringRechteckX50)
text = druckenVitalstoffWerte.anzahl
paragraphStyle.alignment = .right
attributedString.draw(in: stringRechteckX160)
text = druckenVitalstoffWerte.masse
paragraphStyle.alignment = .left
attributedString.draw(in: stringRechteckX170)
}
y = y + 30
}
y = 80
text = "Lebensmittel"
font = UIFont(name: "Helvetica Bold", size: 14.0)
paragraphStyle.alignment = .left
attributedString.draw(in: stringRechteckX220)
// Lebensmittelliste
y = 120
for zeile in 0..<auswahlZeilen.count {
let auswahlZeilenObjekt = auswahlZeilen[zeile]
font = UIFont(name: "Helvetica", size: 12.0)
text = auswahlZeilenObjekt.name
attributedString.draw(in: stringRechteckX220)
text = auswahlZeilenObjekt.anzahl
paragraphStyle.alignment = .right
attributedString.draw(in: stringRechteckX330)
text = auswahlZeilenObjekt.masse
paragraphStyle.alignment = .left
attributedString.draw(in: stringRechteckX340)
}
y = y + 30
// End Draw of page
UIGraphicsEndPDFContext()
}
// I think the Pdf was saved before, but i do not know exactly
static func savePdf(_ urlString: String, _ fileName: String) {
DispatchQueue.main.async {
let url = URL(string: urlString)
let pdfData = try? Data.init(contentsOf: url!)
let resourceDocPath = (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)).last! as URL
let pdfNameFromUrl = "YourAppName-\(fileName).pdf"
let actualPath = resourceDocPath.appendingPathComponent(pdfNameFromUrl)
do {
try pdfData?.write(to: actualPath, options: .atomic)
print("pdf successfully saved!")
} catch {
print("Pdf could not be saved")
}
}
}
}
---- Edit -----
I have change a lot. Thanks for the link, I found other links and had new ideas.
My next Problem is:
Vitalstoffcontroller[6161:165020] [default] [ERROR] Failed to determine whether URL /Users/lukashedinger/Library/Developer/CoreSimulator/Devices/4DE0F1A6-67B7-4909-8C46-78278D403E63/data/Containers/Data/Application/D72F4EE0-D330-4B2D-924E-E17FC805EF55/tmp/Vitalstoffwerte 21092018-21092018.pdf (n) is managed by a file provider
I found this, but not understand, what my mistake is.
UIActivityViewController Error: Failed to determine whether URL is managed by a file provider
How to save file in the documents folder?
(Perhaps is a bug of IOS 12 I read: https://forums.developer.apple.com/thread/103198)
My new Code
// 5. Save PDF file
let dateiName = "Vitalstoffwerte " + heuteString
let path = "\(NSTemporaryDirectory())\(dateiName).pdf"
pdfData.write( toFile: path, atomically: true)
print("open \(path)")
// var fileUrl: URL = URL(fileURLWithPath: NSTemporaryDirectory())
// fileUrl.appendPathComponent("foo")
// fileUrl.appendPathExtension("bar")
let fileUrl: URL = URL(fileURLWithPath: path)
let directory = "\(NSTemporaryDirectory())"
let directoryUrl : URL = URL(fileURLWithPath: directory)
print("open \(fileUrl)")
print("open \(directoryUrl)")
let fm = FileManager.default
//let fileName = String((fileUrl.lastPathComponent)) as NSString
//let documentsUrl:URL = fm.urls(for: .documentDirectory, in: .userDomainMask).first!
//let destinationFileUrl = documentsUrl.appendingPathComponent("\(fileName)")
//print("open \(documentsUrl)")
//do {
// try fm.removeItem(at: destinationFileUrl)
//} catch (let deleteError) {
// print("Error deleting a file \(destinationFileUrl) : \(deleteError)")
//}
//do {
// try FileManager.default.copyItem(at: fileUrl, to: destinationFileUrl)
//} catch (let writeError) {
// print("Error creating a file \(destinationFileUrl) : \(writeError)")
//}
do {
//Show UIActivityViewController to save the downloaded file
let contents = try fm.contentsOfDirectory(at: directoryUrl, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
print("open \(contents)")
//for i in 0..<contents.count {
let activityViewController = UIActivityViewController(activityItems: [contents[0]], applicationActivities: nil)
self.present(activityViewController, animated: true, completion: nil)
// for IPAD
if let popOver = activityViewController.popoverPresentationController {
popOver.sourceView = self.view
//popOver.sourceRect =
//popOver.barButtonItem
}
//}
// return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]lastObject];
} catch {
print("Fehler")
}
}
------- UPDATE : Found a way for my problem, but not for file -----
I give the pdfData direct to ViewController, and do not save it to file.
UIActivityViewController(activityItems: [pdfData], applicationActivities: nil)
I not use a file, that way is good enough for me
I am finish, that is my code
func SavePdfUser(){
import UIKit import Foundation
class PdfErstellung {
}
-- EDIT -- The Problem ist only in IOS 12.
I do not know why, but in some simulator: I can make the file, and give the URL to UIActivityViewController and I have my own filename and in other the URL fail, and I must give the pdfData directly.
My idea: I Try first the URL and if this fail then I give the pdfData
My problem ist, how I can find out, the user could save the file or not (if he had try it), can I ask the after the error code in the log? the do, catch do not go.
2018-09-25 15:36:55.787894+0200 Vitalstoffcontroller[8656:186334] [default] [ERROR] Failed to determine whether URL /Users/lukashedinger/Library/Developer/CoreSimulator/Devices/45A185C5-6DEA-4A52-B415-939758FB7F7E/data/Containers/Data/Application/2469BE4A-53B2-4545-B5D7-E64FBCDB383E/tmp/Vitalstoffwerte 25092018-25092018.pdf (n) is managed by a file provider 2018-09-25 15:36:56.312499+0200 Vitalstoffcontroller[8656:186334] [ShareSheet] cancelled request - error: The operation couldn’t be completed. Invalid argument
-- EDIT --
ma temporary solution is, in ios12 I give the data to UIActivityViewController , otherwise I save the data as file and give the url to UIActivityViewController.