Is it possible to have a self-updating iPhone application?
I believe the answer is no, but I am not sure.
I have a database driven app that is in effect a catalogue of products. I want the database to be on the users iPhone so the catalog is fast to use. However, periodically the content in the database changes.
I could create a push notification to tell the user that there is a database update ready, but it would be better if the app updates itself.
As fluchtpunkt answered, an iOS app can download new data, it just can't download new code. Many iOS apps, such as the multitude of Twitter clients, primarily download and display data.
Where an app will get into trouble is if its interface doesn't make clear the app is downloading data. An app shouldn't use up an iPhone's data plan allotment without the user knowing. Letting the user know can range from explicit notifications including the option to cancel the download to making the concept of the app all about downloading data (e.g. Twitter clients).
The user must also be notified if the app is sharing any information with the server. The app may want to send such data to selectively download specific data. For example the app could use location information to tailor the database, downloading only items available in the region the device happens to be. A login process may be sufficient notification, if the data is account specific.
as long as you don't need to change anything inside the app-bundle this is possible. Just save your content to the Documents Directory.
And of course you are allowed to update your own database.
It's not possible to load an executable on the iPhone, although it's worth noting that you can load resource bundles dynamically. If you use IB for creating your views, then you can create an entirely new view, put it up on the cloud and load it from the app. The only caveat is that you can only update the design, but not extend functionality.
To load a XIB dynamically, use the initWithNibName method,
- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
and pass in the dynamically loaded bundle.
On the Mac, it is possible to have bundles that contain executable code which can be dynamically loaded, but not on the iPhone. There is a Plist configuration named NSPrincipalClass for the Mac, but unfortunately not for the iPhone yet.
u can do it with push notification apart from that u can set timer which will download new data every specified time but the problem is the app has to be open all the time.in ios 4 there is local notification which can send notificationn to user even when app is closed
My app uses an SQLite database and updates it. With every start (or waking up from suspended mode) it connects to my web server and checks for a new database. To authenticate the database, it first only downloads a file containing the MD5 sum of the online database and calculates the MD5 sum of the database on the phone. Only if the sums differ, the new database will be downloaded. This has the nice side effect that it keeps the traffic low.
Sample code for calculatimg the MD5 sum:
#define CHUNK_SIZE 16384
#import <CommonCrypto/CommonDigest.h>
+ (NSString *)md5SumForFileAtPath:(NSString *)path {
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:path];
CC_MD5_CTX md5;
CC_MD5_Init(&md5);
BOOL done = NO;
while(!done)
{
NSData* fileData = [handle readDataOfLength:CHUNK_SIZE];
CC_MD5_Update(&md5, [fileData bytes], [fileData length]);
if ([fileData length] == 0) done = YES;
}
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5_Final(result, &md5);
NSString *digest = [NSString stringWithFormat:#"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],result[4],
result[5], result[6], result[7], result[8], result[9],
result[10], result[11], result[12], result[13],
result[14], result[15]
];
XLog("Checksum for file %#: %#", path, digest);
return digest;
}
However, downloading the entire database is only a good solution if the database is not to large. Additionally, I gzipped the database and extract it after the download.
#import <zlib.h>
+ (void)gunzipFileAtPath:(NSString *)zippedPath toPath:(NSString *)unzippedPath {
gzFile file = gzopen([zippedPath UTF8String], "rb");
FILE *dest = fopen([unzippedPath UTF8String], "w");
unsigned char buffer[CHUNK_SIZE];
int uncompressedLength;
while (uncompressedLength = gzread(file, buffer, CHUNK_SIZE) ) {
if(fwrite(buffer, 1, uncompressedLength, dest) != uncompressedLength || ferror(dest)) {
NSLog(#"error writing data");
}
}
fclose(dest);
gzclose(file);
}
It is perfectly fine to only show the network activity indicator in the status bar and not use a progress bar or other indicator. If the phone's database is up to date, I do not even notify the user, as it is unnecessary information and will only distract him. However, if there is an update, I fade in an overlay of the status bar and display the information for a few seconds. From the feedback of my users, I can tell that they pretty much appreciate this solution.
Do not forget that you add cryptography by calculating the MD5 sum. You have to indicate this to Apple when uploading your next Update. I only had to answer one more question and say that I use encryption only for authentication. The app was approved without any problems.
Related
I need to secure my data in Documents Directory so I have used Apple Data Protection API. Apple Asset
I Enabled Data Protection in Settings App. Settings->General->Passcode Lock On
I Write the file Using the NSDataProtectionKey
[data writeToFile:imagePath options:NSDataWritingFileProtectionComplete error:nil];
File is written successfully
I get the file attributes
[[NSFileManager defaultManager] attributesOfItemAtPath:fullPath
error:NULL];
It returns this the attributes like this as you can see the NSFileProtectionKey is NSFileProtectionComplete
NSFileCreationDate = "2013-01-22 06:10:48 +0000";
NSFileExtensionHidden = 0;
NSFileGroupOwnerAccountID = 501;
NSFileGroupOwnerAccountName = mobile;
NSFileModificationDate = "2013-01-22 06:10:48 +0000";
NSFileOwnerAccountID = 501;
NSFileOwnerAccountName = mobile;
NSFilePosixPermissions = 420;
NSFileProtectionKey = NSFileProtectionComplete;
NSFileReferenceCount = 1;
NSFileSize = 8964;
NSFileSystemFileNumber = 335997;
NSFileSystemNumber = 16777218;
NSFileType = NSFileTypeRegular;
I am sure that I have written the file successfully and I read that file successfully. But when I open this device in iExplorer(A external iOS device reader app for mac) it shows me the file written without encrypted.
Without encrypted means I write a image file. But I am able to view that image(in documents Directory) in iExplorer. But my purpose is to encrypt that. Because I dont want users to see those files. I am aware other encryption AES Algorithms. But want to know why Data Protection API didn't work.
Note: I Use iPad2 iOS 6.0.1 and
Logs recorded in device
But want to know why Data Protection API didn't work.
It did work, you just expected more than what it does.
The encryption iOS applies to the files is transparent to anything in the userland. The encryption is done at hardware level, so that nobody who steals the Flash memory of the device can access the data. However, if the screen is unlocked and the device is turned on, the OS handles these files just like every other file, and they don't appear to be "encrypted" from within the filesystem.
If you want to do this, you have to encode the file using some asymmetric encryption method, such as AES (OpenSSL or GnuTLS would do the job well).
I am able getting data from server and display on UITableView, It's working fine. But when I go to another tab and return to same tab it will call to server for data. I want to store data locally once get from server.
Please suggest me your thoughts.
Thanks!
You have to store the file on the device and this is possible in a few ways:
Store the files in the App its Documents directory
Store the files in the App its Library/Caches folder
Store the files in the App its Library/ folder
There are a few considerations for each option:
Files stored in the Documents folder are by default backed up by iCloud. You really have to think if this is necessary because you're eating up iCloud storage space from the user (for which he/she may pay).
Storing files in the Caches folder is a good way, but they may be deleted by iOS if the users disk is running low on space (therefore the name Cache).
Storing files in another (manually created) folder on the device will keep them as long as the app is installed. No iCloud backup or no removal by iOS
Getting the location of the specific folder can be done using:
[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
Where you have to replace NSDocumentDirectory by NSLibraryDirectory when you want the NSURL to the Library folder.
Storing data is as easy as creating an NSURL which points to the location where you want to store the data and then call the writeToURL:atomically: method on the NSData you want to store.
Converting UIImages to NSData can be done using UIImageJPEGRepresentation or UIImagePNGRepresentation.
i would prefer to use the HJCache library for the temporally store images.
For that followings are the reasons to implement the HJCache open source library.
Make it easy to use asynchronously loaded images in iOS apps.
Make it easy to use a shared file cache.
Make memory management ‘just work’ according to Cocoa norms.
Support typical cases of viewing images, eg scrolling through a list of tweets showing the tweeters profile pic, swiping through a photo album loaded over the net.
Allow the library to be used for other kinds of data too.
For more detail and tutorial visit the github and author blog
I have made an application for my client by keeping target iOS as 4.
But since the application still not submitted to Apple store, my client is planning to upgrade it for iOS 5.0.
For this I read the guideline from Apple and found that "Only user-generated data or that cannot otherwise be recreated by your application, should be stored in the /Documents directory and rest should be stored to /Library/Caches directory"
In my application, I am using server model of in-app purchase for non-consumable product. For this I am storing all my downloaded data (which are basically books or magazines) to Documents directory. The Database is also present in the same directory which contains the details about the downloaded products.
My question is,
1. Should I have to change my code to store the downloaded data to Library/Caches directory instead of to the Documents directory?
2. Where should my database file be placed (to Documents or Caches)?
If I put it products in the Caches then I have to change the logic of retrieval also, since it is considered that if record is present in database, there is no need change the existence of the file and it directly opens it when user clicks on the magazine.
Kindly guide me on this issue.
Thanks in advance.
UPDATED:
I am updating this for those who are still not sure about this problem.
Using the guideline of accepted answer, I have implemented this in 2 of my applications and submitted them to Apple Store. Both were approved in review.
This may promote that the solution suggested in the accepted answer is correct.
Here are the trade-offs:
If you put your files in the Documents directory then they are backed up to iTunes or iCloud but if they are too big and it's possible to download the files again then Apple may reject your app
If you put your files in the Cache directory then they won't be backed up and Apple won't reject your app. However, when iOS 5 gets low on space it may delete all the files in there.
However, with iOS 5.0.1 there is a third option:
Put files in Documents but flag them so that they are not backed up. There's a technote (QA1719) on how to do this.
I think this is probably the best answer for you.
1. Should I have to change my code to store the downloaded data to Library/Caches directory instead of to the Documents directory? = Yes, you need to store the downloaded data to Library/Caches directory.
2. Where should my database file be placed (to Documents or Caches)? = You can keep the database in Documents directory.
One app I know was once rejected because of this. Storing downloadable data in Documents dir is not recommended. The logic behind it is that your data should not unnecessarily inflate the app directory. This app directory is backed up in iCloud, so inflated app directory will cause more data to be saved in iCloud.
If data can be downloaded again, like magazines, books pdf etc. Then keep it in Caches directory. Of course you can store pointers to data (like URLs etc) in the Documents directory so that the user can retrieve them later.
To answer your questions:
Yes, change your code to load DB from Documents and Data from Caches.
Keep database in Documents
You'll have to add code to check if a document in DB exists in Caches, if it doesn't your app should download it again.
On top of the suggestion that you should keep the data in cache directory, there is a concern that you should keep in mind when keeping the data into cache folder:
Whenever iOS feels the memory crunch it deletes all the cache and temp folder.
Problem is described here in detail
To protect these directory not to delete and keep everything for lifetime you should use the attribute that will keep the mentioned directory safe.
Solution is here:
I was looking for same query and I have got the solution .
According to the apple data storage documentation dev may stored the data in cache even user want to persist that data in low memory situation.develper just need to set Do not back up flag.
Use the "do not back up" attribute for specifying files that should remain on device,
even in low storage situations. Use this attribute with data that can be recreated but
needs to persist even in low storage situations for proper functioning of your app or
because customers expect it to be available during offline use.
This attribute works on marked files regardless of what directory they are in, including the Documents directory.
These files will not be purged and will not be included in the user's iCloud
or iTunes backup. Because these files do use on-device storage space, your app is
responsible for monitoring and purging these files periodically.
For more Information go through this link
Use Below Piece of code for setting the do not back up Flag.
import <sys/xattr.h>
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
if (&NSURLIsExcludedFromBackupKey == nil) { // iOS <= 5.0.1
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
} else { // iOS >= 5.1
NSError *error = nil;
[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
return error == nil;
}
}
URL is the path of Data.
I have made an application for my client by keeping target iOS as 4.
But since the application still not submitted to Apple store, my
client is planning to upgrade it for iOS 5.0
Another option (if you are only going to support iOS 5) is to use NewsstandKit. This way only selected issues of the magazine will be deleted by iOS when low on "disk" space. My understanding is that iOS will delete selected issues based on the last time read and maybe the size.
If you don't use newsstand and your app is targeted, all the issues will be deleted.
EDIT: So far, the best I've been able to come up with is a pop-up to ask the user to disable iCloud sync, along with moving all the data to the Documents directory so it won't get wiped: In iOS5, is it possible to detect if a user has an app set to back up?
I develop offline mapping application for iPhone/iPad.
We used to store all of the data (many gigs potentially) in the Caches directory.
As of iOS5, the files in the Caches directory can be randomly deleted when the user's hard drive starts getting full.
How can I store local data, without the data being synced to iCloud, iTunes, and without it being randomly deleted? My local data is a large directory tree with many small data files, in thousands of subdirectories.
I moved our directory tree from the library cache directory to a data.nosync directory in the documents directory, because we had read this might be a solution. However, the data in the nosync folder is still being backed up to iCloud.
Here is now I create the directory:
NSString* noSyncDirectory() {
static NSString *directory = nil;
if (!directory) {
directory = [[NSString stringWithFormat:#"%#/%#",
documentsDirectory(), #"data.nosync"] retain];
[Constants createDirectoryIfNeeded:directory];
}
return directory;
}
From: https://developer.apple.com/library/ios/#qa/qa1719/_index.html
You can use the following method to set the "do not back up" extended attribute. Whenever you create a file or folder that should not be backed up, write the data to the file and then call this method, passing in a URL to the file.
#include <sys/xattr.h>
- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
const char* filePath = [[URL path] fileSystemRepresentation];
const char* attrName = "com.apple.MobileBackup";
u_int8_t attrValue = 1;
int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
return result == 0;
}
More information can be found: https://developer.apple.com/icloud/documentation/data-storage/
Further note: While the developer documentation incorrectly implies ("These files will not be purged and will not be included in the user's iCloud or iTunes backup.") that the do-not-backup flag doubles as a do-not-purge flag that is not the case. Simply leaving files in the Caches Directory and flagging them do-not-backup will not prevent their wipe.
Perhaps you can disable backup for your app and store data files somewhere else in the app tree. Any stuff that needs to be backed can be put in a common area outside your app.
You might be able to do it in provisioning: invalid code signing app submission
or settings:
When you want to give the user the option to enable or disable iCloud usage entirely for your app. If your app includes a Settings bundle or inline preferences, you could include a preference to toggle whether your app stores content in iCloud at all. For example, an app whose data consists entirely of privately managed files might do this to give the user the choice of how those files are stored.
or by removing the com.apple.developer.ubiquity-container-identifiers entitlement (which could get auto-added) with Xcode: Configuring Your App's iCloud Entitlements
Otherwise you might need to issue a warning with instructions on disabling through the UI:
http://www.pcmag.com/article2/0,2817,2394702,00.asp#fbid=bpIwPLZ1HeQ
Another workaround is to group the maps into collections that are installed as separate applications. That would be a way to store the data without creating any directories that sync or get backed-up. The data will be stored in the the .app directory and will be protected.
Depending on how the cache space reclamation function works, it might not delete recently accessed or modified files. You could try periodically touching them on a timer. You could also add some old files as decoys and detect when they've been deleted or when space is low to at least issue a warning or re-download the deleted objects...
This issue might not have a workaround yet... You could possibly try calling URLForUbiquityContainerIdentifier explicitly, since it does some initialization on the first invocation. Then create a sub-directory with a .nosync suffix (based on this example).
The first time you call this method for a given container directory, iOS extends your application sandbox to include that container directory. Thus, it is important that you call this method at least once before trying to search for files in iCloud. And if your application accesses multiple container directories, you should call the method once for each directory.
The doc on .nosync:
To ensure that the persistent store itself is not synced by iCloud: when you set a value for the NSPersistentStoreUbiquitousContentNameKey, UIManagedDocument puts the persistent store in a .nosync directory inside the document package. If you make use of additional content (using the writeAdditionalContent:toURL:originalContentsURL:error: method), you must make sure that the document directory is not a package. Typically you give the document directory an extension that is not recognized as a document extension.
You may want to ensure you have the com.apple.developer.ubiquity-container-identifiers entitlement.
The iCloud Containers field identifies the list of container directories that your app can access in the user’s iCloud storage. (This field corresponds to the com.apple.developer.ubiquity-container-identifiers entitlement.)
Maybe uncheck "Enable Entitlements" in the summary pane of the project or edit the profile to remove the *ubiquity settings. (Notes for ios 5 beta 7 reference entitlements.)
There is also the setUbiquitous function:
setUbiquitous:itemAtURL:destinationURL:error:
Sets whether the item at the specified URL should be stored in the cloud.
Parameters
flag
Specify YES to move the item to iCloud or NO to remove it from iCloud (if it is there currently).
http://developer.apple.com/library/ios/#DOCUMENTATION/Cocoa/Reference/Foundation/Classes/NSFileManager_Class/Reference/Reference.html#//apple_ref/occ/instm/NSFileManager/setUbiquitous:itemAtURL:destinationURL:error:
There is a newer way to prevent iCloud syncing of data without using extended attributes directly:
- (BOOL)addSkipBackupAttributeToItemAtPath:(NSString *) filePathString
{
NSURL* URL= [NSURL fileURLWithPath: filePathString];
assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);
NSError *error = nil;
BOOL success = [URL setResourceValue: [NSNumber numberWithBool: YES]
forKey: NSURLIsExcludedFromBackupKey error: &error];
if(!success){
NSLog(#"Error excluding %# from backup %#", [URL lastPathComponent], error);
}
return success;
}
See Apple's Technical Q&A QA1719 for more details.
Did you try just naming the directory ".nosync" without the data in front? It could be generally . directories are ignored, or perhaps that specifically.
But, it seems like the behavior is just as a user would want it - potentially gigs of space used by an application they may not be using currently, where the space could be reclaimed automatically. I am not sure but you would think the system would be smart about reclaiming files created recently only after there was no choice, so if the user had just stored maps recently they would not be deleted unless there was no other choice.
For the purposes of filing a bug I am going to ask for a way to mark a directory for user prompting before deletion - so that if they are syncing a lot of movies and it would clear out a data set like the maps you are talking about, the user would be asked if they want to remove the data "offline maps" from application "MyCoolMapper" to proceed with the sync.
Just saw the Session 209 - Securing Application Data from de 2010 WWDC.
The keynote explains a lot of things, including the way you can set data protection attributes to your files (NSFileProtectionComplete, NSFileProtectionNone) and how to decide which protection is best for your case.
I just implemented it, but can't figure out how to test if the security is on, any ideas?
In addition, I have a sql lite database that needs to be accessed in background from time to time, and this method of data protection seems to be not good enough.. any link or tutorial that guide me through the best db protection? (found sql cipher but is kinda heavy to add in a evoluted project)
Thanks!
Update: With iOS 6 it's supposedly possible to require data protection for your application by using an entitlement that needs to be configured on the App ID in the iOS provisioning profile. I haven't tested this yet, and this is the best information I could find on it https://devforums.apple.com/message/707939#707939
My investigations into this matter lead me to believe that it is very difficult to determine if data protection is enabled on a device.
File protection is enabled by setting the NSFileProtectionKey file attribute to NSFileProtectionComplete
For example, to create a protected file you could run code like:
[[NSFileManager defaultManager] createFileAtPath:[self filePath]
contents:[#"super secret file contents" dataUsingEncoding:NSUTF8StringEncoding]
attributes:[NSDictionary dictionaryWithObject:NSFileProtectionComplete
forKey:NSFileProtectionKey]];
Unfortunately this code will execute without error even if Data Protection is not enabled on the device (or if the code is run on the Simulator where Data Protection is not available).
Worse, the NSFileProtectionComplete attribute will be be set regardless of whether the file is protected or not. The following:
self.fileProtectionValue = [[[NSFileManager defaultManager] attributesOfItemAtPath:[self filePath]
error:NULL] valueForKey:NSFileProtectionKey];
NSLog(#"file protection value: %#", self.fileProtectionValue);
will spit out file protection value: NSFileProtectionComplete no matter whether Data Protection is enabled or not.
There are two methods that I've been able to use to discover if File Protection is working as expected. Unfortunately neither of these methods are suitable for detecting if Data Protection is enabled on a device in the field.
Both methods work on the idea that a protected file can not be read if the device is locked.
Method one involves using a timer to attempt to read the file after the device is locked, but while your application continues to run:
[self performSelector:#selector(doReload) withObject:nil afterDelay:20];
- (void)doReload {
NSLog(#"protected data available: %#",[[UIApplication sharedApplication] isProtectedDataAvailable] ? #"yes" : #"no");
NSError *error;
self.fileContents = [NSString stringWithContentsOfFile:[self filePath]
encoding:NSUTF8StringEncoding
error:&error];
NSLog(#"file contents: %#\nerror: %#", self.fileContents, error);
}
If you run the above code and lock a data protected device it will spit out:
protected data available: no
file contents: (null)
error: Error Domain=NSCocoaErrorDomain Code=257 "The operation couldn’t be completed. (Cocoa error 257.)" UserInfo=0x16e110 {NSFilePath=/var/mobile/Applications/D71F1F1F-6C25-4848-BB1F-51539B47EC79/Documents/protected_file, NSUnderlyingError=0x16e010 "The operation couldn’t be completed. Operation not permitted"}
The 20 second delay is necessary because there is a 10 second or so grace period where protected data is still available after a Data Protection enabled device is locked.
The second method is to create a protected file in an application, exit the application, lock the device, wait 10 seconds, and then use the XCode organizer to download the contents of the application. This will produce an error message and the protected file will be empty.
If either of the above tests fail to behave as described then Data Protection is either not enable, or your File Protection code was not implemented correctly.
Because I've not found any way to verify within the application that Data Protection is enabled before I write confidential information to disk, I've filed a feature enhancement request with Apple to be able to mark an application as requiring Data Protection to be enabled. (rdar://10167256)
Apple does offer a solution to this through their Mobile Device Management (MDM) APIs, which combined with a third party server can be used to enforce policies that require Data Protection to be enabled on devices.
You can use the iExplorer app to detect if your files are encrypted. iExplorer lets you browse the filesystem of your iPhone/iPad, and open the file (of course your device must be plugged into your Mac).
When the device is locked, the files can't be read correctly.
From the NSFileManager class doc:
The file is stored in an encrypted format on disk and cannot be read from or written to while the device is locked or booting.
You just pass the constant when you set the file attributes.
When writing the contents of an NSData object to disk using the writeToFile:options:error: method, include the NSDataWritingFileProtectionComplete option.
Use the setAttributes:ofItemAtPath:error: method of NSFileManager to add the NSFileProtectionKey attribute (with the NSFileProtectionComplete value) to an existing file
http://developer.apple.com/iphone/library/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StandardBehaviors/StandardBehaviors.html
EDIT (Determining the Availability of Protected Files)
A protected file is accessible only when a device is unlocked. Because applications may continue running while a device is locked, your code should be prepared to handle the possibility of protected files becoming unavailable at any time. The UIKit framework provides ways to track whether data protection is currently enabled.
*
Use applicationProtectedDataWillBecomeUnavailable: and applicationProtectedDataDidBecomeAvailable: methods and use them to track changes to the availability of protected data.
*
An application can register for the UIApplicationProtectedDataWillBecomeUnavailable and UIApplicationProtectedDataDidBecomeAvailable notifications.
*
The protectedDataAvailable property of the shared UIApplication object indicates whether protected files are currently accessible.
Any application that works with protected files should implement the application delegate methods. When the applicationProtectedDataWillBecomeUnavailable: method is called, your application should immediately close any protected files and refrain from using them again until the applicationProtectedDataDidBecomeAvailable: method is called. Any attempts to access the protected files while they are unavailable will fail.
Verifying file protection on jailbroken devices
To step further, if you would like to test the file protection of exact file, then you would need a jailbroken device. For that, here are the (non-detailed) steps:
1) Jailbreak an iOS device
2) Install Open SSH via Cydia (This is required to remotely access files from that device) (https://cydia.saurik.com/openssh.html)
3) Login from your computer (using Mac client or Terminal) as a root user to your device.
To find location of your app's directories and files, there are various ways. Either you can
grep the process of an app (Such as ps ax | grep YourAppName) - Make sure app is running on device to get the process details. It should give the location of app bundles
Alternatively, you can also search specific file using find you are interested in. For eg. find / -type f -name YouAppName.sqlite. It should give file location on the device.
From here, you can try to see if the file is really accessible or not, when phone is locked with a passcode; or not.
- You can simply run cat YouAppName.sqlite to see if contents are accessible. Ia f file is protected, it should show
Operation not permitted
error; else if would show contents of file.
Again, this is required if you'd really like to check file protection of an individual file. If entitlements and capabilities are set properly, verifying entitlements should be enough for fileprotection.
On a side node, file explorer tools such as iExplorer don't help much in verification of FileProtection, because such tools require a device to be in "trusted" mode, so they have permissions to access the content of your device/apps.
Good luck!
Testing can be done within Xcode:
Delete and reinstall your app
On your iOS device go to Settings > Passcode and ensure 'Require Passcode' is set to 'Immediately'
Lock your phone and wait 20 seconds
On Xcode go to Window > Devices
Select the app you'd like to test
Click the settings cog and choose 'download container'
Right click the downloaded .xcappdata file and select 'show package contents'. Anything you can view here has not been encrypted by NSFileProtectionComplete
Unlock your phone and repeat steps 3-6. Files that previously did not appear that you can now view were successfully encrypted.
File protection can be enabled on a per-file or per-directory basis, or can be enabled for the whole application (using entitlements and the provisioning profile). To determine if a file or directory is protected, check the filesystem attributes for the data protection key. This should be valid even it's a parent directory that was set to be protected:
- (BOOL) isProtectedItemAtURL:(NSURL *)URL {
BOOL result = YES;
NSDictionary *attributes = nil;
NSString *protectionAttributeValue = nil;
NSFileManager *fileManager = nil;
fileManager = [[NSFileManager alloc] init];
attributes = [fileManager attributesOfItemAtPath:[URL path] error:&error];
if (attributes != nil){
protectionAttributeValue = [attributes valueForKey:NSFileProtectionKey];
if ((protectionAttributeValue == nil) || [protectionAttributeValue isEqualToString:NSFileProtectionNone]){
result = NO;
}
} else {
// handle the error
}
return result;
}
To determine if the protected content is available, UIApplication provides a method for querying the protection state, isProtectedDataAvailable. Using it with the above method would allow you to determine wether a particular file or directory is available:
- (BOOL) isItemAtURLAvailable:(NSURL *)URL {
BOOL result = NO;
if ([self isProtectedItemAtURL:URL]){
// Item is protected
if ([[UIApplication sharedApplication] isProtectedDataAvailable]){
// Protected content is available
result = YES;
}
} else {
result = YES;
}
return result;
}
I don't think you can test Data Protection with computer-based tools and a non-jailbroken iPhone anymore - maybe you could in the past. Please see my answer here for an updated method for testing Data Protection: https://stackoverflow.com/a/40044841/1165843
For a complete testing I would suggest using tools such as iDB ( https://github.com/dmayer/idb ) for performing pentesting on your iOS app, as described in this guide.
Also relevant for you may be this Cheat Sheet for iOS security testing.