NSData - Write to temporary file / directory - ios

I am a little lost on my quest to -
Download a CKAsset (PDF File)
Assign a Temporary Filename
Write the contents of the CKAsset to the filename
I have managed to download the CKAsset and display the contents in a UIWebView, however I am stumbling over steps 2 and 3, I have a filename from a String, and despite trying a variety of WriteToFile combinations I receive errors.
My code is thus :
let filename = record.object(forKey: "materialsFilename")
if materialsType == "PDF" || materialsType == "pdf" {
if let asset1 = record.object(forKey: "materialsFile") as? CKAsset {
let doc1Data : NSData? = NSData(contentsOf:asset1.fileURL)
let path = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename as! String)
let contentsOfFile = doc1Data
var error: NSError?
// Write File
if contentsOfFile.writeToFile(path, atomically: true, encoding: String.Encoding.utf8, error: &error) == false {
if let errorMessage = error {
print("Failed to create file")
print("\(errorMessage)")
}
} else {
print("File \(filename) created at tmp directory")
}
This version presents the error -
Cannot invoke 'writeToFile' with an argument list of type '(URL?,
atomically: Bool, encoding: String.Encoding, error: inout NSError?)'
The temporary file once created will be passed to a UIActivityViewController, to print / email / airdrop the PDF, having only a CKAsset name, the the UIActivityViewController cannot associate the file type to any of the users installed apps, save for print.

After a little head scratching and reviewing my choices following the pointers above, I changed tack and didn't really need to write to a file, just rename the CKAsset, which I achieved with the following script -
let materialsType = record.object(forKey: "materialsType") as! String
let filename = record.object(forKey: "materialsFilename") as! String
if materialsType == "PDF" || materialsType == "pdf" {
if let asset1 = record.object(forKey: "materialsFile") as? CKAsset {
let doc1Data : Data? = Data(contentsOf:asset1.fileURL) as Data?
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename)
self.materialsWebView.load(doc1Data! as Data, mimeType: "application/pdf", textEncodingName: "UTF-8", baseURL: NSURL() as URL)
self.filenameURL = [(fileURL)]
}
The key seemed to hinge on two lines -
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename)
and
self.filenameURL = [(fileURL)]
Which generates the filename for the UIActivityViewController and thus opens up the access to a number of additional Apps.

Are you sure you're using the correct writeToFile method? The NSData reference doesn't show a method with the same signature you're using. Try using one of the ones listed in the reference.

I only find the following function in Data class.
func write(to: URL, options: Data.WritingOptions)
Try this.

Related

Why .lastPathComponent from file picker returns weird file name?

