Overwrite with remote .plist a local .plist file - objective-c

My application loads a local list.plist file at launch.
Then it has a refreshTable button which fetch a remote version of the .plist file from my website.
App Launch
local list.plist loads
user hits refreshList button
local list.plist is overwritten by remote list.plist
local list.plist updated until remote list.plist updates again
Method to initialize data:
//Remote data
list = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://mywebsite.com/list.plist"]];
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
sortedList = [[list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
//Local data
NSString *localPath = [[NSBundle mainBundle] pathForResource:#"list" ofType:#"plist"];
localList = [[NSMutableArray alloc] initWithContentsOfFile:localPath];
NSSortDescriptor *localDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
localSortedList = [[localList sortedArrayUsingDescriptors:[NSArray arrayWithObject:localDescriptor]] retain];
This is the method to refresh:
- (void) refreshTable:(id)sender
{
//Remote .plist
list = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://mywebsite.com/list.plist"]];
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
sortedList = [[list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
[self.tableView reloadData];
//now write remote plist to local plist
}
After i downloaded the remote plist how can i write over the local plist?
I was thinking to empty the local array containing the local plist and fill it with the remote array and i did it this way:
I solved in the way i thought:
//Remote .plist
list = [[NSMutableArray alloc] initWithContentsOfURL:[NSURL URLWithString:#"http://phillipapps.com/mntlion/list.plist"]];
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
sortedList = [[list sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
NSLog(#"list: %#",list);
[localList removeAllObjects];
[localList addObjectsFromArray:list];
localSortedList = [[localList sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]] retain];
NSLog(#"locallist: %#",localList);
[self.tableView reloadData];
It works, but how can i write over localList with the contents of list?

so … after a view hours in chat we got the problem solved.
- (NSArray*)readPlist
{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:#"localFile.plist"];
NSFileManager *fMgr = [NSFileManager defaultManager];
if (![fMgr fileExistsAtPath:plistPath]) {
plistPath = [[NSBundle mainBundle] pathForResource:#"template" ofType:#"plist"];
}
return [NSArray arrayWithContentsOfFile:plistPath];
}
- (void)writePlist:(NSArray*)arr
{
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[documentPaths lastObject] stringByAppendingPathComponent:#"localFile.plist"];
NSFileManager *fMgr = [NSFileManager defaultManager];
if (![fMgr fileExistsAtPath:plistPath])
[fMgr removeItemAtPath:plistPath error:nil];
[arr writeToFile:plistPath atomically:YES];
}
initializing the ivar:
self.plistArray = [self readPlist];
and after loading the new plist from the server you have to call this code:
self.plistArray = [NSArray arrayWithContentsOfURL:[NSURL URLWithString:#"http://mywebsite.com/list.plist"]];
[self writePlist:plistArray]; // e.g. for caching
[self.tableView reloadData];

I do almost the exact thing you're looking for in an application of mine. You can't write to the NSBundle so the steps are something like this:
Try to load plist from cache directory, if it succeeds go to 3.
Load plist from bundle
Check if loaded plist is up to date (or, trigger further steps by the press of a button)
Download new plist
Save to cache directory
The code looks something like this:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cache = [paths objectAtIndex:0];
ratios = [NSDictionary dictionaryWithContentsOfFile:[cache stringByAppendingPathComponent:#"devices.plist"]];
if (ratios == nil) {
ratios = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"devices" ofType:#"plist"]];
}
NSString *device = [[UIDevice currentDevice] machine];
NSDictionary *d = [ratios objectForKey:device];
if (d!=nil) {
pixelInchRatio = [[d objectForKey:#"pixelInchRatio"] doubleValue];
bezelWidth = [[d objectForKey:#"bezelWidth"] doubleValue];
bezelHeight = [[d objectForKey:#"bezelHeight"] doubleValue];
} else if (fetch) {
[[[[RulerDimensionDownload alloc] init] autorelease] startDownload];
}
Then in the downloader it saves like so:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cache = [paths objectAtIndex:0];
[[NSFileManager defaultManager] createDirectoryAtPath:cache withIntermediateDirectories:YES attributes:nil error:nil];
[ratios writeToFile:[cache stringByAppendingPathComponent:#"devices.plist"] atomically:YES];
Some explanations:
The first few lines in each sample, the paths and cache definition, simply get the location of the cache directory for the application. I use this to store the most up-to-date version I'm my plist.
So, in the loading code, I first try to load the plist from the cache directory. If this fails (ratios is null) I then load it from my application bundle (this is the version of the plist that I ship with the app).
After that, I check to see if the plist has the information needed. (the plist has a definition for each device type. If the device isn't in the plist then I know I need to try to update the plist)
If the plist is out of date I start the download using a class I wrote: RulerDimensionDownload. Once it completes the download I save the file into the cache directory. Then, next time the plist needs to be loaded it will be loaded first and the shipped plist will never be looked at. (I also send a notification with the new plist)

Related

Mac OSX How can I add plist to bundle and modify the plist

I have a plist and I copy the plist to my xcode project but it seems like the file is not in bundle:
NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
nslog (#"dict %#",dict);
but the file plist file is not showing. Question, how can I add the file to project in a way to see in the bundle? also any of you knows how can I modify the plist and save the changes?
I'll really appreciated your help
P.S. I'm using Xcode 5.
You can create a plist file in your bundle and modify its contents as:
NSString *bundlePath = [[NSBundle mainBundle]bundlePath]; //Path of your bundle
NSString *path = [bundlePath stringByAppendingPathComponent:#"MyPlist.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableDictionary *data;
if ([fileManager fileExistsAtPath: path])
{
data = [[NSMutableDictionary alloc] initWithContentsOfFile: path]; // if file exist at path initialise your dictionary with its data
}
else
{
// If the file doesn’t exist, create an empty dictionary
data = [[NSMutableDictionary alloc] init];
}
//To insert the data into the plist
int value = 5;
[data setObject:[NSNumber numberWithInt:value] forKey:#"value"];
[data writeToFile: path atomically:YES];
[data release];
//To retrieve the data from the plist
NSMutableDictionary *savedData = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
int savedValue;
savedValue = [[savedData objectForKey:#"value"] intValue];
NSLog(#"%i",savedValue);
infoDictionary will return Info.plist file.
You should use pathForResource:ofType: method of NSBundle class.
NSString *commonDictionaryPath;
if (commonDictionaryPath = [[NSBundle mainBundle] pathForResource:#"CommonDictionary" ofType:#"plist"]) {
theDictionary = [[NSDictionary alloc] initWithContentsOfFile:commonDictionaryPath];
}
How can I add plist to bundle
Drag your plist to "Copy Bundle Resources" phase of your build target
modify the plist
Copy the plist to your app's Documents folder and modify it there.
this should work
NSString*pListDictPath = [[NSBundle mainBundle] pathForResource:#"yourPlistName" ofType:#"plist" inDirectory:#"YourDirectory"];
if()
{
NSDictionary *theDictionary = [[NSDictionary alloc] initWithContentsOfFile:pListDictPath ];
}

How to save data locally in app? [duplicate]

This question already has answers here:
Working with data in iOS Apps (What to choose? NSData, CoreData, sqlite, PList, NSUserDefaults)
(2 answers)
Closed 9 years ago.
I've been struggling with this for ages now and I really need some good help here. :)
I have an app where I'm parsing a quite big JSON into appdelegate's didFinishLaunchingWithOptions.
My Model Objects are:
Tab:
NSString *title
NSMutableArray *categories
Category:
NSString *title
NSMutableArray *items
Item
NSString *title
NSString *description
UIImage *image
I need to save the data locally, cause the parsing takes about 15 seconds every time my app starts. I'm using the SBJSON framework.
Here's my code for parsing:
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"json_template" ofType:#"json"];
NSString *contents = [NSString stringWithContentsOfFile: filePath encoding: NSUTF8StringEncoding error: nil];
SBJsonParser *jsonParser = [[SBJsonParser alloc] init];
NSMutableDictionary *json = [jsonParser objectWithString: contents];
tabs = [[NSMutableArray alloc] init];
jsonParser = nil;
for (NSString *tab in json)
{
Tab *tabObj = [[Tab alloc] init];
tabObj.title = tab;
NSDictionary *categoryDict = [[json valueForKey: tabObj.title] objectAtIndex: 0];
for (NSString *key in categoryDict)
{
Category *catObj = [[Category alloc] init];
catObj.name = key;
NSArray *items = [categoryDict objectForKey:key];
for (NSDictionary *dict in items)
{
Item *item = [[Item alloc] init];
item.title = [dict objectForKey: #"title"];
item.desc = [dict objectForKey: #"description"];
item.url = [dict objectForKey: #"url"];
if([dict objectForKey: #"image"] != [NSNull null])
{
NSURL *imgUrl = [NSURL URLWithString: [dict objectForKey: #"image"]];
NSData *imageData = [NSData dataWithContentsOfURL: imgUrl];
item.image = [UIImage imageWithData: imageData];
}
else
{
UIImage *image = [UIImage imageNamed: #"standard.png"];
item.image = image;
}
[catObj.items addObject: item];
}
[tabObj.categories addObject: catObj];
}
[tabs addObject: tabObj];
}
What is the best way of doing this? Using Core Data or NSFileManager?
If you have som code example too it will make me very happy.
This is the last thing i need to fix before the app is ready for app store and it just kills me! I can't solve this problem.
If you are working on iOS then you save a file to the Documents folder. On Mac OS X it would be in the Application Support folder. Since you are on iOS, read this answer for how to access the Documents folder.
All of the objects that you want to store should implement NSCoding. The above variables already do. Should you want to store the tabs, categories and items directly they would need to implement NSCoding. Then all you need is to serialize them to a file. When opening you app you can look for this file and get your objects back without parsing.
The code should look something like this (untested and error checking is ommited for brevity):
- (void) saveStateToDocumentNamed:(NSString*)docName
{
NSError *error;
NSFileManager *fileMan = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths[0] stringByAppendingPathComponent:docName];
if ([fileMan fileExistsAtPath:docPath])
[fileMan removeItemAtPath:docPath error:&error];
// Create the dictionary with all the stuff you want to store locally
NSDictionary *state = #{ ... };
// There are many ways to write the state to a file. This is the simplest
// but lacks error checking and recovery options.
[NSKeyedArchiver archiveRootObject:state toFile:docPath];
}
- (NSDictionary*) stateFromDocumentNamed:(NSString*)docName
{
NSError *error;
NSFileManager *fileMan = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [paths[0] stringByAppendingPathComponent:docName];
if ([fileMan fileExistsAtPath:docPath])
return [NSKeyedUnarchiver unarchiveObjectWithFile:docPath];
return nil;
}

How to write and read data from plist to tableview

Hi I am trying to make bookmarks for my browser.
Title of Page, Url of Page, any Comments related to page.
I tried to save it in plist, but unsuccessful. Can anyone can help me to save these thing to plist and retrive in table view. So, when user tap on title it will open url in UIWebView.
Here is the code I have tried, so far:
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"bookmarks.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"bookmarks" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error]; //6
}
NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
NSString *writeUrl = #"Page URL One";
NSString *writeTitle = #"Page Title One";
NSString *writeComment = #"Page comments";
[data setObject:[NSString stringWithString:writeUrl] forKey:#"url"];
[data setObject:[NSString stringWithString:writeTitle] forKey:#"title"];
[data setObject:[NSString stringWithString:writeComment] forKey:#"comment"];
[data writeToFile: path atomically:YES];
NSMutableDictionary *savedUrl = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
NSString *value;
value = [[savedUrl objectForKey:#"url"] stringValue];
NSLog(#"%#", value);
UPDATE: I successfully saved and retrieved data to and from plist. Issue is comming in this line
value = [[savedUrl objectForKey:#"url"] stringValue];
By removing stringValue solve the problem.
value = [savedUrl objectForKey:#"url"];
Now My second issue. I make three items named url, title, comment, types String in plist file.
How can i store different urls. Like
name website: inforains
url: inforains.com
title: Info Rains
comment: good website articles
name website: hitechnology
url: hitechnology.com
title: Hitechnology
comment: hmmm
ans soo on..
how can I store data like this.. so all name website will show on tableview and when user click on anyone, data related to that website will show. I hope i clear my question.
To store multiple records as three items named url, title, comment, types String in plist file
change types from string to dictionary and store data in each dictionary individually for key as 1,2,3 ...
OR
Use three mutable array for each url,title and comment and add object at index 0 at the time of saving it.
and access them when required as nsmutable array.
Change this line
value = [[savedStock objectForKey:#"url"] stringValue];
to
value = [savedUrl objectForKey:#"url"];
because you are saving NSString in dictionary... not NSNumber.
Second issue:
//create mutable array to save all objects
NSMutableArray *objectsArray = [data objectForKey:#"objectsArray"];
if(!objectsArray) {
objectsArray = [[NSMutableArray alloc] init];
}
//create and add dictionary into array
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjects:[NSArray arrayWithObjects:#"url1", #"title1", #"comment2",nil] forKeys:[NSArray arrayWithObjects:#"url", #"title", #"comment",nil]];
[objectsArray addObject:dictionary];
[data setObject:objectsArray forKey:#"objectsArray"];
[data writeToFile: path atomically:YES];
// to read data
NSMutableDictionary *savedUrl = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
NSMutableArray *objectsArraySAved = [savedUrl objectForKey:#"objectsArray"];
for (NSMutableDictionary *dic in objectsArraySAved) {
NSLog(#"URL %#", [dic valueForKey:#"url"]);
}
Are you sure [data writeToFile: path atomically:YES]; works? It returns a BOOL, you should check it.
Are you sure savedURL != nil?
For your new problem, you should put all your dictionaries in a mutable array.
NSMutableArray *bookmarkArray = [[NSMutableArray alloc]init];
[bookmarkArray addObject:data];

Create plist file via code

I want to create a plist file like this:
in code, and then it will be in the Caches folder.
I already know how to fetch the data.
You can do it like this:
//Create a Mutant Dictionary
NSMutableDictionary *theMutantDict = [[NSMutableDictionary alloc] init];
//Fill it with data
[theMutantDict setObject:#"John" forKey:#"Name"];
[theMutantDict setObject:#"Doe" forKey:#"Lastname"];
//Then search for cache dir
NSString *libraryDir = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
NSUserDomainMask, YES) objectAtIndex:0];
NSString *cacheDir = [libraryDir stringByAppendingPathComponent:#"Caches"];
//Then write the file
NSString *filePath = [cacheDir stringByAppendingString:#"/TheFile.plist"];
[theMutantDict writeToFile:filePath atomically:YES];
Just use the writeToFile-method of NSArray to store the array in a plist and arrayWithContentsOfFile to load it.
[self.array writeToFile:path atomically:YES];
self.array = [[NSArray arrayWithContentsOfFile:path];
// or in case you need to add/remove objects (NSMutableArray):
self.array = [[[NSArray arrayWithContentsOfFile:path] mutableCopy] autorelease];
If you got your data stored in an NSArray it's as easy as this:
// filling array with data ...
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
NSString *plistPath = [cachePath stringByAppendingPathComponent:#"my_nsarray_data.plist"];
[klasserArray writeToFile:plistPath atomically:YES];
// other stuff ...

Creating new dictionary when saving data to plist

So I am saving 3 NSStrings from 3 UITextFields to a property list. This works fine, but everytime I save something new, the app overwrites the data that was saved before. So basically there is only 1 Dictionary used, but i want the app to create a new dictionary everytime i save something new, so that no data gets deleted. I have no Idea how i could do this, so please help me!! :)
Code:
NSMutableArray *array = [NSMutableArray arrayWithCapacity:3];
NSArray *keys = [NSArray arrayWithObjects:#"1",#"2",#"3", nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",lab.text],[NSString stringWithFormat:#"%#",lab1.text],[NSString stringWithFormat:#"%#",lab2.text], nil] forKeys:keys]];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data.plist"];
[array writeToFile:path atomically:YES];
Use a NSMutableArray to which you add each new dictionary object and then write that array to data.plist.
Is there any reason why you alloc the array with capacity? I would just use [[NSMutableArray alloc] init], then add your objects.
Also, I had trouble saving NSMutableDictionaries in the NSUserDefaults, so what I ended up doing was just saving the dictionary to a file with
[dict writeToFile:filePath atomically:NO];
and initWithContentsOfFile or initWithContentsOfURL depending if I wanted to load a local or Internet file.
I should add, you can writeToFile, initWithContentsOf* for NSMutableArray as well.
Ok I've got it:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data.plist"];
NSLog(#"path='%#'",path);
NSFileManager *nfm = [[NSFileManager alloc] init];
if([nfm fileExistsAtPath:path])
{
// if file exists, get its contents, add more entries and write back
NSMutableArray *array = [[NSMutableArray alloc] initWithContentsOfFile:path];
NSArray *keys = [NSArray arrayWithObjects:#"4",#"5",#"6",nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",lab.text],[NSString stringWithFormat:#"%#",lab1.text],[NSString stringWithFormat:#"%#",lab2.text], nil] forKeys:keys]];
NSLog(#"modified array=%#",array);
BOOL ok = [array writeToFile:path atomically:YES];
if(!ok){
NSLog(#"Unable to write appended file");
return;
}
} else {
// if file doesn't exist, create a new one
NSMutableArray *array = [[NSMutableArray alloc] init];
NSArray *keys = [NSArray arrayWithObjects:#"1",#"2",#"3",nil];
[array addObject:[NSDictionary dictionaryWithObjects:[NSArray arrayWithObjects:[NSString stringWithFormat:#"%#",lab.text],[NSString stringWithFormat:#"%#",lab1.text],[NSString stringWithFormat:#"%#",lab2.text], nil] forKeys:keys]];
NSLog(#"new array=%#",array);
BOOL ok = [array writeToFile:path atomically:YES];
if(!ok){
NSLog(#"Unable to write new file");
return;
}
}