dictionaryWithContentsOfFile Returns Nil rather the NSdictionary - ios

I have created a file named Dict.json .Contents of the file are valid json containg
{"mydata":[{
"A":4,
"B":14,
"C":7
},
{
"A":4,
"B":12,
"C":7
},
{
"A":34,
"B":154,
"C":6
},
{
"A":34,
"B":162,
"C":6
}]}
I want to create a NSDictonary from this file .I tried the following but it returns nil .
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Dict" ofType:#"json"];
NSMutableDictionary *newArr1=[NSMutableDictionary dictionaryWithContentsOfFile:filePath];
I am also checking that file is not nil;
NSData *myData = [NSData dataWithContentsOfFile:filePath];
if (myData) {
NSLog("There is Data in File !!!!")
}

For loading the json data you will need NSJSONSerialization to fetch json data from file
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Dict" ofType:#"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSMutableDictionary *dic1 = [[NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil] mutableCopy];
Your code only work on plist file, not on json file.

From the Documentation
+ (id)dictionaryWithContentsOfFile:(NSString *)path
Parameters
path
A full or relative pathname. The file identified by path must contain a string representation of a property list whose root object is a dictionary.
Return Value
A new dictionary that contains the dictionary at path, or nil if there is a file error or if the contents of the file are an invalid representation of a dictionary.
As mentioned above you can only create the dictionary + (id)dictionaryWithContentsOfFile:(NSString *)path using plist file not from the .json.

Related

Access file directory outside main Bundle

