How can I save document files to the SQLite Database in iOS to view them later in the program? - ios

I'm having a problem with the code I'm writing.
I'm writing an iOS program (I'm an iOS rookie) which basically requires me to use quick look framework to view some documents on the iPhone (*.doc, *.ppt, *.pdf etc..) which are stored in the database (Core Data - SQLite, nothing external). I need to add the files somehow, but since iOS isn't really allowing me to browse through its file system I can't find and save the documents i need in database. Which kinda blocks everything else i need to do until I can get those documents from the database. (to set up table views that list the files and the details about the files etc.)
This is for a class project so it doesn't need to be perfect condition, I just need to be able to browse through a few documents while I'm presenting the project. I can add all the documents I'm going to use at one time while I'm coding and I won't need to be able to add any new files when I'm using the program during the presentation. And I don't want it to make it more complicated if i don't have to. Like connecting to an external database with the files already saved in and use a php buffer-page to connect to that database or anything like that. I don't have the necessary server system to execute that php file. I want this operation to be done inside the phone.
The solutions I was able to think of so far:
Grab some random office files from the internet and save them into the database. Then use them later.
Create image scans of some office files and "cheat" by using the scanned image instead of actual documents.
I would really appreciate it if someone can tell me another and much easier way to handle this. Please just keep in mind that while I have a programming background with Java and C#, I'm still an iOS rookie and just barely moving on from scratching the surface. So it is likely that I don't know about something iOS provides by default and I'm just pulling my hair out for nothing.
I think thats it, I hope I didn't forget anything. If you need more details I'm going to be here and I can provide them almost instantly. Thanks everyone in advance for your help.

