creating PDF with WKWebView throws unknown error in swift - swift

Following this example ( Swift: create pdf with WKWebView's createPdf - copied almost one to one), I try to create a pdf from WKWebView (I am still very amazed that it is still so catastrophically complicated (up to impossible) to create a PDF under macOS. (It's easier with php.))
However - code is
private func CreatePDF(htmlString: String) {
let width = ((21/2.54)*72)
let height = ((29.7/2.54)*72)
if let downloadDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
let savePath = downloadDir.appendingPathComponent("swiftPdf").appendingPathExtension("pdf")
let webViewConfiguration = WKWebViewConfiguration()
let webView = WKWebView(frame: .init(x: 0, y: 0, width: width, height: height), configuration: webViewConfiguration)
let pdfConfiguration = WKPDFConfiguration()
pdfConfiguration.rect = CGRect(x: 0.0, y: 0.0, width: width, height: height)
webView.loadHTMLString(htmlString, baseURL: Bundle.main.resourceURL)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
webView.createPDF(configuration: pdfConfiguration) { result in
switch result {
case .success(let data):
do {
try data.write(to: savePath)
print("Successfully created and saved pdf…")
} catch let error {
print("Could not _save_ pdf: \(error)")
}
case .failure(let error): // <<-- always comes out here with error
print("Could not create pdf: \(error)")
}
}
}
}
}
The console tells
failure(Error Domain=WKErrorDomain Code=1 "An unknown error occurred" UserInfo={NSLocalizedDescription=An unknown error occurred.})
Could not create pdf: Error Domain=WKErrorDomain Code=1 "An unknown error occurred." UserInfo={NSLocalizedDescription=An unknown error occurred.}
Wonderful errormessage. :-)
But what ist the problem now?

Related

flutter : ar flutter plugin problem with scale and color

i have a problem with ar_flutter_plugin in IOS .. its working good in android , i face 2 problem with plugin :
size of model .
color of model .
i tried to modify plugin to solve these problems but without result !
about scale (size of model) i change code in screen shot below but not real size and color dark than android .
i think Swift developer can help me in this , thanks
func makeNodeFromWebGlb(name: String, modelURL: String, transformation: Array<NSNumber>?) -> Future<SCNNode?, Never> {
return Future {promise in
var node: SCNNode? = SCNNode()
let handler: (URL?, URLResponse?, Error?) -> Void = {(url: URL?, urlResponse: URLResponse?, error: Error?) -> Void in
// If response code is not 200, link was invalid, so return
if ((urlResponse as? HTTPURLResponse)?.statusCode != 200) {
print("makeNodeFromWebGltf received non-200 response code")
node = nil
promise(.success(node))
} else {
guard let fileURL = url else { return }
do {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
let targetURL = documentsDirectory.appendingPathComponent(urlResponse!.url!.lastPathComponent)
try? FileManager.default.removeItem(at: targetURL) //remove item if it's already there
try FileManager.default.copyItem(at: fileURL, to: targetURL)
do {
let sceneSource = GLTFSceneSource(url: targetURL)
let scene = try sceneSource.scene()
for child in scene.rootNode.childNodes {
// child.scale = SCNVector3(0.01,0.01,0.01) // Compensate for the different model dimension definitions in iOS and Android (meters vs. millimeters)
child.scale = SCNVector3(1.0,1.0,1.0)
//child.eulerAngles.z = -.pi // Compensate for the different model coordinate definitions in iOS and Android
//child.eulerAngles.y = -.pi // Compensate for the different model coordinate definitions in iOS and Android
node?.addChildNode(child)
}
node?.name = name
if let transform = transformation {
node?.transform = deserializeMatrix4(transform)
}
/*node?.scale = worldScale
node?.position = worldPosition
node?.worldOrientation = worldRotation*/
} catch {
print("\(error.localizedDescription)")
node = nil
}
// Delete file to avoid cluttering device storage (at some point, caching can be included)
try FileManager.default.removeItem(at: targetURL)
promise(.success(node))
} catch {
node = nil
promise(.success(node))
}
}
}
let downloadTask = URLSession.shared.downloadTask(with: URL(string: modelURL)!, completionHandler: handler)
downloadTask.resume()
}
}
problem
show

Swift: Getting thumbnail from a video URL often fails with error: "The operation could not be completed" [duplicate]