I have a NSArray of strings, something like this:
OBJECT 1
OBJECT 2
OBJECT 3
Now, Inside a local folder, I have folders with the objects name:
/User/Dropbox/ALLOBJECTS
OBJECT 1
OBJECT 2
OBJECT 3
Inside each folder, I have two pictures named after the objects:
OBJECT1-PIC1.png
OBJECT1-PIC2.png
What I need is the path to those pictures, basically, My object has two columns (FILE1, FILE2) which I want to save the pictures.
I will iterate through the array, find the Folder with the same name (inside a local folder) and copy the pictures.
I'm trying to use, as a test:
NSString *filePath = #"~/Users/me/Dropbox/ALLOBJECTS"
NSData *data = [[NSFileManager defaultManager] contentsAtPath:filePath];
NSLog(#"data: %#", data);
but am getting NULL data.
Is it possible to achieve this?
This will only work on the simulator.
NSFileManager* fileManager = [NSFileManager defaultManager];
NSArray* rootContent = [fileManager contentsOfDirectoryAtPath:#"/Users/me/Pictures" error:nil];
NSLog (#"pictures dir contains:");
for (NSString* fileName in rootContent)
NSLog (#"%#", fileName);
NSData *data = [fileManager contentsAtPath:#"/Users/me/Pictures/Pw0wF.png"];
UIImage *image = [UIImage imageWithData:data];

Need to get an XML from server encoded in windows-1252 for my iOS application

I have an iOS application which is download from server an XML file encoded in Windows 1252.
I am using the following code to save it to my document folder :
NSString *path = #"http://server/file.xml";
NSURL *URL = [NSURL URLWithString:[path stringByAddingPercentEscapesUsingEncoding:NSWindowsCP1252StringEncoding]];
NSData *xmlData = [NSData dataWithContentsOfURL:URL];
if(xmlData == nil) {
// Error - handle appropriately
NSLog(#"ERROR");
}
NSString *applicationDocumentsDir=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *storePath=[applicationDocumentsDir stringByAppendingPathComponent:#"annonces.xml"];
[xmlData writeToFile:storePath atomically:TRUE];
NSLog(#"write xml");
It doesn't work, I've got an nil response when I try to read it with the parser. How could I do to get it properly. I cannot change the encoding of te xml which is on the server. If I change the XML encoding manually, I've got a correct response.
This is how I pass the XML string to parse it with XML Dictionnary class :
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *finalPath = [path stringByAppendingPathComponent:#"annonces.xml"];
NSString *string = [[NSString alloc] initWithContentsOfFile:finalPath encoding:NSWindowsCP1252StringEncoding error:NULL];
NSLog(#"string: %#", string);
NSDictionary *items = [NSDictionary dictionaryWithXMLString:string];
NSLog(#"dictionary: %#", items);
Your URL variable contains the url address of the location of the XML file you want to download.
However you are applying NSWindowsCP1252StringEncoding to the url, not to the content of the file.
xmlData is nil because dataWithContentsOfURL: cannot find the file at the location you have specified within URL.
You need to download the file first, then once its downloaded then you can be concerned about what encoding its in and how to parse it.
The way you are using NSWindowsCP1252StringEncoding has got nothing to do with the content of the file.

How to protect iOS bundle files like plist ,image,sqlite,media files

I have created sample hello world project and then added Data.plist file to resource folder.
Now people can easily get the bundle files by unzipping the .ipa file. Is there any ways to protect the Data.plist file that saved in the application bundle of iPhone app?
Encryption is a decent method of scrambling the data but i don't know how to implement encription concept.
Do you have any sample code?
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
NSArray *arrData = [[NSArray alloc]initWithContentsOfFile:filePath];
NSData *datas = [NSKeyedArchiver archivedDataWithRootObject:arrData];
[datas writeToFile:filePath atomically:YES];
After extracting IPA file
encrypt the files on the mac... during deployment:
FIRST: don't add the files to be encrypted to the target
e.g. Encryption-Test.plist
THEN add a shell script phase to your xcode project to use openssl to encrypt&copy the files.
e.g.
openssl enc -e -aes-256-cbc -pass pass:asdasd
-in $PROJECT_DIR/test/Encryption-Test.plist
-out $TARGET_BUILD_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH/Encryption-Test.enc
add RNCryptor source files from github to your project. This makes decryption of openSSL encrypted AES files really easy. (Thanks rob!) https://github.com/RNCryptor/RNCryptor
(Apple's CCrypt api isn't nice to work with directly)
load the data and decrypt it:
e.g.
#implementation TestViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *path = [[NSBundle mainBundle] pathForResource:#"Encryption-Test" ofType:#"enc"];
NSData *passEncryptedData =[[NSData alloc] initWithContentsOfFile:path];
NSString *pass = #"asdasd";
NSData *dataDecrypted = [RNOpenSSLDecryptor decryptData:passEncryptedData withSettings:kRNCryptorAES256Settings password:pass error:nil];
id plist = [NSPropertyListSerialization propertyListFromData:dataDecrypted mutabilityOption:NSPropertyListImmutable format:nil errorDescription:nil];
assert(plist);
self.text.text = [plist description];
}
#end
added full sample: https://github.com/Daij-Djan/encryptBundleFiles
I struggled with the above examples with no success and finally found some code that lets you decrypt an encrypted file (any file in fact) with openssl. I'm working in the documents folder for this example and I'm not using any shell scripting. Here's how to do it:
Encrypt your file in the Terminal like so:
openssl aes-256-cbc -in questions.plist -out questions.enc
Add the file(s) to your Xcode project by dragging them to the Supporting Files directory
Download the RNCryptor library here.
Find the RNCryptor library in the downloaded project: encryptBundleFiles-master/test/RNCryptor and add them into to your Xcode project
Import the RNDecryptor.h and RNOpenSSLDecryptor.h files into your class file, like ExampleViewController.m file.
Add the code below from where you want to call it, like a certain function.
And you're done. You can now use the plist file to for example populate a tableview.
// Copy the plist file from the resources folder to the documents folder
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *txtPath = [documentsDirectory stringByAppendingPathComponent:#"questions.enc"];
if ([fileManager fileExistsAtPath:txtPath] == NO) {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"questions" ofType:#"enc"];
[fileManager copyItemAtPath:resourcePath toPath:txtPath error:&error];
}
NSString *filePath1 = [documentsDirectory stringByAppendingPathComponent:#"questions.encr"];
NSData *passEncryptedData =[[NSData alloc] initWithContentsOfFile:filePath1] ;
NSString *pass = #"asdf"; // Insert your password from step 1
NSData *dataDecrypted = [RNOpenSSLDecryptor decryptData:passEncryptedData withSettings:kRNCryptorAES256Settings password:pass error:&error];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:#"questionsDECRYPTED.plist"]; //The Decrypted file saved here
[dataDecrypted writeToFile:appFile atomically:YES];
Based on Daij-Dan's solution, I created a couple of Swift String extensions. Requires RNCryptor
They are used like this:
//Decrypt a UIImage
let decryptedImage = "encyptedFileNameInBundle".decryptedImage(password: "test")
//Decrypt NSData
let decryptedData = "encyptedDataFileNameInBundle".decryptedData(password: "test")
The string is the file name of the resource that has been added to the bundle resources. It assumes the file has an "enc" extension by default, otherwise, pass the file extension with "fileExtension:".
Extension:
public extension String {
func decryptedImage(password: String, fileExtension: String = "enc") -> UIImage? {
//create encypted file with:
//openssl aes-256-cbc -in fileName.jpg -out fileName.enc
if let decryptedData = self.decryptedData(password: password, fileExtension: fileExtension) {
return UIImage(data: decryptedData)
}
return nil
}
func decryptedData(password: String, fileExtension: String = "enc") -> Data? {
//create encypted file with:
//openssl aes-256-cbc -in fileName.data -out fileName.enc
if let fileURL = Bundle.main.url(forResource: self, withExtension: fileExtension) {
do {
let encyptedData = try Data(contentsOf: fileURL)
return try RNOpenSSLDecryptor.decryptData(encyptedData, with: kRNCryptorAES256Settings, password: password)
} catch {
print("encryptedImage ERR: \(error.localizedDescription)")
}
}
return nil
}
}
Use a NSKeyedArchiver to create an NSData object from your dictionary (NSKeyedArchiver archivedDataWithRootObject:). Then encrypt the NSData with AES and write that to your file.
Reading just the reverse process: first, read the NSData, decrypt it via the method from the mentioned link, then pass the decrypted NSData to NSKeyedUnarchiver (NSKeyedUnarchiver unarchiveObjectWithData:) and you get your dictionary back. That you can use for plist file or as NSDictionary to keep your data safe.
Example 1:
Example 2:
EDIT 2:
NSDictionary *Your_NSDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"Obj1", #"Key1",
#"Obj2", #"Key2", nil];
//store dictionary
NSMutableData *yourData = [[NSMutableData alloc] init];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:Your_NSDictionary forKey: #"key"];
[archiver finishEncoding];
[yourData writeToFile:#"FilePath" atomically:YES];
or
NSString* filePath = [[NSBundle mainBundle] pathForResource:#"Data"
ofType:#"plist"];
NSDictionary* data = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSMutableDictionary * rootObject;
rootObject = [NSMutableDictionary dictionary];
[rootObject setValue: data forKey:#"accounts"];
[NSKeyedArchiver archiveRootObject: rootObject toFile: path];

iOS: read raw plist

I want to read the UserDefaults plist but not as Dictionary or Data. I want it as string like it is when you open it with an editor.
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *homeDir = [documentsPath stringByReplacingOccurrencesOfString:#"Documents" withString:#""];
NSString *defaultsPath = [homeDir stringByAppendingString:[NSString stringWithFormat:#"Library/Preferences/%#.plist", [[NSBundle mainBundle] bundleIdentifier]]];
NSData *data = [NSData dataWithContentsOfFile:defaultsPath];
Already tried:
`NSString *contents = [NSString stringWithContentsOfFile:defaultsPath encoding:NSUTF8StringEncoding error:&error];
which ends up with
The operation couldn’t be completed. (Cocoa error 261.)
Property list formats can be either binary or text. Binary plists can't be loaded into an NSString because strings are for text, not arbitrary binary data. The error you're getting seems to suggest that the file cannot be interpreted as UTF-8, which either means it is encoded using another encoding or is not text at all.
If you are certain that the property list is a text property list, you can use:
NSStringEncoding enc;
NSString *contents = [NSString stringWithContentsOfFile:defaultsPath usedEncoding:&enc error:&error];
This will allow the framework to determine the encoding of the text plist for you. If it isn't a text plist, you can convert it to one using the plutil command line utility:
plutil -convert xml1 file.plist
Or, alternatively you can do this in code by loading the plist using the NSPropertyListSerialization class, obtaining the NSData from it that represents the plist as the XML format, and then convert that to a string.
An example would be [uncompiled and untested]:
// load the file as-is into a data object
NSData *data = [NSData dataWithContentsOfFile:defaultsPath];
// convert the plist data into actual property list structure
id plistFile = [NSPropertyListSerialization propertyListWithData:data
options:0
format:NULL
error:&error];
// get the XML representation of the property list
NSData *asXML = [NSPropertyListSerialization dataWithPropertyList:plistFile
format:NSPropertyListXMLFormat_v1_0
options:0
error:&error];
// convert the NSData object into an NSString object
NSString *asString = [[NSString alloc] initWithData:asXML encoding:NSUTF8StringEncoding];
This should work whether the original plist is in XML or binary format. In this example, I am assuming that the XML representation of the property list is in fact UTF-8 encoded, as this is the most common encoding for XML data.

how to store an image path in a plist?

I know this is probably a silly question but I'm storing most of my game data in a plist - with that I'd like to include references to images used within my game - same hierarchal level as 'supporting files'. I have different types of images stored in 3 separate folders. One folder for example is called imageclue. How could I store the path in my plist, I'm stuck because I can't just store the path in my plist as string - filename.jpg. I've tried getting the path of the file but when I log it out it .
Sorry if I'm not explaining well and thank you in advance for any help :)
EDIT**
I have a plist file added to my program I don't want to programatically add to it as the images are constants - the screenshots below show a tutorial instead of the filename.jpg (because that won't work seen as my images are stored in a file) I wondered what path name do I use as a string.
The image is from a tutorial off of appcoda.com - where it says thumbnails are the image path files. If you look at where the images are stored on the left - they are stored with the program files. My images are in a folder in there so I'm confused as to what to enter in my plist for the image file.
Hope this clears up what I meant, sorry :)
Store three variables in .h file
#interface YourViewController : UIViewController
{
NSString *folder1;
NSString *folder2;
NSString *folder3;
}
in viewdidload:
-(void) viewdidLoad
{
//get the documents directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//getting the folder name:
folder1 = [NSString stringWithFormat:#"%#/imageclue",
documentsDirectory];
folder2 = [NSString stringWithFormat:#"%#/folder2",
documentsDirectory];
folder3 = [NSString stringWithFormat:#"%#/folder3",
documentsDirectory];
}
-(NSArray*) getPlistFromFolder:(NSString*)folder imageName:(NSString*)image
{
NSString *imageTitle = [NSString stringWithFormat:#"%#/image",
folder];
NSArray *data = [[NSArray alloc] initWithContentsOfFile:plistName];
return data;
}
So in the plist file, just store the image name.
Hope this helps...
Do it like this,
NSDictionary *imagePaths = #{#"image 1": [NSHomeDirectory() stringByAppendingPathComponent:#"image 1"]};
[self writeToPlist:imagePaths];
- (void)writeToPlist:imagePaths:(id)plist{
NSError *error;
NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist format:kCFPropertyListXMLFormat_v1_0 options:0 error:&error];
if(error){
NSLog(#"Could not write to file");
return;
}
[data writeToFile:[self plistPath] atomically:YES];
}
Like wise loading is simple as this;
[self loadImagePathForImageNamed:#"image 1"];
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
}
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
NSData *data = [NSData dataWithContentsOfFile:[self plistPath]];
NSString *error;
NSPropertyListFormat format;
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if(error){
NSLog(#"Could not open plist %#", error);
return nil;
}
return dictionary[imageName];
}
You may have to handle the error when the file is not there by creating a new one, otherwise this should work.
You are storing path right way, just need to store filename of image with extension in plist when your images are in your Application Bundle, for more reference you can define key name Instead "item1", "item2" in your plist.
Now coming to actual Question, how to access image from plist
Step 1 : Read your recipes.plist from Application Bundle
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"recipes" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
Step 2 : Now Get Image/Thumbnails name out of it, which you want to load
Step 3 : Define following Function in your Controller, which returns image from name
- (UIImage *)getImageWithName:(NSString *)imageFileName
{
NSString *ext = [imageFileName pathExtension];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:[imageFileName stringByDeletingPathExtension] ofType:ext];
return [UIImage imageWithContentsOfFile:imagePath];
}
HOW TO USE
Suppose you want to load Image with key "Item2" then write following code
NSString *imageFileName = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item2"];
UIImage *item2Image = [self getImageWithName:imageFileName];
For "Item6"
NSString *imageFileName1 = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item6"];
UIImage *item6Image = [self getImageWithName:imageFileName1];

Resources