I'm trying to get real file name from file picker. I used two ways, such:
let fileName = file.lastPathComponent
and such:
let fileName = FileManager.default.displayName(atPath: file.absoluteString)
where file is let file = urls[0]. In any time I received strange letters set:
%D0%91%D0%B5%D0%B7%20%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.rtf
the real file name is: Без названия.rtf. The real file name is on Russian language , when I tried to get file with file name on English, everything was OK, so as I see all problems are connected with Russian file names. Maybe someone faced with similar problems and knows how to solve them? I also tried utf-8 encoding but it didn't help me :(
UPDATE
I send it to a chat server via websocket task:
func documentPicker(_ controller: UIDocumentPickerViewController,
didPickDocumentsAt urls: [URL]) {
let file = urls[0]
do{
let fileData = try Data.init(contentsOf: file)
let encodedString = String.init(data: fileData, encoding: .isoLatin1)!
let fileName = FileManager.default.displayName(atPath: file.path)
let time = Int64(NSDate().timeIntervalSince1970)
print(encodedString.count)
wsSendString(message: ChatMStrings.init().sendFilePart(fileContent: encodedString, fileName: fileName, fileSize: encodedString.count))
}catch{
print("contents could not be loaded")
}
}
sending object:
func sendFilePart(fileContent: String, fileName: String, fileSize: Int) -> String {
let sendFile:[String:[String:Any]] = ["chat":["a":"send_file",
"body":fileContent,
"filename":fileName,
"total":fileSize]]
let jsonData = try? JSONSerialization.data(withJSONObject: sendFile, options: [])
return String(data: jsonData!, encoding: String.Encoding.ascii)!
}
sending method:
func wsSendString(message:String) {
self.webSocket!.send(URLSessionWebSocketTask.Message.string(message)) { error in
if let error = error {
print("WebSocket sending error: \(error)")
}
}
self.listenWS()
}
websocket creation:
webSocket = urlSession.webSocketTask(with: request)
webSocket!.resume()
The issue there is that you are using the wrong URL property. file.absoluteString is wrong. you should use file.path.
let fileURL = URL(fileURLWithPath: "Без названия.rtf")
print(fileURL) // %D0%91%D0%B5%D0%B7%20%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.rtf -- file:///private/var/folders/00/l311vw5s2550g5gz2h25b2vr0000gp/T/
print(fileURL.path) // /private/var/folders/00/l311vw5s2550g5gz2h25b2vr0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.MyPlayground-comparable-enumeration-AD2BDA8A-BF7B-4F92-B561-B080E72B4DF0/Без названия.rtf
print(fileURL.absoluteString) // file:///private/var/folders/00/l311vw5s2550g5gz2h25b2vr0000gp/T/com.apple.dt.Xcode.pg/containers/com.apple.dt.playground.stub.iOS_Simulator.MyPlayground-comparable-enumeration-AD2BDA8A-BF7B-4F92-B561-B080E72B4DF0/%D0%91%D0%B5%D0%B7%20%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.rtf
let fileNamePath = FileManager.default.displayName(atPath: fileURL.path)
print(fileNamePath) // "Без названия.rtf\n"
let fileNameAbsoluteString = FileManager.default.displayName(atPath: fileURL.absoluteString)
print(fileNameAbsoluteString) // %D0%91%D0%B5%D0%B7%20%D0%BD%D0%B0%D0%B7%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F.rtf

Load CKAsset into UIWebView