I'm trying to get video thumbnails with the following code:
let asset = AVAsset(URL: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
do {
let cgImage = try imgGenerator.copyCGImageAtTime(CMTimeMake(1, 30), actualTime: nil)
let uiImage = UIImage(CGImage: cgImage)
imageview.image = uiImage
}
catch let error as NSError
{
print("Image generation failed with error \(error)")
}
Sometimes it works and sometime it doesn't showing the following error:
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could
not be completed" UserInfo={NSLocalizedDescription=The operation could
not be completed, NSUnderlyingError=0x14eab520 {Error
Domain=NSOSStatusErrorDomain Code=-12792 "(null)"},
NSLocalizedFailureReason=An unknown error occurred (-12792)}
I have tried to figure out what is Domain=NSOSStatusErrorDomain Code=-12792 but I don't understand how I can get more details about this error code. How can I convert this error code into a string to get relevant information about what this error means?
I was able to solve this issue by the following approach.
Swift 4.1
func createThumbnailForVideo(atURL videoURL: URL , completion : #escaping (UIImage?)->Void) {
let asset = AVAsset(url: videoURL)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(1, preferredTimescale: 60)
let times = [NSValue(time: time)]
assetImgGenerate.generateCGImagesAsynchronously(forTimes: times, completionHandler: { _, image, _, _, _ in
if let image = image {
let uiImage = UIImage(cgImage: image)
completion(uiImage)
} else {
completion(nil)
}
})
}
Hope this will help.

How do I fix a nil error when trying to save an image to library?

I'm building an app that takes a picture and then you will have the option to save it. However before you have taken a picture the UIImageView is empty (nil) and when you press the save button it crashes because you can't save photos that are nil. How can I make an error handling so it doesn't crash when the UIImageView is nil.
I've tried throw and catch but can't get it to work.
func tryDataConversion() throws -> Data {
let imageData = myImage.image!.jpegData(compressionQuality: 1.0)
return imageData!
}
Full code for action func that I want to work:
#IBAction func saveImageButton(_ sender: Any) {
let imageData = myImage.image!.jpegData(compressionQuality: 1.0)
let compressedImage = UIImage(data: imageData!)
UIImageWriteToSavedPhotosAlbum(compressedImage!, nil, nil, nil)
alert(title: "Recent Photo Saved", msg: "Your most recent photo has been saved to your photo library")
}
as soon as the following line it crashes because when unwrapping it founds nil and it can unwrap nil.
let imageData = myImage.image!.jpegData(compressionQuality: 1.0)
and gives the following error.
Fatal error: Unexpectedly found nil while unwrapping an Optional value:
instead of
let imageData = myImage.image!.jpegData(compressionQuality: 1.0)
let compressedImage = UIImage(data: imageData!)
UIImageWriteToSavedPhotosAlbum(compressedImage!, nil, nil, nil)
try this:
if let imageData = myImage.image?.jpegData(compressionQuality: 1.0),
let compressedImage = UIImage(data: imageData) {
UIImageWriteToSavedPhotosAlbum(compressedImage!, nil, nil, nil)
}
If you want to throw an error to handle you need to create an error:
enum DataConversionError: Error {
case emptyImage
}
func tryDataConversion() throws -> Data {
guard let imageData = someData else {
throw DataConversionError.emptyImage
}
return imageData
}
func handleSaveButtonTap() {
do {
let imageData = try tryDataConversion()
// Do something with your imageData
print(imageData)
}
catch {
// Handle your error here
print("UIImageView is empty")
print("Error: \(error)")
return
}
}

QLThumbnailGenerator doesn't generate the thumbnail

