English is not my native language; please excuse typing errors.
I have read similar questions but I couldn't find anything that could solve my problem..
I am making an iPhone app that shows a list of notes in a table view, and user can add and delete note.
When I add a new note to a server, I get a plist which looks like this:
<plist>
<dict>
<key>success</key><true />
<key>note</key><string>new note</string>
</dict>
</plist>
After user taps the add button, I request a connection to the server, and I do,
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[resultData appendData:data];
}
and then
-(void)connectionDidFinishLoading:(NSURLConnection *)connection{
NSError *sendError;
NSArray *result = [NSPropertyListSerialization propertyListWithData:resultData options: NSPropertyListMutableContainersAndLeaves format:NULL error:&sendError];
if([result count] > 0){
for(NSDictionary *dict in result){
for(NSDictionary *dict in result){
// This gives me an error "objectForKey: unrecognized selector to instance"
NSLog(#"%#", [dict objectForKey:#"success"]);
}
}
}
}
I thought that dict may not be a dictionary, so I tried NSLog(#"%#", dict); and it shows just success. But, I still don't know how I can get the value of success.
Try this ::
.h File
#property (nonatomic,retain) NSDictionary *dict;
.m file
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSString *rootPath;
rootPath = [[NSBundle mainBundle] pathForResource:#"FILE_NAME"
ofType:#"plist"];
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:rootPath];
dict = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
//NSLog(#"PLIST retrive:%#",plistXML);
if (!dictLang) {
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
Get Value ::
NSLog(#" Success :: %#", [dict objectForKey:#"success"]);
Hope, it'll help you.
Thanks.
if ([[NSPropertyListSerialization propertyListWithData:resultData options: NSPropertyListMutableContainersAndLeaves format:NULL error:nil] isKindOfClass:[NSDictionary class]])
{
NSMutableDictionary *tempDicts = [NSPropertyListSerialization propertyListWithData:nil options: NSPropertyListMutableContainersAndLeaves format:NULL error:nil];
}
else if ([[NSPropertyListSerialization propertyListWithData:resultData options: NSPropertyListMutableContainersAndLeaves format:NULL error:nil] isKindOfClass:[NSArray class]])
{
NSMutableArray *tempDicts = [NSPropertyListSerialization propertyListWithData:nil options: NSPropertyListMutableContainersAndLeaves format:NULL error:nil];
}
The return value of the method on NSPropertyListSerialization is - quite logically - the root object of the property list you receive. So result is an NSDictionary (I can't even imagine why you assume it's an array...!?). Try this:
NSLog(#"%#", [result objectForKey:#"success"]);
Related
Why NSDictionary cannot be written?? I have checked the content of the dictionary: all the instances are of NSString and NSNumber. I checked permissions: a text file with the same name at the same path is written well. Of course, my dictionary is not empty.
NSString *file = ...
NSDictionary *dict = ...
// check dictionary keys
BOOL wrong = NO;
for (id num in [dict allKeys]) {
if (![num isKindOfClass:[NSNumber class]]) {
wrong = YES;
break;
}
}
if (wrong) {
NSLog(#"First");
}
// check dictionary values
wrong = NO;
for (id num in [dict allValues]) {
if (![num isKindOfClass:[NSString class]]) {
wrong = YES;
break;
}
}
if (wrong) {
NSLog(#"Second");
}
if (![dict writeToFile:file atomically:YES]) {
// 0k, let's try to create a text file
NSLog(#"Names writing error!");
[#"Something here... .. ." writeToFile:file atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
Output: "Names writing error!"
Text file is created successfully.
Writing out a dictionary creates a property list, and according to the documentation all keys in a property list must be strings.
... and although NSDictionary and CFDictionary objects allow their keys to
be objects of any type, if the keys are not string objects, the
collections are not property-list objects.
NSNumber objects as keys are not supported.
As #vadian points out, you cannot write plist with numeric keys. But you can use NSKeyedArchiver:
NSURL *documents = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:false error:nil];
NSURL *fileURL = [documents URLByAppendingPathComponent:#"test.plist"];
// this will not work
NSDictionary *dictionary = #{#1: #"foo", #2: #"bar"};
BOOL success = [dictionary writeToFile:fileURL.path atomically:true];
NSLog(#"plist %#", success ? #"success" : #"failure");
// this will
fileURL = [documents URLByAppendingPathComponent:#"test.bplist"];
success = [NSKeyedArchiver archiveRootObject:dictionary toFile:fileURL.path];
NSLog(#"archive %#", success ? #"success" : #"failure");
And you can read it back with NSKeyedUnarchiver:
// to read it back
NSDictionary *dictionary2 = [NSKeyedUnarchiver unarchiveObjectWithFile:fileURL.path];
NSLog(#"dictionary2 = %#", dictionary2);
Note, you can do this with any class that conforms (and properly implements) NSCoding. Fortunately, NSDictionary conforms already. You have to make sure that any objects inside the dictionary, also conform (both NSString and NSNumber do). If you had a custom object in your dictionary, you'd have to make it properly conform yourself.
This is all described in the Archives and Serializations Programming Guide.
I am uncertain of how memory is managed in my particular case...
I have two methods:
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
plistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:plistPath];
NSLog(#"AFTER plistPath: \n%#",plistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableString * plistPath;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",plistPath);
// Write to plist
bool res = [temp writeToFile:plistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}
The problem is that the bottom method that sends in "plistPath" to the above method retrives a null plistPath after the above method has set it. Why and how can I fix this?
NSLog(#"path: %#",plistPath);
in the bottom method shows null, why?
I use ARC. Also "destPath" is set and shows the correct path.
I believe you could be a bit confused here.
You are creating plistPath in the bottom method. And then you pass plistPath into
[BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:plistPath];
but plistPath is NULL
NSMutableString * plistPath; // Is NULL
But once it has been passed in the local plistPath takes over.
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath // Notice the local plistPath variable. This is the one you are playing with in this method.
At this point you are setting plistPath but remember it is still just a local variable and not an instance variable. So the button method will never know about it being set, as far as the button method is concerned it is still NULL
plistPath = [NSMutableString stringWithString:[destPath copy]];
So whatever you set in plistPath in the top method will not get passed back to the bottom method, think of the top plistPath as being deallocated when the method does the return.
So the plistPath in the bottom method will remain NULL
So try this instead SOLUTION
static NSMutableString *yourNewStringforPlistPath; //This will be NULL
+(NSMutableDictionary *)loadPlist: (NSString*) name
andErrorDesc: (NSString*) errorDesc
andFormat: (NSPropertyListFormat*) format
andplistPath: (NSMutableString*) plistPath
{
NSString * destPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
destPath = [destPath stringByAppendingPathComponent:
[NSString stringWithFormat:#"%#.plist", name]];
if (![[NSFileManager defaultManager] fileExistsAtPath:destPath])
{
[[NSFileManager defaultManager] copyItemAtPath:[[NSBundle mainBundle] pathForResource:name ofType:#"plist"] toPath:destPath error:nil];
}
yourNewStringforPlistPath = [NSMutableString stringWithString:[destPath copy]];
NSData * plistXML =
[[NSFileManager defaultManager] contentsAtPath:yourNewStringforPlistPath];
NSLog(#"AFTER plistPath: \n%#",yourNewStringforPlistPath);
return
(NSMutableDictionary *)[NSPropertyListSerialization
propertyListFromData:plistXML
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:format
errorDescription:&errorDesc];
}
+(bool)writeToCache:(NSString*) data andField: (NSString*) field
{
NSString * errorDesc = nil;
NSPropertyListFormat format;
NSMutableDictionary * temp = [BPUtils loadPlist:#"cache" andErrorDesc:errorDesc andFormat:&format andplistPath:[NSNull null]]; // As this is already NULL you don't really need to pass yourNewStringforPlistPath in unless in the future this value can be set before this.
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
return false;
}
NSMutableArray * arr = [temp objectForKey:field];
[arr addObject:data];
NSLog(#"path: %#",yourNewStringforPlistPath);
// Write to plist
bool res = [temp writeToFile:yourNewStringforPlistPath atomically:YES];
NSLog(#"RES: %d", res);
return true;
}
I have looked for an answer of a long time and still not found one so I thought I'd ask the question myself.
In my iPad app, I need to have the capability of parsing a .csv file in order to populate a table. I am using http://michael.stapelberg.de/cCSVParse to parse the csv files. However, I have only been successful in parsing local files. I have been trying to access a file from a server but am getting nowhere.
Here is my code to parse a local .csv file:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex == 1)
{
//UITextField *reply = [alertView textFieldAtIndex:buttonIndex];
NSString *fileName = input.text;
NSLog(#"fileName %#", fileName);
CSVParser *parser = [CSVParser new];
if ([fileName length] != 0)
{
NSString *pathAsString = [[NSBundle mainBundle]pathForResource:fileName ofType:#"csv"];
NSLog(#"%#", pathAsString);
if (pathAsString != nil)
{
[parser openFile:pathAsString];
NSMutableArray *csvContent = [parser parseFile];
NSLog(#"%#", csvContent);
[parser closeFile];
NSMutableArray *heading = [csvContent objectAtIndex:0];
[csvContent removeObjectAtIndex:0];
NSLog(#"%#", heading);
AppDelegate *ap = [AppDelegate sharedAppDelegate];
NSManagedObjectContext *context = [ap managedObjectContext];
NSString *currentHeader = [heading objectAtIndex:0];
NSString *currentValueInfo = [heading objectAtIndex:1];
NSManagedObject *newObject = [NSEntityDescription insertNewObjectForEntityForName:#"Field" inManagedObjectContext:context];
[newObject setValue:#"MIS" forKey:#"header"];
[newObject setValue:currentHeader forKey:#"fieldName"];
for (NSArray *current in csvContent)
{
NSManagedObject *newField = [NSEntityDescription insertNewObjectForEntityForName:#"Field" inManagedObjectContext:context];
[newField setValue:currentHeader forKey:#"header"];
[newField setValue:currentValueInfo forKey:#"valueInfo"];
NSLog(#"%#", [current objectAtIndex:0]);
[newField setValue:[current objectAtIndex:0] forKey:#"fieldName"];
[newField setValue:[NSNumber numberWithDouble:[[current objectAtIndex:1] doubleValue]] forKey:#"value"];
}
NSError *error;
if (![context save:&error])
{
NSLog(#"Couldn't save: %#", [error localizedDescription]);
}
[self storeArray];
[self.tableView reloadData];
}
}
}
input.text = nil;
}
Forgive the weird beginning and ending brace indentation. :/
Anyway, so that is my code to take input from a user and access a file locally which I'm sure you guys have realized already. Now I want to know how to get the path of a file in my server.
Also if you guys see anything else wrong such as writing style and other bad habits please tell me as I'm new to iOS.
Thank you so much in advance! If you didn't understand my question please clarify as I'm bad at explaining myself at times! :)
As I am guessing you are trying to get data from a server's .csv file and want to show that data in table view list.
so I suggest you try to get that .csv file data in NSData and then work on that.
NSData *responseData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"serverUrl"]];
NSString *csvResponseString = [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
NSLog(#"responseString--->%#",csvResponseString);
Now try to use nsstring's method (componentsSeparatedByString) with coma (')
arrSepratedData = [[responseString componentsSeparatedByString:#","];
Now use this arr for UITableView data populate.
I'm having an issue properly accessing an NSDictionary built from Flickr data (the flickr.photosets.getPhotos call). Instead of just showing the content of a description tag, it reads the description tag… along with some unnecessary data and quotes.
For example:
NSLog (#"Item description readout: %#", itemDescriptionPre);
yields this response:
Item description readout: {
"_content" = "This is a caption from a photo drawn through Flickr";
}
I've tried to modify the NSString with this
NSString *descripTruncated = [itemDescriptionPre substringFromIndex:17];
But it didn't causes a crash at runtime. It also doesn't address the items at the end of the item. I apologize since NSString modifications seem to be talked about a lot here, but I couldn't find circumstances that mirror mine.
Here is some more context to my code:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Storing incoming data");
NSDictionary *results = [jsonString JSONValue];
NSLog(#"Building NSDictionary.");
NSArray *photos = [[results objectForKey:#"photoset"] objectForKey:#"photo"];
NSLog(#"Building array from dictionary.");
// Loop through each entry in the dictionary...
for (NSDictionary *photo in photos)
{
NSString *title = [photo objectForKey:#"title"];
NSString *description = [photo objectForKey:#"description"];
[photoTitles addObject:title];
[photoDescriptions addObject:description];
}
NSLog(#"Nicer display for results: %# First image title: %# First image description: %#", results, [photoTitles objectAtIndex:0], [photoDescriptions objectAtIndex:0]);
[self updateDisplay];
}
-(void) updateDisplay{
NSString *capTitle = [[photoTitles objectAtIndex:0] uppercaseString];
photoTitleDisplay.text = capTitle;
NSString *itemDescriptionPre = [photoDescriptions objectAtIndex:0];
NSLog (#"Item description readout: %#", itemDescriptionPre);
}
itemDescriptionPre is actually an NSDictionary. This should work:
NSDictionary *itemDescriptionPre = [photoDescriptions objectAtIndex:0];
NSString *itemDescription = [itemDescriptionPre objectForKey:#"_content"];
I created a method to build URLs for me.
- (NSString *)urlFor:(NSString *)path arguments:(NSDictionary *)args
{
NSString *format = #"http://api.example.com/%#?version=2.0.1";
NSMutableString *url = [NSMutableString stringWithFormat:format, path];
if ([args isKindOfClass:[NSDictionary class]]) {
for (NSString *key in args) {
[url appendString:[NSString stringWithFormat:#"&%#=%#", key, [args objectForKey:key]]];
}
}
return url;
}
When I try to build something like below, the URLs aren't encoded, of course.
NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:
#"http://other.com", #"url",
#"ABCDEF", #"apiKey", nil];
NSLog(#"%#", [self urlFor:#"articles" arguments:args]);`
The returned value is http://api.example.com/articles?version=2.0.1&url=http://other.com&apiKey=ABCDEF when it should be http://api.example.com/articles?version=2.0.1&url=http%3A%2F%2Fother.com&apiKey=ABCDEF.
I need to encode both key and value. I searched for something and found CFURLCreateStringByAddingPercentEscapes and stringByAddingPercentEscapesUsingEncoding but none of the tests I made worked.
How can I do it?
IIRC, slashes should be interpreted properly when they're in the query part of a URL. Did you test to see if it still works without encoded slashses? Otherwise, do something like:
if ([args isKindOfClass:[NSDictionary class]]) {
for (NSString *key in [args allKeys]) {
NSString *value = [(NSString*)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)[args objectForKey:key], NULL, CFSTR("/?&:=#"), kCFStringEncodingUTF8) autorelease];
[url appendString:[NSString stringWithFormat:#"&%#=%#", key, value]];
[value release];
}
}
return url;
Note the value of the 4th argument to CFURLCreateStringByAddingPercentEscapes.
You should consider using Google Toolbox for Mac's GTMNSString+URLArguments; it's designed for exactly this purpose.
I'd recommend our KSFileUtilities set of classes. Your example would then be:
- (NSString *)urlFor:(NSString *)path arguments:(NSDictionary *)args
{
NSMutableDictionary *parameters = [NSMutableDictionary dictionaryWithDictionary:args];
[parameters setObject:#"2.0.1" forKey:#"version"];
NSURL *result = [NSURL ks_URLWithScheme:#"http"
host:#"api.example.com"
path:path
queryParameters:parameters;
return [result absoluteString];
}