I am trying to load a PDF File stored in a publicDatabase as a CKAsset, the code worked well on an existing application, published last year. However I now get the following error
Cannot convert value of type 'NSData' to type 'Data' in coercion
here is the code -
if docType == "PDF" || docType == "pdf" {
if let asset1 = record.object(forKey: "documentFile") as? CKAsset {
let doc1Data : NSData? = NSData(contentsOf:asset1.fileURL)
let path = (NSTemporaryDirectory() as NSString).appendingPathComponent(filename)
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(filename)
do {
try doc1Data!.write(to: URL(fileURLWithPath: path), options: .atomic)
} catch {
print(error)
}
self.docWebView.load(doc1Data! as Data, mimeType: "application/pdf", textEncodingName: "UTF-8", baseURL: NSURL() as URL)
self.filenameURL = [(fileURL)]
}
I'm sure there must be a simple explanation, but I cannot see the problem
Thanks in Advance.
Try changing the below line :
let doc1Data : NSData? = NSData(contentsOf:asset1.fileURL)
to
let doc1Data = NSData(contentsOf:asset1.fileURL)
It would appear the answer is simple, change the load Data to loadRequest and reference the URL of the document having obtained it from the CKAsset
if docType == "PDF" || docType == "pdf" {
if let asset1 = record.object(forKey: "documentFile") as? CKAsset {
let doc1Data : NSData! = NSData(contentsOf:asset1.fileURL)
let path = (NSTemporaryDirectory() as NSString).appendingPathComponent(filename)
do {
try doc1Data!.write(to: URL(fileURLWithPath: path), options: .atomic)
let url = URL(fileURLWithPath: path)
let urlRequest = URLRequest(url: url)
self.docWebView?.loadRequest(urlRequest)
} catch {
print(error)
}
}

File not readable from documents folder

I am creating a binary file using few c libraries and saving the file in documents folder of app in iOS. But when i try to read it its not reading. I am using the following code to read.
let fileData = try NSData(contentsOfFile: filePath, options: NSData.ReadingOptions.mappedIfSafe) as Data
But this goes always into catch block.
You must retrieve your Document folder first and then get the content of document.
func readDocument(file:String) -> NSData{
var vreturn:NSData
if let dirs : [String] = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true){
let dir = dirs[0] //documents directory
let path = dir.stringByAppendingString(file);
//reading
vreturn = (try? NSData(contentsOfFile: path)) ?? NSData()
}
return vreturn
}
I'm using this to get String content but it seems to work the same way using NSData. Simple way to read local file using Swift?
do {
let videoData = try Data(contentsOf: avsset.url)
print(videoData.count)
} catch let err {
print("Error:", err)
}
you must catch error...

NSURL to NSDATA or Data is always nil

I am trying to upload a video to a server file through Alamofire but I couldn't get the "data" going to be passed..its always nil
var videoURL = NSURL(string: "")
//returns Optional(file:///private/var/mobile/Containers/Data/Application/1FB40086-228B-4011-A9D4-7874E2EEF9F4/tmp/4A6AAD76-B899-4B67-8E96-925DA4AE9E93.mov)
let videodata = NSData(contentsOfFile: (videoURL?.absoluteString)!)
//nil
let url = NSURL(fileURLWithPath: (videoURL?.absoluteString)!)
let videodata = NSData(contentsOf: url as URL)
//nil
If I get data would lead a way for me to do this:
Alamofire.upload(multipartFormData: { multipartFormData in
multipartFormData.append (videodata as! Data, withName: "file", fileName: "file.mov", mimeType: "video/quicktime")
enter code here
EDIT::
thank you guys, with your help I have struggled my way out of there to this file not found error, but I can see the file is being saved in my gallery, any clue would save my day.
print (videoURL!)
//returns file:///private/var/mobile/Containers/Data/Application/3F280477-DA16-4A67-AE60-D6247143352E/tmp/1E4AC002-6AD0-41E1-9E0D-A09B697F81F7.mov
print (videoURL!.path!)
// returns /private/var/mobile/Containers/Data/Application/3F280477-DA16-4A67-AE60-D6247143352E/tmp/1E4AC002-6AD0-41E1-9E0D-A09B697F81F7.mov
var videoData = NSData()
let path = videoURL!.path!
if FileManager.default.fileExists(atPath: path) {
}else {
print("Could not fin file at url: \(videoURL!.path!)")
// here it throws file not found
}
In Swift 3 use native URL and Data instead of NSURL and NSData.
if let videoURL = URL(string: urlString), let videodata = try? Data(contentsOf: videoURL) {
//Add code of Alamofire here
}
Using absoluteString returns a string that includes file:// at the beginning and you don't want that. You need to return the path of the URL
guard let videoPathString = videoURL.path as? String else {
//handle error here if you can't create a path string
return
}
var videoData = NSData()
//check if file exists at this path first
if (NSFileManager.defaultManager().fileExistsAtPath(videoPathString)) {
videoData = NSData(contentsOfFile: NSString(videoPathString))
} else {
//if file does not exist at that path, handle here
}

From raw data to UIImagePNGRepresentation in fewer steps

Using this code, I extract an image from a Share Extension and I write it to a directory I created in an App Group.
let content = self.extensionContext!.inputItems[0] as! NSExtensionItem
let contentType = kUTTypeImage as String
for attachment in content.attachments as! [NSItemProvider] {
if attachment.hasItemConformingToTypeIdentifier(contentType) {
attachment.loadItem(forTypeIdentifier: contentType, options: nil) { data, error in
// from here
if error == nil {
let url = data as! NSURL
let originalFileName = url.lastPathComponent
if let imageData = NSData(contentsOf: url as URL) {
let img = UIImage(data:imageData as Data)
if let data = UIImagePNGRepresentation(img!) {
// write, etc.
}
}
}
}
Anything is working fine.
What I'd like to know is if it is possible to reduce some code: in particular, after if error == nil, I:
cast data to NSURL;
use NSURL to get a NSData;
use NSData to get a UIImage;
use UIImage to get a UIImagePNGRepresentation;
Aside from avoiding the creation of the imageData variable, isn't there a way to (safely) achieve the same goal with fewer steps?
First of all you need to use native Data and URL instead of NSData & NSURL also if you want to write file in DocumentDirectory then you can directly use that imageData no need to make UIImage object from it and then convert it to data using UIImagePNGRepresentation.
if let url = data as? URL, error == nil {
let originalFileName = url.lastPathComponent
if let imageData = try? Data(contentsOf: data) {
// write, etc.
var destinationURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
destinationURL.appendPathComponent("fileName.png")
try? imageData.write(to: destinationURL)
}
}

Resources