I tried using QLThumbnailGenerator in Simulator/Device for iOS and iPadOS, but it does not work.
I'am able to only obtain a standard empty thumbnail but not the rich icon from my files from documents directory.
Some progress with files in sandbox but nothing useful.
Do you make it work?
Maybe something with the permission is wrong...but what? From my app I am able to list files, and read (open) them.
#IBAction func generateDidSelect(_ sender: Any) {
let docDir = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last
print("DOC: \(docDir)")
let absPath = URL(fileURLWithPath: docDir ?? "").appendingPathComponent("flowers.png").absoluteString
//[[NSBundle mainBundle]URLForResource:#"flowers" withExtension:#"png"];
let fileURL = URL(fileURLWithPath: absPath)
fileURL.startAccessingSecurityScopedResource()
let size = CGSize(width: 200, height: 200)
let scale = UIScreen.main.scale
let isFile = fileURL.isFile()
let exists = FileManager.default.fileExists(atPath: fileURL.path)
print("isFILE? \(isFile ? "YES" : "NO") exists? \(exists ? "YES" : "NO") \nFILE: \(fileURL)")
let request = QLThumbnailGenerationRequest(fileAtURL: fileURL, size: size, scale: scale, representationTypes: QLThumbnailGenerationRequestRepresentationTypeAll)
//request.iconMode = YES;
QLThumbnailGenerator.shared().generateRepresentations(for: request, updateHandler: { thumbnail, type, error i
DispatchQueue.main.async(execute: {
print(String(format: "*** TYPE: %ld ****", Int(type)))
let uiimage = thumbnail?.uiImage
let cgImage = thumbnail?.cgImage
if let uiimage = uiimage {
print("uiimage: \(uiimage)")
}
if let cgImage = cgImage {
print("cgImage: \(cgImage)")
}
if uiimage != nil {
self.thumbnailImageView.image = uiimage
}
if error != nil {
if let error = error {
print("ERROR: \(error)")
}
//self.thumbnailImageView.image = [UIImage imageWithContentsOfFile:fileURL.path]; // test read, works
}
})
})
}
Then I tried with an image into the bundle.
Getting the file url with:
Bundle.main.url(forResource: "flowers", withExtension: "png")
and it magically works! ...but no with fileURLWithPath method.
But, accessing the same identical file uploaded via iTunes into the Documents directory of the app i get a read error.
2019-10-01 12:41:27.167091+0200 test_thumb_obj[618:57118] DOC: /var/mobile/Containers/Data/Application/BE4A5950-5D24-4620-A1FE-B837222E8B64/Documents
2019-10-01 12:41:27.196739+0200 test_thumb_obj[618:57118] isFILE? YES exists? YES
FILE: file:///var/mobile/Containers/Data/Application/BE4A5950-5D24-4620-A1FE-B837222E8B64/Documents/flowers.png
2019-10-01 12:41:27.233546+0200 test_thumb_obj[618:57118] *** TYPE: 0 ****
2019-10-01 12:41:27.233788+0200 test_thumb_obj[618:57118] uiimage:
2019-10-01 12:41:27.233858+0200 test_thumb_obj[618:57118] cgImage: (DP)
< (kCGColorSpaceDeviceRGB)>
width = 493, height = 640, bpc = 8, bpp = 32, row bytes = 1984
kCGImageAlphaPremultipliedFirst | kCGImageByteOrder32Little | kCGImagePixelFormatPacked
is mask? No, has masking color? No, has soft mask? No, has matte? No, should interpolate? Yes
2019-10-01 12:41:27.234761+0200 test_thumb_obj[618:57118] *** TYPE: 1 ****
2019-10-01 12:41:27.234836+0200 test_thumb_obj[618:57118] uiimage: (null)
2019-10-01 12:41:27.234865+0200 test_thumb_obj[618:57118] cgImage: (null)
2019-10-01 12:41:27.234943+0200 test_thumb_obj[618:57118] ERROR: Error Domain=QLThumbnailErrorDomain Code=2 "No cached thumbnail"
2019-10-01 12:41:27.262228+0200 test_thumb_obj[618:57118] *** TYPE: 2 ****
2019-10-01 12:41:27.262317+0200 test_thumb_obj[618:57118] uiimage: (null)
2019-10-01 12:41:27.262349+0200 test_thumb_obj[618:57118] cgImage: (null)
2019-10-01 12:41:27.262452+0200 test_thumb_obj[618:57118] ERROR: Error Domain=QLThumbnailErrorDomain Code=0 "Could not generate a thum
bnail" UserInfo={NSUnderlyingError=0x281676940 {Error Domain=NSCocoaErrorDomain Code=256 "The file couldn’t be opened."}}
The only image a get i TYPE = 0, the white empty one.
And, as before, on simulator nothing good....
Error Domain=NSPOSIXErrorDomain Code=22 "couldn't issue sandbox extension com.apple.app-sandbox.read for...
Some test i missed to make it working?
I had the same issue. I resolved it by changing representationTypes from .all to .thumbnail
let request = QLThumbnailGenerator
.Request(fileAt: fileURL, size: size, scale: scale,
representationTypes: .thumbnail)
Or if you like, you can try to create a recursion. After 20-30 calls of generateRepresentations the error "No cached thumbnail" stopped appearing and the image was available.
func thumbnail(for fileURL: URL, size: CGSize, scale: CGFloat) {
QLThumbnailGenerator.shared.generateRepresentations(for: request) { (thumbnail, type, error) in
let uiimage = thumbnail?.uiImage
if uiimage == nil {
thumbnail(fileURL, size, scale)
}
else {
//image is available
}
}
}
Solved on iOS13.2 beta.
On device ok, still read issues on simulator
This worked for me, I create this method:
import QuickLook
...
func generateThumbnailFromQuickLook(url: URL,
type: QLThumbnailGenerator.Request.RepresentationTypes,
completionHandler: #escaping (Bool, UIImage?) -> Void) {
let size = CGSize(width: 300, height: 250)
let scale = UIScreen.main.scale
let request = QLThumbnailGenerator.Request(fileAt: url,
size: size,
scale: scale,
representationTypes: type)
let generator = QLThumbnailGenerator.shared
generator.generateBestRepresentation(for: request) { thumbnail, error in
if let error = error {
print(error)
}
completionHandler(thumbnail != nil, thumbnail?.uiImage)
}
}
Then I check if the representation type thumbnail fail. If it fails, I try to get the representation type all:
let url: URL = <url file>
generateThumbnailFromQuickLook(url: url, type: .thumbnail) { success, image in
if success == false {
generateThumbnailFromQuickLook(url: url, type: .all) { success, image in
// use the thumbnail here
}
} else if let image = image {
// use the thumbnail here
}
}

