I have a file which I want to file share. 'hello.mp3'
At the moment the code enables file sharing if the file is in the app.
I was hoping I would be able to do it over http. So file sharing with the link instead of the sound being in the app. I want to do this because it will save memory for the user.
Here is the current code. The code allows file sharing if the file is in the app. I want it so the user will 'download' the sound from a http server such as http://test.com/hello.mp3
Thanks
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSArray *names = [NSArray arrayWithObjects: #"hello.mp3",
#"hi.mp3", nil];
for (NSString *fileName in names)
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *documentDBFolderPath = [documentsDirectory stringByAppendingPathComponent:fileName];
if (![fileManager fileExistsAtPath:documentDBFolderPath])
{
NSString *resourceDBFolderPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:fileName];
[fileManager copyItemAtPath:resourceDBFolderPath toPath:documentDBFolderPath error:&error];
}
}
}
I may be wrong, but that may not be allowed. In the guidelines:
2.7 Apps that download code in any way or form will be rejected.
While an audio file may not be "code", if it is used within the app, perhaps as a language translation item, etc...I would think it would be rejected...just my 2 cents worth though.
Correct, you can "stream" content over HTTP, but if your using HTTP AND saving to the "Documents" directory to hold content you risk rejection by the app store.
If you need a workaround maybe use the "tmp" directory and save a file off a file there. The complaint Apple has is mainly using phone data more than should be in a 3rd party application.
Related
I am creating an epub reader. In that I want to list out .epub files from iphone. So I want to know is there any possible way to list out the .epub files from iphone (not just from the project directory path but also anywhere else in the phone)?
No, this is not possible.
Since there is no filesystem access, except the the directory with in apps sandbox.
All apps have to store the files they use with there sandbox, you tell iOS that you app can op .epub files. Which will allow the user to open the file from, example an email in your app.
As answered by #rckoenes, it is not possible to access the filesystem other than your app bundle.
You can access the files in your app bundle like this:
NSString *bundlePathName = [[NSBundle mainBundle] bundlePath];
NSError *error;
NSArray *bundleContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:bundlePathName error:&error];
for (NSString *currentItem in bundleContents) {
if ([currentItem rangeOfString:#"." options:NSBackwardsSearch].location != NSNotFound) {
int tempIndex = (int)([fileName rangeOfString:#"." options:NSBackwardsSearch].location);
tempIndex++;
NSString *aStrExtension = [[fileName substringWithRange:NSMakeRange(tempIndex, [fileName length]-tempIndex)] lowercaseString];
if ([aStrExtension isEqualToString:#"epub"]) {
//Add this file to an array, to make it available for choosing and view its details
}
}
}
If you mean you want to open ePub files saved not inside your application bundle, then you cant, you will have access only to the files inside your app sandbox
As per the #rckoenes: Any files out of App bundle is not accessible,
So I retrieved .epub files like this way.
NSString *bundleRoot = [[NSBundle mainBundle] bundlePath];
NSFileManager *manager = [NSFileManager defaultManager];
NSDirectoryEnumerator *direnum = [manager enumeratorAtPath:bundleRoot];
NSString *filename;
while ((filename = [direnum nextObject] )) {
if ([filename hasSuffix:#".epub"]) { //change the suffix to what you are looking for
[arrayListofEpub addObject:[filename stringByDeletingPathExtension]];
}
}
In the Simulator I can save an NSMutableArray to a file and read it back with the following code:
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:#"RiskValues"]){ // If file exists open into table
NSLog(#"Risk Values File Exists");
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullFileName = [NSString stringWithFormat:#"RiskValues", documentsDirectory];
gRiskValues = [[NSMutableArray alloc] initWithContentsOfFile:fullFileName];
gRiskValuesAlreadyInitialised = YES;
} else {
NSLog(#"Can't find RiskValues file, so initialising gRiskValues table");
Do something else .......
}
This doesn't work on the device. I have tried to locate the file using the following but it still doesn't work:
NSString *fullFileName = [documentsDirectory stringByAppendingPathComponent#"RiskValues"];
What am I doing wrong?
Great answers from everyone. I have resolved the file path and existence issues at a stroke. Many, many thanks.
You have to provide absolute path here:
if ([fileManager fileExistsAtPath:#"RiskValues"])
So it must look like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullFileName = [documentsDirectory stringByAppendingPathComponent:#"RiskValues"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath: fullFileName]){ // If file exists open into table
NSLog(#"Risk Values File Exists");
gRiskValues = [[NSMutableArray alloc] initWithContentsOfFile:fullFileName];
gRiskValuesAlreadyInitialised = YES;
} else {
NSLog(#"Can't find RiskValues file, so initialising gRiskValues table");
Do something else .......
}
NSString *fullFileName = [NSString stringWithFormat:#"RiskValues", documentsDirectory];
this line, you're not creating your full path string right. what you should do is
NSString *fullFileName = [documentsDirectory stringByAppendingPathComponent:#"RiskValues"];
also this check
if ([fileManager fileExistsAtPath:#"RiskValues"])
Will never pass on iOS as it is not a full path to any place you are allowed to write at in your sandbox. I suppose it works on the simulator because on the mac it's looking up relatively to the HD root (or something, not sure how the mac file system works :) ), but on the iOS you're going to have to give it a path to a file/directory in your documents (maybe by appending #"RiskValues" to it or whatever)
1) [NSString stringWithFormat:#"RiskValues", documentsDirectory] is just #"RiskValues". So this name points to file in application's directory.
2) [fileManager fileExistsAtPath:#"RiskValues"] searches for file in application directory. It's available for read/write in simulator (it's in your computer file system after all) but it's read-only on device.
BTW (NSFileManager Class Reference)
Attempting to predicate behavior based
on the current state of the file
system or a particular file on the
file system is not recommended. Doing
so can cause odd behavior in the case
of file system race conditions. It's
far better to attempt an operation
(such as loading a file or creating a
directory), check for errors, and
handle any error gracefully than it is
to try to figure out ahead of time
whether the operation will succeed.
Solution:
1) Do not check file presence. Just try to make dictionary with initWithContentsOfFile:encoding:error:
2) You want it to be in documents directory so construct path like this
[documentsDirectory stringByAppendingPathComponent:#"RiskValues"];
I am building an add-on to my app where the user can search for an item in a list that is pre-populated with data from a .plist file. It is an NSDictionary. If the term, the user searched for, does not exist, the user can tap a + button and add it so it is there the next time.
First of I thought it would be as easy as using the NSUserDefaults, but a few problems arises.
To have the list included I must place it in the bundle, but if it is there I can not add new key/value pairs to it. This I can only do with files situated in the Documents folder.
So I guess I have to bundle the plist, then on first run I'll move it to the documents folder and access it there.
This opens up the problem when I need to update the app, I guess it will overwrite the values the user put in.
Is there a secure, easy-understandable, right way to achieve the functionality I describe?
Thanks for any help given:)
Edit: **** the actual approach, as suggested by TheSquad and TomH *****
+ (NSMutableDictionary*) genericProducts {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *documentPlistPath = [documentsDirectory stringByAppendingPathComponent:#"GenericProducts.plist"];
NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
NSString *bundlePlistPath = [bundlePath stringByAppendingPathComponent:#"GenericProducts.plist"];
if([fileManager fileExistsAtPath:documentPlistPath]){
NSMutableDictionary *documentDict = [NSMutableDictionary dictionaryWithContentsOfFile:documentPlistPath];
return documentDict;
} else {
NSError *error;
BOOL success = [fileManager copyItemAtPath:bundlePlistPath toPath:documentPlistPath error:&error];
if (success) {
NSMutableDictionary *newlySavedDict = [NSMutableDictionary dictionaryWithContentsOfFile:documentPlistPath];
return newlySavedDict;
}
return nil;
}
}
And for adding a new product to the list:
+ (void) addItemToGenericProducts:(NSString*) newProduct {
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *documentPlistPath = [documentsDirectory stringByAppendingPathComponent:#"GenericProducts.plist"];
NSMutableDictionary *documentDict = [NSMutableDictionary dictionaryWithContentsOfFile:documentPlistPath];
[documentDict setObject:newProduct forKey:[MD5Checksum cheksum:newProduct]];
[documentDict writeToFile:documentPlistPath atomically:YES];
}
I had the same thoughts with my sqlite database...
I end up doing exactly that, copy the bundled file into documents in order to be able to modify it.
What I have done is checking at each startup if the file exist, if it does not, copy it.
If you do an update of your App, the documents folder will not be touch, this means the copied file from the previous version will still be present.
The only issue is that if you want your plist to be upgraded you will have to handle that in your application. If you have to do so I suggest you use the NSUserDefault to check if a previous version of the app existed before...
The contents of the documents directory is not altered when an application is updated.
The contents of the documents directory are deleted when the user deletes the app.
When the app is run the first time write a flag to NSUserDefaults. On subsequent runs of the app, check for existence of the flag. (alternatively, you can just check for existence of the plist in he documents directory)
NSString *myfile = [[NSBundle] mainBundle] pathForResource:#"fileName" ofType:#"plist"];
NSMutableArray *mydata= [[NSMutableArray alloc] initWithContentsOfFile:myfile];
/* code to modify mydata */
[mydata writeToFile:myfile atomically:YES]
In case of simulator 'fileName.plist' is modified but in case of iphone device file remains unchanged. There is no exception seen either.
Is the above code expected to work fine on both iphone and simulator ?
Also in the debugger when I hover over 'mydata' I see different values in case of simulator and device. In case of simulator I see for example, '5 objects' but in case of actual device it shows '{(int)[$VAR count]}'. Could this be related to file not being written ?
You can't write to files in a bundle's resource directory. On top of that you wouldn't want to, because any changes would be overwritten when you update your app. The documents directory persists across versions and (I believe) it is backed up via itunes.
Here is a snippet that checks the documents directory for a plist. if the file doesn't exist it copies a plist out of the resources into the documents directory.
BOOL success;
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"score.plist"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return success;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"score.plist"]];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
return success;
I cache the images to the document directory of my app using the following code.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *saveDirectory = [paths objectAtIndex:0];
But even after I close the application, its still there. How can I clear it when I lose my app. I am doing all this in my simulator.
I would implement applicationWillTerminate: in my application delegate and remove the cache files there. Or better yet, as suggested by Vladimir, save them in a temporary directory and let the OS clean them up when needed.
- (void)applicationWillTerminate:(UIApplication *)app
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *cacheFiles = [fileManager contentsOfDirectoryAtPath:saveDirectory error:error];
for (NSString *file in cacheFiles) {
error = nil;
[fileManager removeItemAtPath:[saveDirectory stringByAppendingPathComponent:file] error:error];
/* handle error */
}
}
If you do not want your cached images to be preserved after application is closed better save them to temporary directory - they will be removed automatically.
If you want to manually remove the files you must store the paths for them and use the following NSFileManager function:
- (BOOL)removeItemAtPath:(NSString *)path error:(NSError **)error
Edit: sorry, I appeared to be wrong here about automatic deleting. Here's a quote from Developing Guide:
Use this directory to write temporary files that you do not need to persist between launches of your application. Your application should remove files from this directory when it determines they are no longer needed. (The system may also purge lingering files from this directory when your application is not running.)
NSString *file;
NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:saveDirectory];
NSError* err;
while (file = [dirEnum nextObject]) {
err = nil;
[[NSFileManager defaultManager] removeItemAtPath:[saveDirectory stringByAppendingPathComponent:file] error:&err]];
if(err){
//print some errror message
}
}
Use the temporary directory path as specified in this question:
How can I get a writable path on the iPhone?