Can't read data that was written with writeToFile:atomically - ios

I have these 2 lines of code:
[[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://somepath.com/first.plist"]] writeToFile:#"first.plist" atomically:YES];
NSDictionary *dTmp=[[NSDictionary alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"first" ofType:#"plist"]];
But my dTmp is null. I'm not specifying any path, so it's supposed to be stored in the root directory, right?
What is my mistake?

There are a couple of problems with your code.
You should not use synchronous networking calls like dataWithContentsOfURL. it willl block the UI. Look at NSURLSession instead.
You can't write to a file in the root directory. iOS apps are limited to a few sandbox directories like the apps' documents and caches directories.
You should write code that writes and reads to one of your allowed sandbox directories.
Also, don't write compound statements. It makes it hard to read and debug. Do one thing on each line.
Check the return value at each step to see what's going wrong.

Related

Can I get the source code from the .m file?

I want to get the source code from .m file so I use this way:
NSString *path = [[NSBundle mainBundle] pathForResource:#"ViewController" ofType:#".m"];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSLog(#"%#", content);
However the console print "null".
I wonder if there any way we can use to get .m file's content even if use private method.
If you're copying the actual source file inside your application's bundle and including it, then sure, that'll work, although I don't know why you'd want to do that.
If you're wondering if you can decompile a binary file back to Objective-C code, then no, you can't do that—at least not simply. If you have good enough skill with assembly language, of course, you can read a disassembly of the code, figure out what the original Objective-C did, and use that knowledge to recreate it.

How can I change .plist entries based on my Scheme?

I have some App-Info.plist entries that need to change based on my environment. When I'm doing developmental work, they need to be one set of values, vs QA vs Production.
What would be nice is if I could simply have a script or something that runs based on the Scheme used to do the compilation.
Is this possible?
You can do it by performing some extra steps:
Duplicate [AppName]-Info.plist file by any name. Example: [AppName]-Info-dev.plist or [AppName]-Info-staging.plist etc
Map newly created .plist file in App's Target Settings. Get idea from following screenshot:
At the end, if you want to get some entry from .plist file then you need to get it like: [[[NSBundle mainBundle] infoDictionary] objectForKey:#"baseUrl"]
Project setting will automatically pick correct .plist file and give you required value.
I think msmq's answer is valid, but you should be a little careful about using the main info.plist this way. Doing that suggests that all your versions of info.plist are almost identical, except for a couple of differences. That's a recipe for divergence, and then hard-to-debug issues when you want to add a new URI handler or background mode or any of the other things that might modify info.plist.
Instead, I recommend you take the keys that vary out of the main info.plist. Create another plist (say "Config.plist") to store them. Add a Run Script build phase to copy the correct one over. See the Build Settings Reference for a list of variables you can substitute. An example script might be:
cp ${SOURCE_ROOT}/Resources/Config-${CONFIGURATION}.plist ${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Config.plist
Then you can read the file using something like this (based on Read in the Property List):
NSString *baseURL;
NSString *path = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSDictionary *dict = (NSDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListImmutable
format:NULL
errorDescription:&errorDesc];
if (dict != nil) {
baseUrl = dict[#"baseURL"];
} else {
NSAssert(#"Could not read plist: %#", errorDesc); // FIXME: Return error
}
There are other solutions of course. I personally generally use the preprocessor for this kind of problem. In my build configuration, I would set GCC_PREPROCESSOR_DEFINITIONS to include BaseURL=... for each build configuration and then in some header I would have:
#ifndef BaseURL
#define BaseURL #"http://default.example.com"
#endif
The plist way is probably clearer and easier if you have several things to set, especially if they're long or complicated (and definitely if they would need quoting). The preprocessor solution takes less code to process and has fewer failure modes (since the strings are embedded in the binary at compile time rather than read at runtime). But both are good solutions.
You can add a User-Defined-Setting in Xcode>Build-Settings, add its values according to all the schemes listed there. And then simply use that as a variable in Info plist file. That should work just fine.
This way you can avoid creating duplicate plist files, just for the sake of one or two different properties.

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

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!

How to hardcode a text file into iOS app

Currently using:
NSString* path = [[NSBundle mainBundle] pathForResource:#"filename"
ofType:#"txt"];
This only works however after I manually add the file into the application bundle.
I can use the documents directory but that's even worse.
After resetting the sim, the file goes away. How do I get it to stay?
How would I write out the file from somewhere to there? Like from a file in source? I don't want to alloc a several megabyte NSString object.
Credit goes to Lyle42 on freenode irc (#iphonedev):
I wasn't aware this field even existed. By adding any file into the copy files build phase (under build phases), they persist across builds.
Then this code:
NSString *_filePath = [[NSBundle mainBundle] pathForResource:#"filename" ofType:#"txt"];
NSLog(#"%#",_filePath);
NSData *_binary = [NSData dataWithContentsOfFile:_filePath];
NSString *_fileContents = [[NSString alloc] initWithData:_binary encoding:NSUTF8StringEncoding];
Works just great. (loads contents of file into an NSString).
You can use this to get your path for the file
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [documentPaths objectAtIndex:0];
filePath = [path stringByAppendingPathComponent:file_name];
you can get the file from your app bundle using
NSString *pathFromApp = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:file_name];
I can use the documents directory but that's even worse.
Not really. The documents directory is persistent, and the recommended directory for essential documents created by your application. In addition, it's backed up by iTunes, and can be specified for iCloud backup. The app bundle just can't compete with that!
After resetting the sim, the file goes away. How do I get it to stay?
You can't, once the simulator has been "reset" (and I mean a hard wipe of all data and apps), nothing is saved. In between test builds, however, data copied out of the app bundle should survive unless you're storing things in /tmp. In addition, objects in the bundle are "refreshed" (recopied) in the build phase, meaning your resource isn't wiped out, merely reset to the one specified in your project.
How would I write out the file from somewhere to there? Like from a
file in source? I don't want to alloc a several megabyte NSString
object.
To read the file into memory, it would have to turn into a "several megabyte NSString" eventually, but if you really want to add a little IO to your plans for a massive string object, have a look at NSFileManager.

parsing XML located inside the project

This is the first time i'm going to parse an XML file with Xcode and it differs a bit from what I'm used to...
I read these 2 docs :
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/XMLParsing/Articles/UsingParser.html
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/XMLParsing/Articles/HandlingElements.html#//apple_ref/doc/uid/20002265-BCIJFGJI
I understand what's going on... But I still got one question. How can I do to parse an XML file which is located directly inside the project (I mean, for example in the resource file, where I usually put my images?). They usually show how to get the url of the XML file..But it's not the case here. It's going to be loaded directly on the iPad, among images and everything...
You Simply have to give the path of a local file :-
NSString *myFile= [documentsDirectory stringByAppendingPathComponent:#"youFile.xml"];
NSURL *xmlFile = [NSURL fileURLWithPath:myFile];
NSXMLParser *parser= [[NSXMLParser alloc] initWithContentsOfURL:xmlFile];
This is just an example implement your own login , use above two lines to get the path of local file.

Resources