This is a question I am sure Xcode developers could also answer. I have a screenshot of my code below in Xamarin.
Why am I getting an Unauthorized access exception? I should be able to write to the documents folder right?
var webClient = new WebClient();
//var documentsFolder = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // iOS 7 and earlier
var documentsFolder = NSBundle.MainBundle.BundlePath;
var fileNameAndPath = Path.Combine (documentsFolder, "verses.xml");
if(!File.Exists(documentsFolder)){
Directory.CreateDirectory(documentsFolder);
if(!File.Exists(fileNameAndPath)){
//File.SetAttributes(fileNameAndPath, FileAttributes.Normal);
File.Create(fileNameAndPath);
//Throws exception here.
}
}
And ERRORs:
Access to the path "/var/mobile/Applications/1F95D694-BBA5-4FB3-AE6C-0C2CDD9DEDD8/comexample.app/verses.xml" is denied
Access to the path '/private/var/mobile/Applications/1F95D694-BBA5-4FB3-AE6C-0C2CDD9DEDD8/Documents/verses.xml' is denied.
I have tried both paths and I get access denied.
The BundlePath is not a writable area for iOS applications.
From the Xamarin notes at http://developer.xamarin.com/guides/ios/application_fundamentals/working_with_the_file_system/
The following snippet will create a file into the writable documents area.
var documents =
Environment.GetFolderPath (Environment.SpecialFolder.MyDocuments); // iOS 7 and earlier
var filename = Path.Combine (documents, "Write.txt");
File.WriteAllText(filename, "Write this text into a file");
There's a note on the page for iOS 8 that a change is required to get the documents folder
var documents = NSFileManager.DefaultManager.GetUrls (NSSearchPathDirectory.DocumentDirectory,
NSSearchPathDomain.User) [0];
Assuming you're on iOS 8, the documents directory isn't connected to the bundle path. Use the function NSSearchPathForDirectoriesInDomains() (or URLsForDirectory:inDomains:) to find the documents directory.
I assume it should be a Directory.Exists() where you check if the directory exists:
if(!File.Exists(documentsFolder)){
Related
I am trying to download the pdf file and trying to view it from my app using xamarin.forms. I am able to create a path in android device but not able to create the path on the iPad. I need to create the custom folder and need to download the files in that directory. How to achieve that.
string decoded = System.Text.Encoding.ASCII.GetString(fileBytes);
byte[] encodedDataAsBytes = DecodeUrlBase64(DataString);
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string finalPath=Path.Combine(documentsPath, Path.GetFileName(fileFullPath));
Directory.CreateDirectory(finalPath);
File.WriteAllBytes(finalPath, encodedDataAsBytes);
Everything is working fine when I removed CreateDirectory, Following are the changes to my code which is working as I expected
byte[] encodedDataAsBytes = DecodeUrlBase64(DataString);
string documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string finalPath = Path.Combine(documentsPath, Path.GetFileName(fileFullPath));
File.WriteAllBytes(finalPath, encodedDataAsBytes);
My app has just been rejected by Apple because it was storing temporary or cache files in the documents directory. Right. Their rejection message states:
Temporary files used by your app should only be stored in the /tmp directory
I suppose it is that besides the Documents and Library in the Application's folder.
I am now trying to debug this issue in the iPhone Simulator, and when I use NSTemporaryDirectory(), the value I get in the Xcode debugger is /var/folders/yj/gnz1c7156c7d6d4fj429yms40000gn/T/tempzip.zip, and not /Users/me/Library/Application Support/iPhone Simulator/5.1/Applications/8F71AB72-598C-427A-A116-36833D3209F7/tmp/tempzip.zip.
So: is NSTemporaryDirectory() having a different behaviour using the iPhone Simulator, and, is it possible to track the application's temporary directory at debug time ?
iOS 9 or later • Swift 3 or later
let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory())
iOS 10.0+Beta, macOS 10.12+, tvOS 10.0+Beta & watchOS 3.0+ • Xcode 8 • Swift 3 or later
let tmpDirURL = FileManager.default.temporaryDirectory
UPDATED 2016 ANSWER :
Data which is explicitly accepted by the user as personal, and potentially backuped in his/her iCloud space, should be written in user's "Documents" directory
Data that belongs and extends your application (an extension user can download for instance,...), but which is NOT in the bundle, should be written in a subfolder of "Application Support/" directory, having the title of your appID. It can also be the "Cache" directory.
Data with short-life time can be stored in the tmp directory of your application. In this case, use of NSTemporaryDirectory() is possible to get the "tmp" directory. Check this link for additional help.
Check this official iOS developement Apple page in section "Determining Where to Store Your App-Specific Files" for explanations.
Below are 3 functions in Swift designed to return NSURLs to these directories and make your like simpler.
Swift:
func GetDocumentsDirectory()->NSURL{
//returns User's "Documents" directory
//something like this on a real device : file:///private/var/mobile/Containers/Data/Application/APPID/Documents/
//something like this on the simulator : file:///Users/MACUSERID/Library/Developer/CoreSimulator/Devices/SIMDEVICEID/data/Containers/Data/Application/APPUUID/Documents/
let filemgr = NSFileManager.defaultManager()
let docsDirURL = try! filemgr.URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
return docsDirURL
}
func GetApplicationSupportDirectory()->NSURL{
//returns Application's support directory
//something like this on a real device : file:///private/var/mobile/Containers/Data/Application/APPID/Library/Application%20Support/YOURAPPBUNDLEID/
//something like this on the simulator : file:///Users/MACUSERID/Library/Developer/CoreSimulator/Devices/SIMDEVICEID/data/Containers/Data/Application/APPUUID/Library/Application%20Support/YOURAPPBUNDLEID/
let AllDirectories : [NSURL]
var ApplicationSupportDirectory : NSURL=NSURL.init()
var ApplicationDirectory : NSURL=NSURL.init()
AllDirectories=NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)
if AllDirectories.count>=1{
ApplicationSupportDirectory=AllDirectories[0]
}
if !ApplicationSupportDirectory.isEqual(nil) {
ApplicationDirectory=ApplicationSupportDirectory.URLByAppendingPathComponent(NSBundle.mainBundle().bundleIdentifier!)
}
return ApplicationDirectory
}
func GetTemporaryDirectory()->NSURL{
//returns Application's temporary directory
//something like this on a real device : file:///private/var/mobile/Containers/Data/Application/APPID/tmp/
//something like this on the simulator : file:///Users/MACUSERID/Library/Developer/CoreSimulator/Devices/SIMDEVICEID/data/Containers/Data/Application/APPUUID/tmp/
return NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true)
}
I have tested this on a real device, and it returned : "/private/var/mobile/Applications/C82383-EBD6-4F72-BC16-A865478D27/tmp/tempzip.zip"
So overall, using NSTemporaryDirectory() is the correct way of finding the path to the temporary directory, and that if you want to debug and view what is done within, you need to find it manually in the Finder if you are using the iPhone Simulator.
Check newer answer below (this one is deprecated)
According to the docs, you should avoid NSTemporaryDirectory() in favour of this approach
- (NSURL)URLForTemporaryFolder
{
// Get a parent folder, trying user folder (fails iOS) and falling back to AppSupport and Docs
NSURL *parentFolderURL = [NSURL URLForDirectory:NSUserDirectory domainMask:NSUserDomainMask];
if (!parentFolderURL) parentFolderURL = [NSURL URLForDirectory:NSApplicationSupportDirectory domainMask:NSUserDomainMask];
if (!parentFolderURL) parentFolderURL = [NSURL URLForDirectory:NSDocumentDirectory domainMask:NSUserDomainMask];
// Get the temp folder URL using approach outlined in the docs
NSURL *tmpURL = [[NSFileManager defaultManager]
URLForDirectory:NSItemReplacementDirectory
inDomain:NSUserDomainMask
appropriateForURL:parentFolderURL
create:YES
error:NULL];
return tmpURL;
}
Be aware that this creates a new unique temp folder each time you call it and it's up to you to clean it up.
We are currently developing an iOS10 app, including "Messages Extension".
To share CoreDatas persistant store.sqlite inbetween App and Extension, we are using a shared "Apple App Group" directory, which is working fine.
Now we have to get our hands on the store for debug reasons and are unable to find the directory. The Apps container directories are completely empty, which makes sense. But how to download our database? Do we have to somehow copy it programmatically to a reachable place?
To sum it up:
We already use CoreData which stores model.sqlite in our shared directory.
Everything is up and running.
What we want to archive is to download the database to our computer.
Without a shared directory we can simply download the App container from the device, using Xcode->Devices. But as we do use a shared directory, the .sqlite database is not within the container.
Question:
How can we download the .sqlite database from the device to our computer?
EDIT on 2018-10-12: Updated code for Swift 4.x (Xcode 10). (Older version retained for reference.)
In Swift 4.x:
let sharedContainerURL :URL? = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.etc.etc")
// replace "group.etc.etc" above with your App Group's identifier
NSLog("sharedContainerURL = \(String(describing: sharedContainerURL))")
if let sourceURL :URL = sharedContainerURL?.appendingPathComponent("store.sqlite") {
if let destinationURL :URL = FileManager().urls(for: .documentDirectory, in: .userDomainMask).first?.appendingPathComponent("copyOfStore.sqlite") {
try! FileManager().copyItem(at: sourceURL, to: destinationURL)
}
}
In older version of Swift (probably Swift 2.x):
let sharedContainerURL :NSURL? = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.etc.etc") // replace "group.etc.etc" with your App Group's identifier
NSLog("sharedContainerURL = \(sharedContainerURL)")
if let sourceURL :NSURL = sharedContainerURL?.URLByAppendingPathComponent("store.sqlite")
{
if let destinationURL :NSURL = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0].URLByAppendingPathComponent("copyOfStore.sqlite")
{
try! NSFileManager().copyItemAtURL(sourceURL, toURL: destinationURL)
}
}
Something like the above will get a file from the app group's shared container to the app's Documents directory. From there, you could use Xcode > Window > Devices to get it to your computer.
You could also use iTunes file sharing to retrieve the file from the app's Documents directory after setting UIFileSharingEnabled to YES in the Info.plist file, but bear in mind that this will expose the directory's contents to the user as well. Should be okay for development/debugging purposes, though.
I looked at many SO question where the user wanted to access a document in his app directory. I am new to all of this and just want some clarification on the matter. I am building an open-source content blocker for iOS 9.
You can see the file tree. I wonder if it is possible to access the file called blockerList.json which is in the folder Adblock Content Blocker. My goal was to write the json file based on what the user want to build. I need to be able to modify the content of this file. Is this possible or should I stop trying and leave it like that?
Thanks for any help.
The app bundle is readonly as #dan said. So you have to copy the file from bundle into the Document directory then you able to modify the file at anytime.
Copy file into document like this:
class func copyFile(fileName: String) {
let destPath: String = getPathInDocument(fileName)
var fromPath = NSBundle.mainBundle().pathForResource("blockerList", ofType: "json")!
var fileManager = NSFileManager.defaultManager()
if !fileManager.fileExistsAtPath(destPath) {
fileManager.copyItemAtPath(fromPath, toPath: destPath, error: nil)
} else {
println("The file allready exists at path:\(destPath)")
}
}
class func getPathInDocument(fileName: String) -> String {
return NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0].stringByAppendingPathComponent(fileName)
}
Hope this will help you!
Note : You have readonly permission for application's bundle file.
Firstly copy file in NSDocumentDirectory with backup attribute. Use refrerence copy file to document directory iphone and for How do I prevent files from being backed up to iCloud and iTunes?
Check references ImportingJSONFile and Y6JSONFileManager-iOS to read and write changes in JSON File.
I want to allow my app to download images from the web to be placed in /images and then displayed in the UI - this is to allow branding specific images to be downloaded for a multitenant app.
The app downloads the image fine and places the image into the /Images folder
However when trying to load the image using UIImage.FromFile it always returns null - even though I know full well the image is there!
I know UIImage.FromBundle caches stuff so I chose to use FromFile but this doesn't seem to work?!
Is there something I need to do to make this work? Or am I pushing my luck a little?
I am using the following to create the file path:
public static UIImage SafeClaim_Logo = UIImage.FromFile(NSBundle.MainBundle.BundlePath +"/Images/Logo.png");
The following is the code I use to download and save the file - the file path from the above is the same below when I inspect it
HttpClient client = new HttpClient ();
var clientResponse = await client.GetByteArrayAsync(url);
using (FileStream fs = new FileStream(NSBundle.MainBundle.BundlePath + path, FileMode.OpenOrCreate))
{
var bytes = clientResponse;
fs.Write(bytes, 0, bytes.Length);
}
The app bundle is read-only so you can't change or add something in there at runtime.
All content created at runtime should be stored in your documents or cache folders.
You can get the documents folder by calling:
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
One good place to store this kind of files is the Library/ folder. On iOS 7 you can access it using:
var libraryFolderPath = Path.GetFullPath(
Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments), "..", "Library"));
On iOS 8:
var url = NSFileManager.DefaultManager.GetUrls (
NSSearchPathDirectory.LibraryDirectory, NSSearchPathDomain.User) [0];
var libraryFolderPath = url.Path;
The reason is explained by Xamarin documentation:
The Library directory is a good place to store files that are not
created directly by the user, such as databases or other
application-generated files. The contents of this directory are never
exposed to the user via iTunes.
You can create your own subdirectories in Library; however, there are
already some system-created directories here that you should be aware
of, including Preferences and Caches.
The contents of this directory (except for the Caches subdirectory)
are backed up by iTunes. Custom directories that you create in Library
will be backed up.
If you need to enable iTunes file sharing and expose the content of the Documents/ folder, your files are not visible to the user.