How do I save an image to a png file? - swift

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()
}

Related

Cannot write Data to file in 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.

Opening a PDF stored in local memory in a PDFView SWIFT

I have downloaded a pdf file to my cache memory.Now I wish to open this PDF file in a PDFView.
I have added a PDFView to my ViewController, here is the code for the same.
let pdfView = PDFView()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(pdfView)
}
The below given code will return the location to which the PDF was downloaded as a URL.
guard let pdfURL = downloader.downloadData(urlString: "https://www.tutorialspoint.com/swift/swift_tutorial.pdf", downloadType: DownloadType.cache.rawValue) else { return }
I have checked the URL given back and the file exists.
Now in the following code I am trying to open it in the pdf view.
if let document = PDFDocument(url: pdfURL) {
pdfView.document = document
}
Below given code shows the download data method.
public func downloadData(urlString : String,downloadType : String)->URL?{
let documentsUrl:URL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first as URL!
var destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile.pdf")
try? FileManager.default.removeItem(at: destinationFileUrl)
guard let url = URL(string: urlString)else{
return nil
}
let urlSession = URLSession(configuration: .default)
let downloadTask = urlSession.downloadTask(with: url) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
if downloadType == "cache" {
do {
try? FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
}
downloadTask.resume()
return destinationFileUrl
}
But it seems like it is returning nil and the code inside if let block is not executed. Please Help!!
Nil of course.
return destinationFileUrl, use it to init PDF, gets nil.
it returns, While the task is still executing, so the file in the path not exists.
Because downloading is an asynchronous action.
So here is completionHandler closure for.
Ususlly, turn this
public func downloadData(urlString : String,downloadType : String)->URL?{
let documentsUrl:URL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first as URL!
var destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile.pdf")
try? FileManager.default.removeItem(at: destinationFileUrl)
guard let url = URL(string: urlString)else{
return nil
}
let urlSession = URLSession(configuration: .default)
let downloadTask = urlSession.downloadTask(with: url) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
if downloadType == "cache" {
do {
try? FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
}
downloadTask.resume()
return destinationFileUrl
}
into
public func downloadData(urlString : String,downloadType : String, completionHandler: #escaping (URL) -> Void){
let documentsUrl:URL = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first as URL!
var destinationFileUrl = documentsUrl.appendingPathComponent("downloadedFile.pdf")
try? FileManager.default.removeItem(at: destinationFileUrl)
guard let url = URL(string: urlString)else{
return nil
}
let urlSession = URLSession(configuration: .default)
let downloadTask = urlSession.downloadTask(with: url) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
if downloadType == "cache" {
do {
try? FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.copyItem(at: tempLocalUrl, to: destinationFileUrl)
completionHandler(destinationFileUrl)
} catch (let writeError) {
print("Error creating a file \(destinationFileUrl) : \(writeError)")
}
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription);
}
}
downloadTask.resume()
}
In the completionHandler call back , init the PDF

loading a url picture

I'm new to IOS development and I'm trying to load an image from a URL, I understand there are some changes between the swift versions.
for some reason I get imageData = nil and I'm not sure why..
private func fetchImage()
{
let url = URL(fileURLWithPath: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png")
if let imageData = NSData(contentsOf: url as URL){
image = UIImage(data: imageData as Data)
}
}
Please check :
private func fetchImage() {
let url = URL(string: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png")!
let task = URLSession(configuration: .default).dataTask(with: url) { (data, response, error) in
if error != nil {
print("Error Occurred: \(String(describing: error))")
}
else {
if let imageData = data {
let image = UIImage(data: imageData)
} else {
print("Image file is currupted")
}
}
}
task.resume()
}
You are using the wrong initializer of URL. That one is for filesystem URLs, not for network URLs. This is the working version of your function:
private func fetchImage(){
if let url = URL(string: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png"), let imageData = try? Data(contentsOf: url){
image = UIImage(data: imageData)
}
}
However, you should completely rewrite your function, because Data(contentsOf:) is a synchronous method and hence should only be used to retrieve local files, not files from the internet.
func fetchImage(from url:URL, completion: #escaping (UIImage?)->Void){
URLSession.shared.dataTask(with: url, completionHandler: { data, response, error in
guard error == nil, let data = data else {
completion(nil);return
}
completion(UIImage(data: data))
}).resume()
}
fetchImage(from: URL(string: "https://zgab33vy595fw5zq-zippykid.netdna-ssl.com/wp-content/uploads/2017/09/blog_1280x720.png")!, completion: {image in
if let image = image {
//use the image
} else {
//an error occured and the image couldn't be retrieved
}
})

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.