I'm trying to create a vCard representation in the iPhone. I created a string representation for the vCard. I'm not sure how to convert it into the NSData form in order to mail it as an attachment. This is what I have so far:
NSString *vCardString = [vCard getVCFString]; // returns string representation for vCard
NSData *vCardData = [vCardString dataUsingEncoding:NSUTF8StringEncoding];
[mailController addAttachmentData:vCardData mimeType:#"text/x-vcard" fileName:#"LocationInfo"];
When I click on the attachment when I do a test email, it goes to the create new contact/add as existing contact. Is this correct since the iPhone recognizes it as a contact? I guess I was interested in getting the location information out of it, but that didn't seem to show up in my attachment. The code for creating my VCF representation is:
vcfString = [[NSMutableString allocWithZone:[self zone]] initWithCapacity:kDefaultStringSize];
[vcfString appendString:#"BEGIN:VCARD\n"];
[vcfString appendString:#"VERSION:3.0\n"];
if (s) {
NSMutableString *aString = [[NSMutableString alloc] initWithFormat:#"%#;", s];
[vcfString appendString:aString];
[aString replaceOccurrencesOfString:#";" withString:#"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [aString length])];
street = [[NSString allocWithZone:[self zone]] initWithString:aString];
[aString release];
}
if (c) {
NSMutableString *aString = [[NSMutableString alloc] initWithFormat:#"%#;", c];
[vcfString appendString:aString];
[aString replaceOccurrencesOfString:#";" withString:#"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [aString length])];
city = [[NSString allocWithZone:[self zone]] initWithString:aString];
[aString release];
}
if (st) {
NSMutableString *aString = [[NSMutableString alloc] initWithFormat:#"%#;", st];
[vcfString appendString:aString];
[aString replaceOccurrencesOfString:#";" withString:#"" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [aString length])];
state = [[NSString allocWithZone:[self zone]] initWithString:aString];
[aString release];
}
// TODO: look up semicolon for VCard representation
if (z) {
NSString *aString = [[NSString alloc] initWithFormat:#"%#\n", z];
[vcfString appendString:aString];
zip = [[NSString allocWithZone:[self zone]] initWithString:aString];
[aString release];
}
[vcfString appendString:#"END:VCARD"];
Totally agree with Dave DeLong's answer. Just want to add that since I simply wanted to create a vCard file (.vcf) and attach it to an in-app email action what I did was create a string, then a temporary file, with all the vCard fields I had data for.
In my case I made a method in my NSManagedObject subclass to return a -vCardRepresentation string.
An Objective-c category or simple framework would be a great help - I might do it when time.
The Wikipedia page and the formal spec for vCard (3.0) really help.
UPDATE 2: I'm rushing to get an app complete, but because I did want to create vCard data and add it as attachments to in-app mail messages in several places I've now made a separate class controller that makes / hides the details of the vCard syntax. Then I just have a method that returns an NSData version of the vCard string to add directly to a mail message as an attachment. This is a much cleaner solution, you don't need to create any files, even if they were only temporary. Plus you don't really need an NSString representation of the data anyway, unless you did want to create a file and use it more than once to save re-making the data?
My controller class has a number of -setXXXX methods that add values to a dictionary where the keys are strings with the vCard field names, like FN, TEL and ADR. Then when I call its -vCardRepresentation, this now returns an NSData object (built by scanning the dictionary) that can be used directly in MFMailComposeViewController's -addAttachmentData:mimeType:fileName: method.
If I can tidy my code, so it's generic enough, I'll post it later.
UPDATE: Here's my code, it might help someone get started:
- (NSString *)vCardRepresentation
{
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
[mutableArray addObject:#"BEGIN:VCARD"];
[mutableArray addObject:#"VERSION:3.0"];
[mutableArray addObject:[NSString stringWithFormat:#"FN:%#", self.name]];
[mutableArray addObject:[NSString stringWithFormat:#"ADR:;;%#",
[self addressWithSeparator:#";"]]];
if (self.phone != nil)
[mutableArray addObject:[NSString stringWithFormat:#"TEL:%#", self.phone]];
[mutableArray addObject:[NSString stringWithFormat:#"GEO:%g;%g",
self.latitudeValue, self.longitudeValue]];
[mutableArray addObject:[NSString stringWithFormat:#"URL:http://%#",
self.website]];
[mutableArray addObject:#"END:VCARD"];
NSString *string = [mutableArray componentsJoinedByString:#"\n"];
[mutableArray release];
return string;
}
Obviously I'm making reference to properties in my class, plus a method called -addressWithSeparator to build-up the data for the address, which has to include the ; separator even for optional address fields.
Related
i am trying to parse a text file saved in doc dir below show is the code for it
NSArray *filePaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *docDirPath=[filePaths objectAtIndex:0];
NSString *filePath=[docDirPath stringByAppendingPathComponent:#"SKU.txt"];
NSError *error;
NSString *fileContents=[NSString stringWithContentsOfFile:filePath];
NSLog(#"fileContents---%#",fileContents);
if(!fileContents)
NSLog(#"error in reading file----%#",error);
NSArray *values=[fileContents componentsSeparatedByString:#"\n"];
NSLog(#"values-----%#",values);
NSMutableArray *parsedValues=[[NSMutableArray alloc]init];
for(int i=0;i<[values count];i++){
NSString *lineStr=[values objectAtIndex:i];
NSLog(#"linestr---%#",lineStr);
NSMutableDictionary *valuesDic=[[NSMutableDictionary alloc]init];
NSArray *seperatedValues=[[NSArray alloc]init];
seperatedValues=[lineStr componentsSeparatedByString:#","];
NSLog(#"seperatedvalues---%#",seperatedValues);
[valuesDic setObject:seperatedValues forKey:[seperatedValues objectAtIndex:0]];
NSLog(#"valuesDic---%#",valuesDic);
[parsedValues addObject:valuesDic];
[seperatedValues release];
[valuesDic release];
}
NSLog(#"parsedValues----%#",parsedValues);
NSMutableDictionary *result;
result=[parsedValues objectAtIndex:1];
NSLog(#"res----%#",[result objectForKey:#"WALM-FT"]);
The problem what i am facing is when i try to print lineStr ie the data of the text file it is printing as a single string so i could not able to get the contents in line by line way please help me solve this issue.
Instead use:
- (NSArray *)componentsSeparatedByCharactersInSet:(NSCharacterSet *)separator
it covers several different newline characters.
Example:
NSArray *values = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *lineStr in values) {
// Parsing code here
}
ALso seperatedValues is over released. First one is created with alloc init, then on the next line it is replaced by the method componentsSeparatedByString. So the first one od lost without being released, that is a leak. Later the seperatedValues created by componentsSeparatedByString is released but it is already auto released by componentsSeparatedByString to that is an over release;
Solve all the retain/release/autorelease problem with ARC (Automatic Reference Counting).
Here is a version that uses convenience methods and omits over release:
NSArray *values = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *lineStr in values) {
NSArray *seperatedValues = [lineStr componentsSeparatedByString:#","];
NSString *key = [seperatedValues objectAtIndex:0];
NSDictionary *valuesDic = [NSDictionary dictionaryWithObject:seperatedValues forKey:key];
[parsedValues addObject:valuesDic];
}
NSLog(#"parsedValues---%#",parsedValues);
Are you sure the line separator used in your text file is \n and not \r (or \r\n)?
The problem may come from this, explaining why you don't manage to split the files into different lines.
I'm working on an iPad project that is using functions in a WebService.
Handling webservice connection, data etc works find. But i'm not able to parse the result SOAP using TouchXML. Getting the nodes out of the xml always returns 0 length.
The output xml:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <LogOnResponse xmlns="http://coreservices.org/v1"> <LogOnResult> <Id>1c0e9ad0-a3be-4cd0-8f0d-0616a63a4e28</Id> <userId>2</userId> <user> <ID>2 <Name>Beheerder <emailAddress /> <cultureName>nl-NL</cultureName> </user> </LogOnResult> </LogOnResponse> </soap:Body></soap:Envelope>
parser code:
NSData *aData = [[NSData alloc] initWithBytes:[webData mutableBytes] length:[webData length]];
NSString *xmlData = [[NSString alloc] initWithData:aData encoding:NSASCIIStringEncoding];
NSLog(#"%#", xmlData);
[xmlData release];
CXMLDocument *domUserIdentity = [[[CXMLDocument alloc] initWithData:aData options:0 error:nil] autorelease];
[aData release];
NSArray *nodesList = [domUserIdentity nodesForXPath:#"//LogOnResult" error:nil]; // 0 length
for (CXMLElement *resultElement in nodesList) {
for (int counter = 0; counter
NSString *elemName = [[resultElement childAtIndex:counter] name];
NSString * elemValue = [[[resultElement childAtIndex:counter] stringValue] stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]];
[elemName release];
[elemValue release];
}
}
[nodesList release];
Any idea what did i do wrong?
Thank's alot in advance.
Inoel
Try using the NSXMLDocument, it should work as well as CXMLDocument. Here are some docs:
link
I recommend using an XPath Parser. You aren't doing anything too heavy, so the speed hit is well worth it. My personal choice is TFHpple. There are slightly faster and more precise solutions out there, but I find TFHpple's simplicity hard to beat.
Example:
NSData *data = [[NSData alloc] initWithContentsOfFile:#"example.html"];
// Create parser
xpathParser = [[TFHpple alloc] initWithXMLData:data];
NSArray *elements = [xpathParser search:#"//LogOnResult"];
TFHppleElement *element = [elements objectAtIndex:0];
// Get the text within the cell tag
NSString *content = [element content];
[xpathParser release];
[data release];
Obviously you'd have to write some code to save the parts you want. Looking at this data, I'd probably make a custom data holder class that you can access with properties. You can parse your XML into that and then save it as a property in the view controller that will be accessing it.
Happy coding!
I am doing Fconnect in that when a user connects to facebook I get a string like this
{"id":"100001480456987","name":"Vishnu Gupta","first_name":"Vishnu","last_name":"Gupta","link":"http:\/\/www.facebook.com\/profile.php?id=100001480456987","education":[{"school":{"id":"110885222265513","name":"st.joseph"},"type":"High School"}],"gender":"male","email":"vishu.gupta20#gmail.com","timezone":5.5,"locale":"en_US","verified":true,"updated_time":"2010-11-27T10:10:25+0000"}
Now I want to split the id name and email id of the user so that I can store it in my database.
Can someone tell me how to do it????
You don't want to split a string to get those values. Instead, you want to parse the JSON to grab data. I've used this library, it works very well: http://stig.github.com/json-framework/
Hope this helps!
EDIT: Some sample code:
NSDictionary *dict = [responseFromFacebook JSONValue];
NSString *facebookID = [dict objectForKey:#"id"];
NSString *name = [dict objectForKey:#"name"];
NSString *email = [dict objectForKey:#"email"];
this looks like JSON. Some information on JSON handling with Objective C is available at
http://blog.zachwaugh.com/post/309924609/how-to-use-json-in-cocoaobjective-c
Use a JSON parser. See this answer for links to stackoverflow questions about the different JSON libraries available.
Of course I'd also like to mention my own JSON parsing library, JSONKit. At the time of this writing I think it's fair to say that it's the fastest JSON parser out there.
Try this
NSDictionary *dict=[[NSDictionary alloc]init];
string=[string stringByReplacingOccurrencesOfString:#"{" withString:#""];
string=[string stringByReplacingOccurrencesOfString:#"}" withString:#""];
string=[string stringByReplacingOccurrencesOfString:#"\"" withString:#""];
NSArray *seperated=[string componentsSeparatedByString:#","];
for(int index=0;index<[seperated count];index++)
{
NSArray *sub=[[seperated objectAtIndex:index] componentsSeparatedByString:#":"];
[dict setValue:[sub objectAtIndex:0] forKey:[sub objectAtIndex:1]];
}
What does mean? I get this error when trying to iterate through a file in Cocoa obj-c.
I can't find any information on the web.
Would appreciate some help. Thanks.
EDIT
I've been following this tutorial (link) to preload Core Data. I've tried creating a Cococa application and have also tried doing this from within my iPhone app. I think all my setup code for Core Data is fine. Whenever this method is called I get EXEC BAD ACCESS.
- (void)loadInitialData
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// name ZSTREET_1 ZSTREET_2 ZCITY ZZIP ZURL ZTEL latitude longitude
NSString *path = [[NSBundle mainBundle] pathForResource:#"placesdata" ofType:#"txt"];
NSString *fileString = [NSString stringWithContentsOfFile:path]; // reads file into memory as an NSString
NSArray *lines = [fileString componentsSeparatedByString:#"\r"]; // each line, adjust character for line endings
NSManagedObjectContext *context = [self managedObjectContext];
for (NSString *line in lines)
{
NSLog(line);
NSString* string = [[NSString alloc] initWithUTF8String:line];
NSArray *parts = [string componentsSeparatedByString:#"\t"];
// value mapping
NSString *name = [parts objectAtIndex:0];
NSString *street_1 = [parts objectAtIndex:1];
NSString *street_2 = [parts objectAtIndex:2];
NSString *city = [parts objectAtIndex:3];
NSString *zip = [parts objectAtIndex:4];
NSString *url = [parts objectAtIndex:5];
NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
[f setNumberStyle:NSNumberFormatterDecimalStyle];
NSNumber *latitude = [f numberFromString:[parts objectAtIndex:6]];
NSNumber *longitude = [f numberFromString:[parts objectAtIndex:7]];
[f release];
// splitting the parts to create the objects
Place *place = (Place *)[NSEntityDescription insertNewObjectForEntityForName:#"Place" inManagedObjectContext:context];
Address *address = (Address *)[NSEntityDescription insertNewObjectForEntityForName:#"Address" inManagedObjectContext:context];
Location *location = (Location *)[NSEntityDescription insertNewObjectForEntityForName:#"Location" inManagedObjectContext:context];
// set attributes
[place setValue:name forKey:#"name"];
[address setValue:street_1 forKey:#"street_1"];
[address setValue:street_2 forKey:#"street_2"];
[address setValue:city forKey:#"city"];
[address setValue:zip forKey:"#zip"];
[address setValue:url forKey:#"url"];
[location setValue:latitude forKey:#"latitude"];
[location setValue:longitude forKey:#"longitude"];
// link the objects together
[place setValue:address forKey:#"address"];
[place setValue:location forKeyPath:#"address.location"];
[string release];
}
NSLog(#"Done initial load");
NSError *error;
if (![context save:&error]) {
NSLog(#"Error saving: %#", error);
}
[context release];
[pool drain];
}
For other people running into this problem with entirely different code, this is a bit of a red herring.
The warning is from the debugger itself. The debugger creates a struct containing info for each object in the system. After the EXC_BAD_ACCESS, it tried to create one of these but was unable to. Note that this is a warning and not an error so it may even be expected in situations like this.
In any event, the details surrounding this don't matter. You've got to find the source of your EXC_BAD_ACCESS. A common cause is trying to access an object after it has been released.
For someone who comes across this in the future, I got this problem because I was doing this too much:
NSString* aString = #"";
for(int i=0; i<someLargeNumber; i++) {
aString = [aString stringByAppendingFormat:#"..."];
}
Once I switched to using NSMutableString, the problem was resolved.
This line is wrong and should produce a compiler warning:
NSString* string = [[NSString alloc] initWithUTF8String:line];
The method initWithUTF8String: expects an UTF-8 encoded C string and not a NSString object.
Before you continue you should fix all compiler warnings! And you also should check that the parts array actually contains as many objects as you expect. You also need to use a format string with NSLog, you might even crash there if your line contains any % characters.
I happen to run into EXC_BAD_ACCESS on a fairly regular basis (which is not a good thing), because we haven't enabled ARC yet.
I find the best way to track those errors down by using the Profiler in Zombie modus. This post shows you how to "hunt for zombies":
How do I set up NSZombieEnabled in Xcode 4?
I've got an RSS parser method and I need to remove whitespace and other nonsense from my extracted html summary. I've got a NSMutableString type 'currentSummary'. When I call:
currentSummary = [currentSummary
stringByReplacingOccurrencesOfString:#"\n" withString:#""];
Xcode tells me "warning: assignment from distinct Objective-C type"
What's wrong with this?
If currentSummary is already a NSMutableString you shouldn't attempt to assign a regular NSString (the result of stringByReplacingOccurrencesOfString:withString:) to it.
Instead use the mutable equivalent replaceOccurrencesOfString:withString:options:range:, or add a call to mutableCopy before the assignment:
// Either
[currentSummary replaceOccurencesOfString:#"\n"
withString:#""
options:NULL
range:NSMakeRange(0, [receiver length])];
// Or
currentSummary = [[currentSummary stringByReplacingOccurrencesOfString:#"\n"
withString:#""]
mutableCopy];
This works great for nested elements as well of course:
*Edited*
// Get the JSON feed from site
myRawJson = [[NSString alloc] initWithContentsOfURL:[NSURL
URLWithString:#"http://yoursite.com/mobile_list.json"]
encoding:NSUTF8StringEncoding error:nil];
// Make the content something we can use in fast enumeration
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSDictionary * myParsedJson = [parser objectWithString:myRawJson error:NULL];
[myRawJson release];
allLetterContents = [myParsedJson objectForKey:#"nodes"];
// Create arrays just for the title and Nid items
self.contentTitleArray = [[NSMutableArray alloc]init];
for (NSMutableDictionary * key in myArr) {
NSDictionary *node = [key objectForKey:#"node"];
NSMutableString *savedContentTitle = [node objectForKey:#"title"];
// Add each Title and Nid to specific arrays
//[self.contentTitleArray addObject:contentTitle];
//change each item with & to &
[self.contentTitleArray addObject:[[savedContentTitle
stringByReplacingOccurrencesOfString:#"&"
withString:#"&"]
mutableCopy]];
}
The code below, as shown in the use-case above might be helpful.
[self.contentTitleArray addObject:[[contentTitle
stringByReplacingOccurrencesOfString:#"&"
withString:#"&"]
mutableCopy]];
That usually means you dropped the asterisks in the definition of (in this case) currentSummary.
So you most likely have:
NSMutableString currentSummary;
when you need:
NSMutableString *currentSummary;
In the first case, since Objective-C classes are defined in type structures, the complier thinks your trying to assign a NSString to a struct.
I make this typo on a distressingly regular basis.