Cannot write Data to file in Swift - swift

I have an image that I want it's data to be saved in this fileUri generated by ("react-native-fs"). LibraryDirectoryPath/saved_images/{filename}:
/Users/macbookpro/Library/Developer/CoreSimulator/Devices/9CBD2F1E-7330-418D-81BE-108C064DEA7E/data/Containers/Data/Application/C26348CC-3463-43EF-9B26-B7E31641E2EA/Library/saved_images/6B3A6A3A-8DE3-488B-AF43-A54775545B38.jpg
And below is my implementation:
do {
let url = URL(string: fileUri)
let fileExisted = FileManager().fileExists(atPath: url!.path)
if (fileExisted) {
try decryptedData.write(to: url!)
} else {
let handle = try FileHandle(forWritingTo: url!)
handle.write(data) // data is type Data
handle.closeFile()
}
} catch {
reject("FileError", "Failed to write file", error)
}
I also tried let url = URL(fileURLWithPath: fileUri) with and without file:// prepending to fileUri
do {
let url = URL(fileURLWithPath: fileUri)
let fileExisted = FileManager().fileExists(atPath: url.path)
if (fileExisted) {
try decryptedData.write(to: url)
} else {
let handle = try FileHandle(forWritingTo: url)
handle.write(data)
handle.closeFile()
}
} catch {
reject("FileError", "Failed to write file " + error.localizedDescription, error)
}
it says:

You are using the wrong API.
let url = URL(string: fileUri)
is for strings representing a full – even encoded - URL starting with a scheme like file:// or https://.
On the other hand fileUri is actually a path without a scheme, so you have to use
let url = URL(fileURLWithPath: fileUri)
This returns a non optional URL by adding the file:// scheme.
fileUri should be renamed as filePath.

Related

Swift URL returns nil when the url contains an internationalized domain name (IDN)

I was wondering why this piece of code always exit with 1:
import Foundation
// an idn domain:
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
let urlStr = "https://" + uLabel
guard let url = URL(string: urlStr) else { exit(1) }
exit(0)
Since Apple's browser Safari does support well IDN domains, I was surprised their URL library does not... I tried to urlencode the string beforehand, but it is not helping.
======EDIT======
After fixing the piece above upon Matt's suggestion, I faced another problem during fetching the website data:
import Foundation
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
let scheme = "https"
var comps = URLComponents()
comps.scheme = scheme
comps.host = uLabel
guard let url = comps.url else { exit(1) }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let rawContent = data else { exit(1) }
guard let content = String(data: rawContent, encoding: String.Encoding.utf8) else { exit(1) }
if content.contains("UASG Testbed Landing Page") {
// successfully fetch content of the page
exit(0)
} else {
// error during fetching
exit(1)
}
}
task.resume()
RunLoop.main.run()
The program still exits with 1. It seems the domain is not converted to an A-LABEL as it is on Safari, as the error suggests (the certificate is valid, the error is misleading):
NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “համընդհանուր-ընկալում-թեստ.հայ” which could put your confidential information at risk., NSErrorFailingURLKey=https://%d5%b0%d5%a1%d5%b4%d5%a8%d5%b6%d5%a4%d5%b0%d5%a1%d5%b6%d5%b8%d6%82%d6%80-%d5%a8%d5%b6%d5%af%d5%a1%d5%ac%d5%b8%d6%82%d5%b4-%d5%a9%d5%a5%d5%bd%d5%bf.%d5%b0%d5%a1%d5%b5/,
I don't know why you're having trouble, but rule number one is never never never call URL(string). Use URLComponents. That's what it's for.
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
let scheme = "https"
var comps = URLComponents()
comps.scheme = scheme
comps.host = uLabel
let url = comps.url // works for me
Until, someone can find a way to tell the URL framework to correctly represent the URL "https://համընդհանուր-ընկալում-թեստ.հայ", it is possible to translate the domain part to an A-LABEL before passing it to the URL constructor and elude its wrong internal representation:
import Foundation
import IDNA
// an idn domain:
let uLabel = "համընդհանուր-ընկալում-թեստ.հայ"
guard let aLabel = uLabel.idnaEncoded else { exit(1) }
let supportedUrl = "https://" + aLabel
guard let url = URL(string: supportedUrl) else { exit(1) }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let rawContent = data else { exit(1) }
guard let content = String(data: rawContent, encoding: String.Encoding.utf8) else { exit(1) }
if content.contains("UASG Testbed Landing Page") {
// successfully fetch content of the page
exit(0)
} else {
// error during fetching
exit(1)
}
}
task.resume()
RunLoop.main.run()
This piece of code does exit 0. The IDNA library is there (ensure you take the master branch, because released versions are still on IDNA2003):
https://github.com/Wevah/IDNA-Cocoa

How do I save an image to a png file?