How to Create thumbnail from local files

I want to create a thumbnail image for files (word, excel, video ....)
This what i did:
import QuickLook
class ThumbsCreator: NSObject {
private var file : File?
init(file: File?) {
super.init()
self.file = file
}
func createThumb() {
let url = URL(string: (self.file?.path()))
}
}
After a lot of search, I found this solution :
import PDFKit
import AVKit
import WebKit
func createThumb() {
let url = URL(string: (self.file?.path()))
switch file?.type {
case: FileType.image.rawValue:
let image = UIImage(contentsOfFile: (url?.path)!)
_finalImage = self.createScaledImage(image: image!)
break
case: FileType.office.rawValue:
//Loading.......
break
case FileType.Pdf.rawValue:
guard let doc = PDFDocument(url: url!) else {return}
guard let page = doc.page(at: 0) else {return}
_finalImage = page.thumbnail(of: CGSize(width: 768, height: 1024), for: .cropBox)
break
case: FileType.video.rawValue:
let asset = AVAsset(url: url!)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
let time = CMTime(seconds: 2, preferredTimescale: 1)
do {
let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
_finalImage = UIImage(cgImage: imageRef)
} catch let error{
print("Error: \(error)")
}
break
}
}
func createScaledImage(image: UIImage) {
let THUMB_WIDTH = 150.0 - 40.0
let THUMB_HEIGHT = THUMB_WIDTH - 23.0
var itemThumb = resizeImage(image: image, constraintSize: CGSize(width: THUMB_WIDTH, height: THUMB_HEIGHT))
let thumbRect = CGRect(x: 0, y: 0, width: 10, height: 10)
UIGraphicsBeginImageContextWithOptions(thumbRect.size, true, 0.0)
let context = UIGraphicsGetCurrentContext()
// Fill a white rect
context?.setFillColor(gray: 1.0, alpha: 1.0)
context?.fill(thumbRect)
// Stroke a gray rect
let comps : [CGFloat] = [0.8, 0.8, 0.8, 1]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let strokeColor = CGColor(colorSpace: colorSpace, components: comps)
context?.setStrokeColor(strokeColor!)
UIRectFrame(thumbRect)
//CGColorRelease(strokeColor!)
itemThumb.draw(in: thumbRect.insetBy(dx: 1, dy: 1))
itemThumb = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
self.finishThumCreation(image: image)
}
}
Starting from iOS 13 and macOS 10.15, there is the QuickLook Thumbnailing API. It supports any file format for which the OS can provide a preview: either because the OS knows this format or because the owner of the third-party format provided a QuickLook plugin.
Here is an example based on Apple's tutorial:
func thumbnail(for fileURL: URL, size: CGSize, scale: CGFloat) {
let request = QLThumbnailGenerator
.Request(fileAt: fileURL, size: size, scale: scale,
representationTypes: .lowQualityThumbnail)
QLThumbnailGenerator.shared.generateRepresentations(for: request)
{ (thumbnail, type, error) in
DispatchQueue.main.async {
if thumbnail == nil || error != nil {
// Handle the error case gracefully.
} else {
// Display the thumbnail that you created.
}
}
}
}
On macOS before 10.15, in my app I fallback to NSWorkspace.shared.icon(forFile:) which provides a document icon based on the file type (but not a thumbnail).
You can use https://developer.apple.com/documentation/uikit/uidocumentinteractioncontroller/1616801-icons
var icons: [UIImage] { get }
let controller = UIDocumentInteractionController(url:someUrl)
print(controller.icons.first)
Only for a video
extension UIViewController {
func thumbnail(_ sourceURL:URL) -> UIImage {
let asset = AVAsset(url: sourceURL)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
let time = CMTime(seconds: 1, preferredTimescale: 1)
do {
let imageRef = try imageGenerator.copyCGImage(at: time, actualTime: nil)
return UIImage(cgImage: imageRef)
} catch {
print(error)
return UIImage(named: "NoVideos")!
}
}
}
There's no good API for this yet. There is NSURLThumbnailDictionaryKey, but YMMV. You can indeed get icons via UIDocumentInteractionController.