I have an app that when first launched reads in its data from a local XML file and is then archived for subsequent loads.
In future app updates its envisioned that this XML file might be updated with more data. To determine if I should re-load the XML data I have placed a custom key/value (a version number for the XML file) in the info.plist.
Now when I update the version number from 1.0 to 1.1 in the plist file, the app still reads it as 1.0. I'm guessing there is some kind of caching going on. Is there a way I can get the updated version on each load?
NSDictionary* infoDict = [[NSBundle mainBundle] infoDictionary];
NSString* version = [infoDict objectForKey:#"IdeasVersionNumber"];
NSLog(#"version = %#",version); // always 1.0?!!
Is there a better method to do this? I didn't want to create a separate plist/XML file just to hold the version number as it seems like a waste of resources to load/parse these each time when the info.plist is always read.
Many thanks for any advice.
I am surprised by this behavior, but as it looks like the Info.plist is not always read, you might as well make a separate file for it. Or use NSUserDefaults.
A simple clean build did the trick! Thanks to #MCannon for the tip. I should have copped onto it.
Related
I'm currently trying to change the value of CFBundleURLTypes in the .plist file to a new value but it seems that nothing has an effect and the old value is still present if I call
NSLog(#"%#", [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleURLTypes"]);
I searched the whole project for the old value but the old value is nowhere present. I cleaned the project twice but it doesn't have an effect. Does the suppurted URL Schemes get cached somewhere?
Thanks for your help
It's not cached; the main bundle is read-only on iOS, because changing it would invalidate the signature. Possibly barring jailbreaking, I don't think there's a way to alter the Info plist after deployment.
Solved it. Problem was that I added an extra target to the app and the extra .plist for the target was not included in the project, so changes doesn't had an effect.
I'm creating one iphone application that uses and xml to get some data from and a few images. Lets say that originally these files will be in the application bundle. But then I want to make my application to get updates from a web service. So lets say I download a new xml and new Images.
Where do I save them? I think I will be able to save them in the application "cache" right?
But then How do I make my application check if this resource exists in the cache, then load that one...else load the one in the application bundle?
iOS to be used 4.3 but if it makes it easier we can go to 5.
You will have to use NSFileManager to save the file to and load it from the document directory. Check out the following link, which actually contains all the info you need:
http://www.friendlydeveloper.com/2010/02/using-nsfilemanager-to-save-an-image-to-or-loadremove-an-image-from-documents-directory-coding/
If [UIImage imageWithContentsOfFile:fullPath]; (fullPath being the NSString with contains the path to your image in your document directory) returns nil, then the image is not present, so you'll have to load the default image from your bundle.
You can access your default bundle files using [[NSBundle mainbundle] pathForResource:#"yourFileNameHere" ofType:#"yourTypeHere"] This will return the path to your file in your bundle and you can just pass it to [UIImage imageWithContentsOfFile:]
This example only applies to UIImage, but you can easily adapt it to use other classes. I think the UIImage example is enough for illustrative purposes though.
You'll probably want to create a versioning scheme. After the app starts (or periodically) call the web service for a very cheap call to ask what version it has. You'll know what version you have (write in a plist or have the version number in the folder structure). Another options is pushing a notification.
The next thing you'll have to think about is how the app reacts to getting a new data set. You could do it only on start up but that might block the start up experience if you're bounding it to a web call. The other option is to allow the dataset to change while the app is open. You could have a model that uses NSNotificationCenter to notify your views and controllers that the data has changed. I would probably version the storage as well (folder per) to help with the transition.
As for how, you can make web requests with something like ASIHttpRequest, NSFileManager to write to the documents directory, and plists to save settings like version.
Aw geez! I screwed something up!
I'm a Core Data noob, working on my first iOS app. After much Stack Overflowing I'm using this code:
NSString *path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"momd"];
if (!path) {
path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"mom"];
}
NSAssert(path != nil, #"Unable to find Resource in main bundle");
CoreData is the name of my app.
I've tried to put in initial data into the app by finding the path to the sqlite file in my iPhone simulator, and then going and inserting into that sqlite file. But at some point, I moved the sqlite (thinking it would create a fresh copy), deleted the app from the simulator, and the sqlite file is gone. I'm not sure if I'm leaving out some part of the process (this was a few hours ago) but the end result is that everything is screwed up.
How do I resubstantiate this sqlite / momd file? "Clean" and "Clean all targets" are grayed out.
I'm happy to post the relevant code from my app that would help shed some light on this problem but there's tons of code relating to Core Data which I don't understand, so I'm not sure what part to post! Any help is greatly appreciated.
Here are a few recommendations:
The code you posted to get the .mom(d) file is not exactly the recommended way. Use mergedModelFromBundles instead, as in
self.managedObjectContent= [NSManagedObjectModel mergedModelFromBundles:nil];
It takes care of getting the path, choosing/merging the correct mom or momd, and initializing of the MOC all by one step. You should use this. But note that you need to clean the build process once in a while, as discussed in this SO question/answer.
This shows that, although crawling through StackOverflow is often good, that's not the best approach when you deal with a big framework like CoreData.
Honestly, take a day and read the documentation from the start to the end. You might want to google bits of code and to start coding immediately, but reading through the documentation definitely saves the development time considerably in the long run.
Also, Marcus Zarra's CoreData book (see here) helped me a lot. It might look expensive, but it was totally worth while.
On a different topic, I don't think it a good strategy to put the pre-cooked sqlite file into the simulator's directory (inside ~/Library/Application Support/iPhone Simulator/) even for development purpose... because it doesn't work on the real device. If you need to do so, put the sqlite file as a resource of the app, and copy it at the launch time to either Documents or Caches, and use that afterwards.
If the clean targets menu items are grey, it could be that you have the app running.
The way you're getting the path to the mom/momd looks like you've tried to integrate CoreData after the fact (rather than opting to have it included when you created the project). I had the same issues as you probably do. The default code that looks for momd assumes it exists, and you should be able to, as well, if you add a version of your data model to the project.
To do that, select the xcdatamodel file, and in the Design menu select Data Model > Add Model Version. Then, you'll have CoreData.xcdatamodeld with a subtree containing Coredata.xcdatamodel and a new version. You can just delete the extra version and you'll have the hierarchy you need. If you build and look in the App bundle, you'll see the CoreData.momd directory with the mom inside.
Those steps aren't included in any of the CoreData tutorials I've found so far. I hope it's helpful!
Practical answer:
You might try the following code if you're having path issues. You'll also want to check that your xcdatamodel file is included as a resource for your target.
[[NSBundle bundleForClass:[self class]] pathForResource:#"CoreData" ofType:#"momd"]
Better answer: There's a distinction between a momd file, which represents your NSManagedObjectModel, and a sqlite file, which is used by your NSPersistentStore object. The best approach to working with CoreData is to let the SDK handle all the interaction with sqlite. The only thing you should need to set up regarding sqlite is telling your NSPersistantStore where to stash your sqlite file. The managed objects that you define in your managed object model will handle all the data insertion and updates for you.
If you're not familiar with the objects I've referenced, check out the apple docs for a very helpful overview. You'll definitely want to take the time to grasp the full CoreData stack, as the time investment will pay off in far less headaches if you work with Core Data the way Apple expects. It is a more abstracted design than many web developers are accustomed to, but trying to circumvent that abstraction by working directly with SQLite creates its own set of complexity.
http://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdBasics.html#//apple_ref/doc/uid/TP40001650-TP1
I found it very valuable to go look at my executable in the simulator "disk image".
If you head into ~/Library/Application Support/iPhone Simulator//Applications, you can see the apps on the phone. Figure out which one is yours (use find, or look at the date), and then browse into your target.app and look for the .momd file.
In my case, my momd name was different from my .xcdatamodeld file name, which was why core data couldn't load it. Once I corrected the name in the AppDelegate things started working correctly.
Note that the momd "file" seems to actually be a directory (like the .app setup).
--
In terms of adding the code to initialise core data; I did this too. I created a new blank project with core data enabled, and copied over all the initialisation steps for core data in the AppDelegate file. There's a whole bunch of stuff related to initialising the whole stack, so if that is not present, you need to put it there.
If you added a version to your model, change .mom to .momd
NSString *path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"momd"];
if (!path) {
path = [[NSBundle mainBundle] pathForResource:#"CoreData" ofType:#"momd"];
I'm wondering what is the best way to store long strings in iPhone? e.g. I have 'about' page in my app which is basically an html page, and I load it to UIWebView.
Is there any better way so store it apart from store it as string in code? may be in resource file?
Thank you
You can add the html file directly as a resource in your bundle. Then when you want to use it you can use the following code
NSString* fileName = [[NSBundle mainBundle] fileForResource:#"myHtmlFile" withExtension:#"html"];
NSString* fileContents = [NSString stringWithContentsOfFile:fileName encoding:NSUTF8Encoding error:nil];
[webView loadHTMLString:fileContents baseURL:baseURL];
You can use a plist that is very flexible and fast to load.
If you plan to localize this text into different language you should use String File (both are available under "Resource" when you choose to create a new file in xcode)
I haven't done this myself, but I'm guessing you could simply deploy an HTML file with your bundle (just add it as a resource to your project) and point the UIWebView at it.
If the string will never change throughout the life of the application, you should use a resource packaged in your application bundle.
As the previous posts alluded to, you have several options. You can make a plist, an HTML file, or any other kind of file resource.
A nice benefit of these options is that you have the ability to make localized versions.
If the string will change through use of the application, you may also consider storing the value of the string in a local database with SQLite and CoreData.
I've not found a answer to this question anywhere, but this seems like a typical problem:
I have in my "Resources" Folder a XML File that my App needs to show some informations. But I have to check in my Server, if the server has a newer XML available, so I should replace my XML (in the resources folder) for this new one.
How can I achieve that?
Thanks in advance for any help!
I'm not sure you get write access to the Resources section. A better way to do it, is to download the folder to your "Documents" directory. Then when you app checks to load the XML file, it should see if there is a version in the documents directory and load that first. If it doesn't find a replacement one, then it uses the original version.
This means the original XML file can be used if there is some issue with the downloaded one. Which happens, either the network got cutoff and didn't finish, or the server gave the wrong xml file and it doesn't validate.
You can access the document directory using:
NSArray *docPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *destinationpath = [docPaths lastObject];
If you are wanting to check the versions, you can use an element inside the XML to give you the version number or you can use the Last-Modified date. An example of the version could be:
<myxml version="234">
Then you could check this value.
You can let the API, a method to check The most recent modified date. And in the client site, you can get your most recent modified date of the XML file and compare with the one in the server. You can get the description of the XML file and return true or false to replace.
U need some way to distinguish the new XML from the old one. I assume you have some mechanism to do that. Typically some kind of Version node inside the XML.
If the server's version and the one on ur phone is different, get the new one else don't do anything.