How do I load an image from specified url and save it to an existing local png file ? I'm trying to do it as follow but get an error
The file couldn’t be saved because the specified URL type isn’t supported
let url = URL(string: urlString)
let data = try? Data(contentsOf: url!)
try data?.write(to: URL(string: "icon.png")!)
First of all never use synchronous Data(contentsOf to load data from a server.
The error occurs because the URL has the wrong format
A remote URL must contain a scheme and host (http://server.com...)
An local URL in the file system must be created with URL(fileURLWithPath: and the path must start with a slash.
Use the following code to asynchronously download a file:
extension FileManager {
open func secureCopyItem(at srcURL: URL, to dstURL: URL) -> Bool {
do {
if FileManager.default.fileExists(atPath: dstURL.path) {
try FileManager.default.removeItem(at: dstURL)
}
try FileManager.default.copyItem(at: srcURL, to: dstURL)
} catch (let error) {
print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
return false
}
return true
}
}
func download() {
let storagePathUrl = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("file.png")
let fileUrl = "https://server/file.png"
let urlRequest = URLRequest(url: URL(string: fileUrl)!)
let task = URLSession.shared.downloadTask(with: urlRequest) { tempLocalUrl, response, error in
guard error == nil, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
print("Error")
return
}
guard FileManager.default.secureCopyItem(at: tempLocalUrl!, to: storagePathUrl) else {
print("Error")
return
}
}
task.resume()
}

Error copying files with FileManager (CFURLCopyResourcePropertyForKey failed because it was passed an URL which has no scheme)

I'm trying to copy some (media) files from one folder to another using FileManager's copyItem(at:path:), but I'm getting the error:
CFURLCopyResourcePropertyForKey failed because it was passed an URL which has no scheme
Error Domain=NSCocoaErrorDomain Code=262 "The file couldn’t be opened because the specified URL type isn’t supported."
I'm using Xcode 9 beta and Swift 4.
let fileManager = FileManager.default
let allowedMediaFiles = ["mp4", "avi"]
func isMediaFile(_ file: URL) -> Bool {
return allowedMediaFiles.contains(file.pathExtension)
}
func getMediaFiles(from folder: URL) -> [URL] {
guard let enumerator = fileManager.enumerator(at: folder, includingPropertiesForKeys: []) else { return [] }
return enumerator.allObjects
.flatMap {$0 as? URL}
.filter { $0.lastPathComponent.first != "." && isMediaFile($0)
}
}
func move(files: [URL], to location: URL) {
do {
for fileURL in files {
try fileManager.copyItem(at: fileURL, to: location)
}
} catch (let error) {
print(error)
}
}
let mediaFilesURL = URL(string: "/Users/xxx/Desktop/Media/")!
let moveToFolder = URL(string: "/Users/xxx/Desktop/NewFolder/")!
let mediaFiles = getMediaFiles(from: mediaFilesURL)
move(files: mediaFiles, to: moveToFolder)
The error occurs because
URL(string: "/Users/xxx/Desktop/Media/")!
creates a URL without a scheme. You can use
URL(string: "file:///Users/xxx/Desktop/Media/")!
or, more simply,
URL(fileURLWithPath: "/Users/xxx/Desktop/Media/")
Note also that in fileManager.copyItem() the destination must
include the file name, and not only the destination
directory:
try fileManager.copyItem(at: fileURL,
to: location.appendingPathComponent(fileURL.lastPathComponent))

Load XML file from main Bundle in Swift 3.0

I have a .GPX file contains routing info of a hiking trip which I want to load into my app. Everything is ok if I load it from remote URL (https://dl.dropboxusercontent.com/u/45741304/appsettings/Phu_si_Lung_05_01_14.gpx) but I can't load this same file from app bundle (already in "Copy bundle resources" and had correct target membership).
Here's my code for loading this file from remote URL:
var xmlParser: XMLParser!
func startParsingFileFromURL(urlString: String) {
guard let url = URL(string: urlString) else {
print("Can't load URL: \(urlString)")
return
}
self.xmlParser = XMLParser(contentsOf: url)
self.xmlParser.delegate = self
let result = self.xmlParser.parse()
print("parse from URL result: \(result)")
if result == false {
print(xmlParser.parserError?.localizedDescription)
}
}
and from the main bundle:
func startParsingFile(fileName: String, fileType: String) {
guard let urlPath = Bundle.main.path(forResource: fileName, ofType: fileType) else {
print("Can't load file \(fileName).\(fileType)")
return
}
guard let url:URL = URL(string: urlPath) else {
print("Error on create URL to read file")
return
}
self.xmlParser = XMLParser(contentsOf: url)
self.xmlParser.delegate = self
let result = self.xmlParser.parse()
print("parse from file result: \(result)")
if result == false {
print(xmlParser.parserError?.localizedDescription)
}
}
Error on load from app bundle:
parse from file result: false
Optional("The operation couldn’t be completed. (Cocoa error -1.)")
You are saying:
guard let urlPath = Bundle.main.path(forResource: fileName, ofType: fileType) else {
print("Can't load file \(fileName).\(fileType)")
return
}
guard let url:URL = URL(string: urlPath) else {
print("Error on create URL to read file")
return
}
First, it is very silly to turn a string path into a URL. You knew you wanted a URL, so why didn't you start by calling url(forResource:...)?
Second, if you ever do turn a string path into a URL, you must make a file URL.

Bad URL in swift 2

When I want to load a url it goes always into the catch block. How can i solve this? I think it's something with the escaped url.
If i set url in the if block it goes immediately to the else block.
let urls : String = Baseurl+"lat="+latitude+"&lon="+longitude+"&APPID={"+apiKey+"}"
let escapedurl = urls.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
let url: NSURL = NSURL(string: escapedurl!)!
if let url = NSURL(string: escapedurl!) {
do {
let contents = try NSString(contentsOfURL: url, usedEncoding: nil)
print(contents)
} catch {
// contents could not be loaded
print("bad")
}
} else {
// the URL was bad!
print("bads")
}
URLHostAllowedCharacterSet() returns the character set for characters allowed in a host URL subcomponent.
For example, in the URL http://www.example.com/index.html, the host component is www.example.com.
Try to change
let escapedurl = urls.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())
into
let escapedurl = urls.stringByAddingPercentEncodingWithAllowedCharacters(.URLQueryAllowedCharacterSet())