How to load database at App Start up? - objective-c

I'm developing an App that Loads database of Airports.
I put the database ".JSON" File in my Application in Xcode.
Everything's working well, except a Very long time to load the database.
I used this way to get an array from the JSON File.
-(void)readDataFromFile
{
NSString * filePath =[[NSBundle mainBundle] pathForResource:#"airports" ofType:#"json"];
NSError * error;
NSString* fileContents =[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
if(error)
{
NSLog(#"Error reading file: %#",error.localizedDescription);
}
self.airports = (NSArray *)[NSJSONSerialization JSONObjectWithData:[fileContents dataUsingEncoding:NSUTF8StringEncoding] options:0 error:NULL];
NSLog(#"%#",self.airports);
}
MY QUESTION
How can I move this time of wait when the application load?
In Small Words I need to load the database at the Start up !

The earliest time to do this is in the application didFinishLaunchingWithOptions method in your app delegate class.

Related

NSData writeToFile make synchronous or find out when it completes

I know there are a few questions asked on the behavior of NSData writeToFile atomically: and how that is supposed to be a SYNC method.
But in my experience it is not behaving like that. I've included pseudo code of my method that will accept and array of file names and will save them from Camera Roll to Documents folder of the app.
The For loop runs through each item in array and calls 'writeToFile'. My NSLogs show that the loop runs through and the NSLog message after the loop completion is displayed somewhere in the middle of the files being saved. for eg: I pass 4 images to this array to save, the NSLogs show that first file was saved and then it displays log that says it reached end of For loop and then the rest of the files being saved is displayed.
My question is - how do I make it synchronous or setup a completion handler of some sort that will only get called when the file write has completely finished. I need to eventually upload the files once they've been saved in the apps documents folder. Rest of the logic is in place. I just am having trouble finding the moment when saving of all files has completed.
I do not want to do a hack and set a delay because my file sizes and number of files can vary widely. Any guidance on this is much appreciated. Thanks
//'each item in 'arrayOfSelectedMedia' holds a dictionary of keys 'imageData' ,absolutePathToFileData' and 'filename'
-(void) saveArrayOfFiles: (NSMutableArray*) arrayOfSelectedMedia
toLocation: (NSString*) subFolder
whenCompletePostNotificationTo:(NSString*)backToCaller{
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:globalChatsMediaFolder]){
[[NSFileManager defaultManager] createDirectoryAtPath:mediaFolder withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
}
else{
NSLog(#"folder %# exists already",mediaFolder);
}
NSString *completePath = [globalChatsMediaFolder stringByAppendingPathComponent:subFolder];
if (![[NSFileManager defaultManager] completePath]){
[[NSFileManager defaultManager] completePath withIntermediateDirectories:NO attributes:nil error:&error]; //Create folder
NSLog(#"creating folder %# ", completePath);
}
else{
NSLog(#"folder %# exists already", completePath);
}
/******* go through all files in array and save it to their specific folder ***********/
for(int i=0; i < [arrayOfSelectedMedia count]; i++){
NSString *saveFileAs = [NSString stringWithFormat:#"%#",[arrayOfSelectedMedia[i] objectForKey:#"filename"]];
NSURL *absolutePathToSelectedFile = [arrayOfSelectedMedia[i] objectForKey:#"absolutePathToFileInCameraRoll"];
// Create assets library
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init] ;
// Try to load asset at mediaURL
[library assetForURL:absolutePathToSelectedFile resultBlock:^(ALAsset *asset) {
// If asset exists
if (asset) {
// Type your code here for successful
ALAssetRepresentation *rep = [asset defaultRepresentation];
Byte *buffer = (Byte*)malloc((NSUInteger)rep.size);
NSUInteger buffered = [rep getBytes:buffer fromOffset:0.0 length:(NSUInteger)rep.size error:nil];
NSData *dataToUpload = [NSData dataWithBytesNoCopy:buffer length:buffered freeWhenDone:YES];
NSLog(#"%s , A direct BYTE size of file %d bytes", __PRETTY_FUNCTION__, [dataToUpload length]);
NSLog(#"absolute path to asset is %# ", asset.defaultRepresentation.url);
NSString *absolutePathToFile = [completePath stringByAppendingPathComponent:saveFileAs];
BOOL writeStatus = [dataToUpload writeToFile:absolutePathToFile options:NSDataWritingAtomic error:&error];
if(writeStatus){
NSLog(#"file %# with %lu bytes was successfully saved locally", saveFileAs, (unsigned long)[dataToUpload length]);
}
else{
NSLog(#"write to local file failed: %#",error);
}
}
else {
// Type your code here for not existing asset
NSLog(#"%s, File at path '%#' could not be found.",__PRETTY_FUNCTION__,absolutePathToSelectedFile);
}
} failureBlock:^(NSError *error) {
// Type your code here for failure (when user doesn't allow location in your app)
NSLog(#"%s, failure to read file at path: %#. Check if user has allowed access to file location.",__PRETTY_FUNCTION__,absolutePathToSelectedFile);
}];
} // end of For loop
NSLog(#"Finished saving array of Files to local Disk. back to caller provided as '%#'",backToCaller);
if(backToCaller.length > 0){ // i.e. it was not set to nil and I need to post notification
NSLog(#"posting notification to go back to Caller %#", backToCaller);
[[NSNotificationCenter defaultCenter] postNotificationName:backToCaller object:nil userInfo:nil];
}
}

Objective C can't read text file when running on iPhone

I've made an app that is relying on reading and writing a plist-file. This works well when I'm running the app in the iPhone simulator, but doesn't work at all when I'm testing it on my iPhone. I've also made a pre made text file in .txt format with demo data. The app works when I'm running this file.
All the reading and writing is done in a class that looks like this:
-(void)saveArray:(NSMutableArray *)inputArray
{
albumArray = inputArray;
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [path objectAtIndex:0];
NSString *filePath = [documentFolder stringByAppendingFormat:#"albums.plist"];
[albumArray writeToFile:filePath atomically:YES];
}
Update: Changed the string from "stringByAppendingFormat" to "stringByAppendingPathComponent" and it seems to work now. Thanks a lot! You guys made my day made.
Are you sure, that the folders already exist?
Here is a function i'm using to get the path to my file:
- (NSString*) pathToSavedAlbums
{
NSURL *applicationSupportURL = [self applicationDataDirectory];
if (! [[NSFileManager defaultManager] fileExistsAtPath:[applicationSupportURL path]])
{
NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:[applicationSupportURL path]
withIntermediateDirectories:YES
attributes:nil
error:&error];
if (error)
{
NSLog(#"error creating app support dir: %#", error);
}
}
NSString *path = [[applicationSupportURL path] stringByAppendingPathComponent:#"albums.plist"];
return path;
}
Check the spelling of the plist name as well as the case, device is case sensitive for docs but simulator isn't. Also try deleting the app from the device and reinstalling it ?
writeToFile:atomically: returns a bool, so check that to see if it fails to even write to the path. Check the file path string and ensure this is where you want it to go.

Last Opened Documents using Cocoa

Is there a way to get the last opened files of an application? I know with LSSharedFileListCreate you can get an array of the global recent documents of the user but I was wondering if it was possible to get the recent documents for a specific application, such as Xcode, using cocoa or objective-c.
I don't think there is a public API for getting recent documents of apps other than your own. The recent files are saved as plists in ~/Library/Preferences though, so you could just read those directly.
Note however that the format of these plists might change with future OS upgrades, so you should ideally not rely on this.
Here's an example for reading the recent documents of Xcode:
NSString *bundleID = #"com.apple.dt.Xcode";
NSString *prefPath = [[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"Preferences"];
NSString *recentFilesFileName = [bundleID stringByAppendingPathExtension:#"LSSharedFileList.plist"];
NSString *recentFilesPath = [prefPath stringByAppendingPathComponent:recentFilesFileName];
NSArray *recentDocumentItems = [[[NSDictionary dictionaryWithContentsOfFile:recentFilesPath] objectForKey:#"RecentDocuments"] objectForKey:#"CustomListItems"];
for (NSDictionary *recentDocumentItem in recentDocumentItems) {
NSString *name = [recentDocumentItem objectForKey:#"Name"];
NSData *bookmarkData = [recentDocumentItem objectForKey:#"Bookmark"];
NSError *error = nil;
NSURL *bookmarkURL = [NSURL URLByResolvingBookmarkData:bookmarkData options:NSURLBookmarkResolutionWithoutMounting | NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:NULL error:&error];
if (bookmarkURL) {
NSLog(#"File name: %#", name);
NSLog(#"URL: %#", bookmarkURL);
} else {
NSLog(#"Could not resolve URL for file %#: %#", name, error);
}
}
This won't work in a sandboxed app of course.
You can use -[NSDocumentController recentDocumentURLs], which retrieves an array of URLs of the most recently opened documents:
NSArray *array = [[NSDocumentController sharedDocumentController] recentDocumentURLs];

NSString writeToFile, NSSavePanel and write permissions

I've only been leaning Cocoa/Objective C for a few days so apologies that this is probably simple/obvious but it's got me stumped.
I've written this handler for saving 3 floats to a text file. However when I'm running it the files are not being saved. Could anyone suggest if there's an error in my code or if you think there's something else (like file write permissions) preventing the file from being written.
Research has lead me to look into Sandboxing, but that gets confusing very quickly and I'm hoping just running the app from xcode in debug would let me write to my user directory.
Heres the code:
- (IBAction)saveResultsAction:(id)sender {
//Sets up the data to save
NSString *saveLens = [NSString stringWithFormat:#"Screen width is %.02f \n Screen Height is %.02f \n Lens is %.02f:1",
self.myLens.screenWidth,
self.myLens.screenHeight,
self.myLens.lensRatio];
NSSavePanel *save = [NSSavePanel savePanel];
long int result = [save runModal];
if (result == NSOKButton) {
NSURL *selectedFile = [save URL];
NSLog(#"Save URL is %#", selectedFile);
NSString *fileName = [[NSString alloc] initWithFormat:#"%#.txt", selectedFile];
NSLog(#"Appended URL is %#", fileName);
[saveLens writeToFile:fileName
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
}
}
a NSURL object is no POSIX path..
its a URL and getting its description doesnt make it a path
NSString *fileName = [selectedFile.path stringByAppendingPathExtension:#"txt"];
BUT as said, you shouldnt have to append the .txt at all. just use what the panel returns. Else, there would be sandboxd errors because you dont have access rights to the modified filename :)
NSString *fileName = selectedFile.path;
The problem is that you don't need to append the file extension to the URL.The extension is already there.You could directly do this:
if (result == NSOKButton)
{
[saveLens writeToURL: [save URL]
atomically:YES
encoding:NSUTF8StringEncoding
error:nil];
}
I see you've already accepted an answer, but it may also be helpful to know how to debug this type of issue using NSError pointers.
Cocoa uses NSError with method calls which generate error conditions, which richly encapsulate errors. (Objective-C also has exceptions, but they're reserved for cases of programmer error, like an array index out of bounds, or a nil parameter that should never be.)
When you have a method which accepts an error pointer, usually it also return a BOOL indicating overall success or failure. Here's how to get more information:
NSError *error = nil;
if (![saveLens writeToFile:fileName
atomically:YES
encoding:NSUTF8StringEncoding
error:&error]) {
NSLog(#"error: %#", error);
}
Or even:
NSError *error = nil;
if (![saveLens writeToFile:fileName
atomically:YES
encoding:NSUTF8StringEncoding
error:&error]) {
[NSApp presentError:error];
}

Adding text file to Xcode project and using it in app

I have a .txt file (in plain text format) and I would like to integrate this into a iOS app I am starting to write. The info in the text file must be searchable by the app. Is there a way to do this? If so, what would be best way to do so? Or would I need to add this text file into ViewController.m in code instead?
You will need to add the txt file to your project and then read it into memory. Here is one way to read the entire contents into memory at once:
NSString *path = [[NSBundle mainBundle] pathForResource:#"yourTextFile"
ofType:#"txt"];
NSError *error = nil;
NSString *data = [NSString stringWithContentsOfFile:path
encoding:NSUTF8StringEncoding
error:&error];
if (error) {
// handle the error condition
} else {
// continue your processing
}