It sounds like NSFileManager will help you.
If you place your documents into your project tree, they will be available to your app. Use NSFileManager to copy them into the app's Documents folder using something like:
- (void)placeBundleFileInDocuments:(NSString *)filename
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:[[filename lastPathComponent] stringByDeletingPathExtension] ofType:[filename pathExtension]];
NSString *documentsFolderPath = [NSString stringWithFormat:#"%#/", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]];
NSString *path = [documentsFolderPath stringByAppendingPathComponent:[NSString stringWithFormat:#"%#", filename]];
if ([fileManager fileExistsAtPath:path])
return;
NSError *error = nil;
[fileManager copyItemAtPath:bundlePath toPath:path error:&error];
if (error) {
NSLog(#"Unable to copy file (%#).", error.localizedDescription);
}
}
Then, you can use NSFileManager to retrieve details about the files. You might find this method useful:
- (NSDictionary *)attributesOfItemAtPath:(NSString *)path error:(NSError **)error
I hope this helps!

Related

Writing big file on IOS [duplicate]

I save some run-time generated files inside the .app bundle of my iOS app. In the simulator it works fine, in the device it crashes:
Could create output files in the given shader cache path
'/var/mobile/Applications/CB064997-B40E-4FE3-9834-B3217CE33489/SimedTest.app/Ogre3D/assets/RTShaderLib/cache/
Is there a good overview of where I should and shouldn't put files - how to use Documents, Library and tmp, etc?
To clarify, these are files created at startup which pre-calculate some data to save time. IF they are not present they get created so it's fine they are deleted, but not while the app is running.
The bundle is read-only. You don't want to mess around with it for two reasons:
Code Signing: the signature is verified by against the contents of the bundle; if you mess around with the bundle, you break the signature.
App Updates: updates work by replacing the entire app bundle with a newly downloaded one; any changes you make will get lost.
Where you should save stuff:
Documents: if you want it to persist and be backed up
Library/Caches: if you just want to cache downloaded data, like profile pics; will be auto deleted by the system if it is low on room unless you specify with a special do-not-delete flag.
tmp: temporary files, deleted when your app is not running
For a full explanation check out File System Programming Guide and QA1719.
No, every time you change your bundle you invalidate your signature.
If you want to write files you`l need to write in the best folder depending on what you want to do with that file.
Documents folder for long duration files
Cache for small operations
and so on
EDIT
To get the path you`ll need something like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"filename.ext"];
With this path you can write or read like this:
write:
NSString *content = #"One\nTwo\nThree\nFour\nFive";
[content writeToFile:fileName atomically:NO encoding:NSStringEncodingConversionAllowLossy error:nil];
read:
NSString *content = [[NSString alloc] initWithContentsOfFile:fileName usedEncoding:nil error:nil];

Large files downloaded to Documents and backed up to iCloud

I have an iOS app in the app store that can download relatively large files that need to stay on the device for offline use. Those files are currently stored in the app's Documents folder but I'm just now reading that the Documents folder is backed up and should really only be used for user-generated content. This Apple technical Q&A states that the NSURLIsExcludedFromBackupKey should be set to prevent backup. This states that an app's /Library/Caches is the right place to put these kinds of files although further reading suggests that the folder may be cleared when the device is low on storage which is unacceptable for this app. I believe /Library/Application Support/ is then the best location for them -- does this sound right?
Unfortunately, this mistake got through the app review process. What are some best practices for fixing this now that people are using the app and already have some files persisted to the Documents folder and to their backups? It seems I need to move all the existing files and set their NSURLIsExcludedFromBackupKey on app update. How do I guarantee that this is done exactly once and that it isn't interrupted? Is moving the files out of the Documents folder important or could I leave them there? Will changing the files' backup status remove them from existing backups?
I'm using Swift 2.1.1 and targeting iOS 8.0+.
As stated in the technical Q&A, you best bet could be create a subdirectory in the Documents, and exclude that subdirectory once.
I don't believe you can write a 'do it once and be sure it is done' routine, since you can't guarantee your app doesn't crash while it is running. You certainly could set a completion flag when you are sure it is done so that once it is done you don't have to run it again.
Exclude your directory from backup, not the individual files.
From Xcode:
You can use this property to exclude cache and other app support files which are not needed in a backup. Some operations commonly made to user documents cause this property to be reset to false; consequently, do not use this property on user documents.
Here is the strategy I have used with good results
(sorry, its in objective-c -- I'm not a swift guy. Hopefully it will give you the idea):
- (BOOL)moveAllFiles{
// Searches through the documents directory for all files ending in .data
BOOL success = true;
NSString *myNewLocation = #"/store/my/files/here/now";
// Get the documents directory
NSArray *documentDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [documentDirectories objectAtIndex:0];
// Get all files ending in .data (whatever your file type is)
NSArray *dataFilesArray = [NSArray arrayWithArray:[NSBundle pathsForResourcesOfType:#"data" inDirectory:documentDirectory]];
// If you have multiple resource types, use this line once each for each resource type, then append the arrays.
// Iterate the found files
NSString *fileName = [NSString string];
for (int i=0; i<[dataFilesArray count]; i++) {
fileName = [[dataFilesArray objectAtIndex:i] lastPathComponent];
// Move file, set success to false if unsuccessful move
if (![[NSFileManager defaultManager] moveItemAtPath:[dataFilesArray objectAtIndex:i]
toPath:[myNewLocation stringByAppendingPathComponent:fileName]
error:nil]) {
success = false; // Something went wrong
}
}
return success;
}
Now use the value of success to set a key in the user defaults file. Check for that key on startup. If it is false (or absent), run this routine (again).
This example is with file paths. You can do the same thing with file URLs if you wish.

Using Core Data created in one project as input to another project

I’ve found a couple of similar questions to this, but nothing that quite fits my needs. I’ve developed a simple digitizing app in Swift that presents an image and allows me to digitize specific points in the image using the simulator.
I use Core Data to save the data. One entity (“Coursemap”) has attributes for the image (Binary Data) and some meta data (Strings). The Coursemap entity has a one-to-many relationship with a “Points” entity. The Points entity defines attributes for the x/y coordinates for specific points in the image. The digitizer app is working well; I can digitize and save several images using Core Data. In the final version of my app I’ll probably need about 50-60 digitized images.
What I’d like to do is use the data from the output of my digitizer app as input to an iPad game I’m developing. So I basically want to copy the Core Data files created in my digitizer project over to my game project so my game app can use the digitized images. Is there a way to do this? Is Core Data the best way to handle this?
What I’ve tried so far: I name the two projects the same, but keep them in separate folders, e.g.:
~/digitizer/myApp and ~/games/myApp.
The digitizer core data files are created in the folder:
~/Library/Developer/CoreSimulator/Devices/…/Documents.
When I run the game app, it seems to expect the Core Data files to be in the same directory (which is why I named the two projects the same). When the game app tries to fetch data, I get the error, “The model used to open the store is incompatible with the one used to create the store”. I’ve double-checked the data models and they are identical.
Any ideas on this? Thanks.
I realized something similar:
- pick your prefilled sqlite file from the simulator folder
- copy it over to your other project
- during runtime, check on your other project if a sqlite structure exists and if it doesn't create it by copying the sqlite file from your bundle to the documents folder:
if(![[NSFileManager defaultManager]fileExistsAtPath:coreDataDatabase.path])
{
url = coreDataDatabase;
[[NSFileManager defaultManager] createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
url = [url URLByAppendingPathComponent:#"StoreContent"];
[[NSFileManager defaultManager] createDirectoryAtURL:url withIntermediateDirectories:YES attributes:nil error:NULL];
url = [url URLByAppendingPathComponent:#"persistentStore"];
// copy the sqlite file to /Documents/<YourApp>/StoreContent/persistentStore
NSArray *stringParts = [kCoreDataPreloadedDatabase componentsSeparatedByString:#"."];
NSString *sqlitePath = [[NSBundle mainBundle]
pathForResource:[stringParts objectAtIndex:0] ofType:#"sqlite"];
NSError *anyError = nil;
BOOL success = [[NSFileManager defaultManager]
copyItemAtPath:sqlitePath toPath:[url path] error:&anyError];
if(!success)
{
Error(#"Unable to copy sqlite file: %#", anyError);
}
}

Unable to connect SQLite Database in iOS

I am completely new to SQLite and iOS. I am following a basic tutorial on how to use SQLite in iOS:
http://www.switchonthecode.com/tutorials/using-sqlite-on-the-iphone#comment-11617
In the above link, they have specified the database as:
sqlite3 *database;
int result = sqlite3_open("/myExampleDatabase.db", &database);
but when I use the above code with replacing my database name, I get an error as specified in the subsequent alertview.
My question here is, do I have to add the database file into my resource folder? If not, do I have to have my database file somewhere that is accessible to iOS?
I suggest using FMDB wrapper for SQLite:
https://github.com/ccgus/fmdb
If you want to open a sqlite database, you might want to:
Make sure you're including your database in your bundle.
Programmatically copy the database from your bundle to your documents (esp important if user will be modifying the database; if you're only reading, you could go ahead an just open the version in the bundle).
If you're running this in your simulator, you can go ahead and inspect the bundle and Documents folders if things don't go right, just to make sure everything is where it should be. You simulator's folder is something like "~/Library/Application Support/iPhone Simulator/5.1/Applications/" (replace the 5.1 with whatever version of your simulator you are using). You might have to unhide your Library folder, if you haven't already, by running the chflags nohidden ~/Library in a Terminal command-line window.
So, the code for getting the path of the database (and copying it to the Documents if it's not there yet), might look like:
NSString *databaseName = kDatabaseName; // obviously, replace this with your database filename, e.g., #"myExampleDatabase.db"
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *databaseFullDocumentPath = [documentsFolder stringByAppendingPathComponent:databaseName];
NSString *databaseFullBundlePath = [[NSBundle mainBundle] pathForResource:databaseName ofType:#""];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:databaseFullDocumentPath])
{
NSAssert([fileManager fileExistsAtPath:databaseFullBundlePath], #"Database not found in bundle");
NSError *error;
if (![fileManager copyItemAtPath:databaseFullBundlePath toPath:databaseFullDocumentPath error:&error])
NSLog(#"Unable to copy database from '%#' to '%#': error = %#", databaseFullBundlePath, databaseFullDocumentPath, error);
}
Then, if you're doing your own sqlite calls, it would be something like:
sqlite3 *database;
if (sqlite3_open_v2([databaseFullDocumentPath UTF8String], &database, SQLITE_OPEN_READWRITE, NULL) == SQLITE_OK)
{
// do whatever you want to do
}
Or, alternatively, if you're using FMDB, it would be something like:
FMDatabase *db = [[FMDatabase alloc] initWithPath:databaseFullDocumentPath];
NSAssert(db, #"Unable to open create FMDatabase");
BOOL success = [db open];
NSAssert(success, #"Unable to open database");
if (success)
{
// do whatever you want to do
}
I fully support the previous answer in most cases, however:
Are you sure you have to use sqlite3 instead of Core Data?
There are several discussion where you can get information when to use a database wrapper (like fmdb) and when to use Core Data. (Speaking personally, I love to use fmdb, but it always results in more code, complexity and most of the time a worse performance)
Core Data vs SQLite 3
Use CoreData or SQLite on iPhone?
Core Data vs Sqlite and performance
Core Data vs SQLite 3
is it worth using core data for a simple sqlite app on the iphone with one table and no relationships or complicated subtable/views?
Core Data vs. SQLite for SQL experienced developers
Some links to get started with Core Data:
Core Data Programming Guide (Apple)
Core Data Tutorial for iOS (Apple)

UIManagedDocument can only read documents that are file packages

My app is using a core data SQLite database. I would like to enable my users to use iCloud to sync it between devices - and I was thinking I could use UIManagedDocument.
I subclassed it, following Apple's documentation, and it is works when a new persistent store file needs to be created. However, when I try to use it to open my old persistent store file, I get the following exception thrown error:
"UIManagedDocument can only read documents that are file packages"
Does this mean that I need to migrate the old persistent store to a new store managed by UIManagedDocument? If so, do I need to do this manually (i.e. read each record one-at-a-time from the old store and write it into the new one)?
Thanks in advance!
UIManagedDocument creates packages(folders) rather than atomic stores. The store is still there but its buried in the package. If you right click on the file that is created in your Documents folder in the simulator you'll be able to see the structure. The default is
mydocument.foo
-> StoreContent
-> persistentStore
What you need to do is create a new extension for your app file type so for example if your database extension is .myappdb you need to create a new document type in your project settings which might be .myappdbw. You can copy all settings from the entry for .myappdb
Next at the point where you handle opening your legacy document at mydocumenturl instead of passing that to your persistent store co-ordinator you create the directory structure above.
NSURL *newurl = [[mydocumenturl URLByDeletingPathExtension] URLByAppendingPathExtension:#"myappdbw"];
NSURL *desturl = [newurl URLByAppendingPathComponent:#"StoreContent"];
[[NSFileManager defaultManager] createDirectoryAtURL:desturl withIntermediateDirectories:YES attributes:nil error:NULL];
NSURL *finalurl = [desturl URLByAppendingPathComponent:#"persistentStore"];
and then move the legacy database into the folder system you have created
[[NSFileManager defaultManager] moveItemAtURL:mydocumenturl toURL:finalurl error:NULL];
and then you can pass the bundle url to UIManagedDocument
UIManagedDocument *doc = [[UIManagedDocument alloc] initWithFileURL:newurl];
A link which will be useful for the iCloud integration is
http://developer.apple.com/library/ios/#releasenotes/DataManagement/RN-iCloudCoreData/_index.html
Its all a bit mysterious as the most of the promised sample code has failed to appear so far but on the other hand its mostly fairly simple to deduce. Have a look at WWDC2011 sessions 107,116 and 315 for more hints.
But note that if you are going to use this method for migrating your legacy docs DONT set the NSPersistentStoreUbiquitousContentNameKey at point you migrate because the package changes when you do. The doc above describes it quite well.
Thanks for this tip. I think I found an even simpler solution.
I just create a new UIManagedDocument with a different filename than my old persistent store location.
In my subclassed UIManagedDocument, I override the configurePersistentStoreCoordinatorForURL method and do the migration once there:
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)storeURL ofType:(NSString *)fileType modelConfiguration:(NSString *)configuration storeOptions:(NSDictionary *)storeOptions error:(NSError **)error
{
// If legacy store exists, copy it to the new location
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:legacyPersistentStoreURL.path])
{
NSError* thisError = nil;
[fileManager copyItemAtURL:legacyPersistentStoreURL toURL:storeURL error:&thisError];
[fileManager removeItemAtURL:legacyPersistentStoreURL error:&thisError];
}
return [super configurePersistentStoreCoordinatorForURL:storeURL ofType:fileType modelConfiguration:configuration storeOptions:storeOptions error:error];
}

Resources