I have placed a file inside my App's Root folder. The path of the file would be like "/Users/MyUser/Documents/MyApp/ios/TestFolder/AppConfigKeyFile". I want to read this file at runtime. Below is the code which I have written in AppDelegate to fetch the file
let testFolderURL = URL(fileURLWithPath: #file)
.deletingLastPathComponent()
.deletingLastPathComponent()
.appendingPathComponent("TestFolder", conformingTo: .folder)
let appConfigKeysFileUrl = testFolderURL.appendingPathComponent("AppConfigKeyFile")
print(appConfigKeysFileUrl)
do {
let appconfigKeysData = try Data(contentsOf: appConfigKeysFileUrl)
cancellable = MyJsonDecoder.decode(from: appconfigKeysData).sink { error in
switch error {
case .finished:
break
case .failure(let failure):
print(failure.localizedDescription)
}
} receiveValue: { (appConfigKeys: AppConfigKeys) in
print(appConfigKeys.myKey)
}
} catch {
print(error.localizedDescription)
}
This code works as expected on Simulator. But when I try to run it on actual device it gives me following error "The file “AppConfigKeyFile” couldn’t be opened because there is no such file."
Any help is much appreciated.
Related
I have the following SwiftUI code where a simple button brings up the iOS file manager and allows the user to select a CSV file to be imported. I've found that it works well for files that are stored locally on my device but if I try to select a file from Google Drive or OneDrive it gets a URL but when I then try to retrieve the data from it, it returns an error saying that the file was not found.
After a lot of head scratching, I've found that when using the file browser if I long press to bring up the context menu and then view the info for the file (which I'm guessing may be pulling it down to the phones local cache), it will then work as expected. This is shown in the following animated gif:
I've found that once I've done that caching trick, I can access the file without issue in other apps using the same code and I've also found that I can uninstall my app and reinstall it and it continues to work.
Can anyone advise on an approach using SwiftUI where I can avoid this File Not Found error when trying to import the file from Google Drive or OneDrive?
The entire code that I've been using for testing is as follows:
import SwiftUI
struct ContentView: View {
#State private var isImporting: Bool = false
#State private var fileContentString = ""
#State var alertMsg = ""
#State var showAlert = false
func reportError(error: String) {
alertMsg = error
showAlert.toggle()
}
var body: some View {
VStack {
Button(action: { isImporting = true}, label: {
Text("Select CSV File")
})
.padding()
Text(fileContentString) //This will display the imported CSV as text in the view.
}
.padding()
.fileImporter(
isPresented: $isImporting,
allowedContentTypes: [.commaSeparatedText],
allowsMultipleSelection: false
) { result in
do {
guard let selectedFileURL: URL = try result.get().first else {
alertMsg = "ERROR: Result.get() failed"
self.reportError(error: alertMsg)
return
}
print("selectedFileURL is \(selectedFileURL)")
if selectedFileURL.startAccessingSecurityScopedResource() {
//print("startAccessingSecurityScopedResource passed")
do {
print("Getting Data from URL...")
let inputData = try Data(contentsOf: selectedFileURL)
print("Converting data to string...")
let inputString = String(decoding: inputData, as: UTF8.self)
print(inputString)
fileContentString = inputString
}
catch {
alertMsg = "ERROR: \(error.localizedDescription)"
self.reportError(error: alertMsg)
print(alertMsg)
}
//defer { selectedFileURL.stopAccessingSecurityScopedResource() }
} else {
// Handle denied access
alertMsg = "ERROR: Unable to read file contents - Access Denied"
self.reportError(error: alertMsg)
print(alertMsg)
}
} catch {
// Handle failure.
alertMsg = "ERROR: Unable to read file contents - \(error.localizedDescription)"
self.reportError(error: alertMsg)
print(alertMsg)
}
}
.alert(isPresented: $showAlert, content: {
Alert(title: Text("Message"), message: Text(alertMsg), dismissButton: .destructive(Text("OK"), action: {
}))
})
}
}
The console log output is as follows:
selectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv
Getting Data from URL...
ERROR: The file “example4.csv” couldn’t be opened because there is no such file.
selectedFileURL is file:///private/var/mobile/Containers/Shared/AppGroup/8F147702-8630-423B-9DA0-AE49667748EB/File%20Provider%20Storage/84645546/1aTSCPGxY3HzILlCIFlMRtx4eEWDZ2JAq/example4.csv
Getting Data from URL...
Converting data to string...
First Name,Last Name
Luke,Skywalker
Darth,Vader
My testing has been done on a physical iPhone 12 Pro Max running iOS 14.2 and a physical iPad Air 2 running iPadOS 14.4.
I found an answer to my issue. The solution was to use a NSFileCoordinator() to force the file to be downloaded.
With the code below, if I access a file in cloud storage that hasn't been previously downloaded to the local device it will print "FILE NOT AVAILABLE" but it will now just download the file rather than throwing a file not found error.
Ideally I would like to be able to download just the file property metadata first to check how big the file is and then decide if I want to download the full file. The NSFileCoordinator has a metadata only option but I haven't worked out how to retrieve and interpret the results from that. This will do for now...
if selectedFileURL.startAccessingSecurityScopedResource() {
let fileManager = FileManager.default
if fileManager.fileExists(atPath: selectedFileURL.path) {
print("FILE AVAILABLE")
} else {
print("FILE NOT AVAILABLE")
}
var error: NSError?
NSFileCoordinator().coordinate(
readingItemAt: selectedFileURL, options: .forUploading, error: &error) { url in
print("coordinated URL", url)
let coordinatedURL = url
isShowingFileDetails = false
importedFileURL = selectedFileURL
do {
let resources = try selectedFileURL.resourceValues(forKeys:[.fileSizeKey])
let fileSize = resources.fileSize!
print ("File Size is \(fileSize)")
} catch {
print("Error: \(error)")
}
}
do {
print("Getting Data from URL...")
let inputData = try Data(contentsOf: selectedFileURL)
print("Do stuff with file....")
}
}
This question already has answers here:
how to use writeToFile to save image in document directory?
(8 answers)
Closed 3 years ago.
I am trying to copy a file (audioFile) to DestinationURl however when I do I get this error:
Unable to create directory Error Domain=NSCocoaErrorDomain Code=516 "“20200116183746+0000.m4a” couldn’t be copied to “Documents” because an item with the same name already exists." UserInfo={NSSourceFilePathErrorKey=/<app>/Documents/20200116183746+0000.m4a, NSUserStringVariant=(
Copy
), NSDestinationFilePath=<appURL>/Documents/20200116183746+0000.m4a, NSUnderlyingError=0x600003c2d290 {Error Domain=NSPOSIXErrorDomain Code=17 "File exists"}}
Here I my code
let DirPath = DocumentDirectory.appendingPathComponent(self.txtName.text ?? "NA")
do
{
try FileManager.default.createDirectory(atPath: DirPath!.path, withIntermediateDirectories: true, attributes: nil)
print("\n\n\nAudio file: \(self.audioFile)\n\n\n")
let desitationURl = ("\(DirPath!.path)/")
try FileManager.default.copyItem(atPath: self.audioFile.path, toPath: desitationURl)
}
catch let error as NSError
{
print("Unable to create directory \(error.debugDescription)")
}
I have removed the / on let desitationURl = ("(DirPath!.path)/") and I can see that the file has been generated and the folder is generated however nothing is moved.
Any help will be appreciated
Osian
You need to specify the actual name of the destination file, not just the directory it will go inside. The file manager is literally trying to save your file as the directory name. I cleaned your code up a bit to make it more readable, as well:
guard let dirURL = DocumentDirectory
.appendingPathComponent(self.txtName.text ?? "NA") else { return }
if !FileManager.default.fileExists(atPath: dirURL.path) {
do {
try FileManager.default
.createDirectory(atPath: dirURL.path,
withIntermediateDirectories: true,
attributes: nil)
} catch let error as NSError {
print("Unable to create directory \(error.debugDescription)")
}
}
print("\n\n\nAudio file: \(audioFile)\n\n\n")
let dstURL = dirURL.appendingPathComponent(audioFile.lastPathComponent)
do {
try FileManager.default.copyItem(atPath: audioFile.path, toPath: dstURL.path)
} catch let error as NSError {
print("Unable to copy file \(error.debugDescription)")
}
FlashAir W-04 Fourth Generation SD memory card
In my iOS app directory listing api not working.
Response :
Task .<0> HTTP load failed (error code: -1003 [12:8])
2019-05-21 23:32:40.267572+0530 Razzo[10489:87069] NSURLConnection finished with error - code -1003
Error Domain=NSCocoaErrorDomain Code=256 "The file “command.cgi” couldn’t be opened." UserInfo={NSURL=http://flashair/command.cgi?op=100&DIR=/DCIM}
Please help me what was wrong
below code snippet
private func getdata() {
let url100 = URL(string: "http://flashair/command.cgi?op=100&DIR=/DCIM")
var dirStr: String? = nil
do {
if let url100 = url100 {
dirStr = try String(contentsOf: url100, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
if let dir = dirStr {
arrayfiles = dir.components(separatedBy: "\n")
}
tblContent.reloadData()
}
} catch {
print(error)
self.displayAlert(message: error.localizedDescription)
}
}
Config file below
[Vendor]
CIPATH=/DCIM/100__TSB/FA000001.JPG
APPMODE=4
APPNETWORKKEY=12345678
VERSION=F15DBW3BW4.00.03
CID=02544d535733324755e3c6dc7b012301
PRODUCT=FlashAir
VENDOR=TOSHIBA
MASTERCODE=f437b71e0e4f
LOCK=1
contentsOf: url100 would be for a local file URL. You are not providing a valid file URL.
If your file is remote you need to download the file as data with URLSession. If it is local you need to work out its file URL.
I am trying to download a plist file from a remote location and use it in the iOS app I am creating. The file is going to be used for calendar details within the app's calendar. The goal is obviously that I can update the remote file instead of having to push updates to the app itself every time we need to make changes to calendar details.
I started with the code used in this example: Download File From A Remote URL
Here is my modified version:
// Create destination URL
let documentsUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let destinationFileUrl = documentsUrl.appendingPathComponent("2017.plist")
//let destinationFileUrl = URL(string: Bundle.main.path(forResource: String(currentYear), ofType: "plist")!)
//Create URL to the source file you want to download
let fileURL = URL(string: "https://drive.google.com/open?id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL!)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
// Success
if let statusCode = (response as? HTTPURLResponse)?.statusCode {
print("Successfully downloaded. Status code: \(statusCode)")
}
do {
try FileManager.default.removeItem(at: destinationFileUrl)
try FileManager.default.moveItem(at: tempLocalUrl, to: destinationFileUrl)
print("File was replaced")
print(NSArray(contentsOf: tempLocalUrl))
//print(tempLocalUrl)
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
I originally tried to overwrite the file that is bundled with the app to being with, that resulted in errors. So I instead tried to just save it in the app's documents folder and that removed that error. I had to make sure and remove any previous version of the file because it was giving me a file already exists error after the first run.
While it says everything is working (The outputs for both successful download and replaced file happen) when I print the contents of the array from the downloaded URL it just gives me nil.
This is my first attempt to use any kind of external resources in an app. Before I have always kept everything internal, so I am sure there is something glaringly obvious I am missing.
Update 1:
I realized I didn't have the correct URL to use to download a file from a Google drive. That line of code has been changed to:
let fileURL = URL(string: "https://drive.google.com/uc?export=download&id=0BwHDQFwaL9DuLThNYWwtQ1VXblk")
So now I actually am downloading the plist like I originally thought I was. Even removing the deletion issue mentioned in the first comment, I still can't get the downloaded file to actually replace the existing one.
Update 2:
I have reduced the actual file manipulation down to the following:
do {
try FileManager.default.replaceItemAt(destinationFileUrl, withItemAt: tempLocalUrl)
print("File was replaced")
print(NSArray(contentsOf: destinationFileUrl))
} catch (let writeError) {
print("Error creating a file \(String(describing: destinationFileUrl)) : \(writeError)")
}
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
After the replacement is performed the output of the file shows the correct new contents that were downloaded from the internet.
Later in the code when I try and access the file it seems to be nil in content again.
Look at your download completion code. You:
Delete the file at the destination URL (in case there was one
leftover)
MOVE the temp file to the destination URL (removing it from the temp
URL)
Try to load the file from the temp URL.
What's wrong with this picture?
You are trying to get the contents of the moved file. You already moved the file to destination url and then you are trying to get the contents of the file from temporary location.
For getting file data, Please try the following :
let fileData = try! String(contentsOf: destinationFileUrl, encoding: String.Encoding.utf8)
print(fileData)
I wanna get thumbnails for dropbox files that I display on a view. I know that I've to use the loadThumbnail method, but I don't get exactly how to do it.
I wrote this :
for file in dropboxMetadata.contents {
dbRestClient.loadThumbnail(file.path, ofSize: "s", intoPath: "https://api-content.dropbox.com/1/thumbnails/auto/")
}
but I get some errors like this :
error making request to /1/thumbnails/dropbox/star.jpg - (4) Error Domain=NSCocoaErrorDomain Code=4 "The operation couldn’t be completed.
Thanks for your help !
Petesh has the right idea. intoPath is the destination directory for those thumbnails.
Try this:
func createTempDirectory() -> String? {
let tempDirectoryTemplate = NSTemporaryDirectory().stringByAppendingPathComponent("XXXXX")
let fileManager = NSFileManager.defaultManager()
var err: NSErrorPointer = nil
// remove any previous temporary folder that's there, in case it's there
fileManager.removeItemAtPath(tempDirectoryTemplate)
if fileManager.createDirectoryAtPath(tempDirectoryTemplate, withIntermediateDirectories: true, attributes: nil, error: err) {
return tempDirectoryTemplate
} else {
print("can't create temporary directory at \(tempDirectoryTemplate)")
return nil
}
}
The above code for which I found in this question
Then you could change your own code to do something like:
let temporaryDirectory = createTempDirectory()
for file in dropboxMetadata.contents {
dbRestClient.loadThumbnail(file.path, ofSize: "s", intoPath: temporaryDirectory)
}
If this works, then you could change the "intoPath" parameter into any directory you think is more appropriate.