cocoa format NSURL string for gmail atom feed - cocoa

I'm trying to format a url string to retrieve a gmail atom feed but I'm having problems with it. Here's my code:
NSString *urlstring = [NSString stringWithFormat:#"https://%#:%##gmail.google.com/­gmail/­feed/atom", username, userpass];
NSString *encodedString = [urlstring stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:encodedString];
Here's what's in my log.
https://••••••••#gmail.com:•••••••••#gmail.google.com/%C2%ADgmail/%C2%ADfeed/atom
This-->%C2%AD seems to be the problem. It should just be a slash. Any idea how to clean that up? Thanks.

Short answer:
Your urlstring contains soft hyphens.
Comprehensive answer:
In the following code withSoftHyphens and withoutSoftHyphens look equal:
NSString *withSoftHyphens = #"example/­example/­example";
NSString *withoutSoftHyphens = #"example/example/example";
NSLog(#"%#",[withSoftHyphens stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]);
NSLog(#"%#",[withoutSoftHyphens stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]);
The output is however different:
(checkout yourself by copying and executing the code above)
"example/%C2%ADexample/%C2%ADexample"
"example/example/example"
The soft hyphens are basically represented by %C2%AD after encoding the string.
Quote from Wikipedia:
Soft hyphen is a type of hyphen used to specify a place in text where
a hyphenated break is allowed without forcing a line break in an
inconvenient place if the text is re-flowed.
In other words, your urlstring contains soft hyphens.
Simply remove /­g and /­f using the backspace key and type them again.
Notice you actually need THREE backspaces to only remove two characters (/­g).
- The first backspace removes the g.
- The second backspace removes the invisible soft hyphen.
- The third backspace removes the /.
In conclusion, your code works just fine after removing the soft hyphens:
NSString *username = #"Anne";
NSString *userpass = #"Password";
NSString *urlstring = [NSString stringWithFormat:#"https://%#:%##gmail.google.com/mail/feed/atom", username, userpass];
NSString *encodedString = [urlstring stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:encodedString];
NSLog(#"%#", url);
Output:
https://Anne:Password#gmail.google.com/mail/feed/atom

Related

NSAttributedString initWithHTML incorrect character encoding?

-[NSMutableAttributedString initWithHTML:documentAttributes:] seems to mangle special characters:
NSString *html = #"“Hello” World"; // notice the smart quotes
NSData *htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];
NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithHTML:htmlData documentAttributes:nil];
NSLog(#"%#", as);
That prints “Hello†World followed by some RTF commands. In my application, I convert the attributed string to RTF and display it in an NSTextView, but the characters are corrupted there, too.
According to the documentation, the default encoding is UTF-8, but I tried being explicit and the result is the same:
NSDictionary *attributes = #{NSCharacterEncodingDocumentAttribute: [NSNumber numberWithInt:NSUTF8StringEncoding]};
NSMutableAttributedString *as = [[NSMutableAttributedString alloc] initWithHTML:htmlData documentAttributes:&attributes];
Use [html dataUsingEncoding:NSUnicodeStringEncoding] when creating the NSData and set the matching encoding option when you parse the HTML into an attributed string:
The documentation for NSCharacterEncodingDocumentAttribute is slightly confusing:
NSNumber, containing an int specifying the NSStringEncoding for the
file; for reading and writing plain text files and writing HTML;
default for plain text is the default encoding; default for HTML is
UTF-8.
So, you code should be:
NSString *html = #"“Hello” World";
NSData *htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *options = #{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
NSCharacterEncodingDocumentAttribute: #(NSUTF8StringEncoding)};
NSMutableAttributedString *as =
[[NSMutableAttributedString alloc] initWithHTML:htmlData
options: options
documentAttributes:nil];
The previous answer here works, but mostly by accident.
Making an NSData with NSUnicodeStringEncoding will tend to work, because that constant is an alias for NSUTF16StringEncoding, and UTF-16 is pretty easy for the system to identify. Easier than UTF-8, which apparently was being identified as some other superset of ASCII (it looks like NSWindowsCP1252StringEncoding in your case, probably because it's one of the few ASCII-based encodings with mappings for 0x8_ and 0x9_).
That answer is mistaken in quoting the documentation for NSCharacterEncodingDocumentAttribute, because "attributes" are what you get out of -initWithHTML. That's why it's NSDictionary ** and not just NSDictionary *. You can pass in a pointer to an NSDictionary *, and you'll get out keys like TopMargin/BottomMargin/LeftMargin/RightMargin, PaperSize, DocumentType, UTI, etc. Any values you try to pass in through the "attributes" dictionary are ignored.
You need to use "options" for passing values in, and the relevant option key is NSTextEncodingNameDocumentOption, which has no documented default value. It's passing the bytes to WebKit for parsing, so if you don't specify an encoding, presumably you're getting WebKit's encoding-guessing heuristics.
To guarantee the encoding types match between your NSData and NSAttributedString, what you should do is something like:
NSString *html = #"“Hello” World";
NSData *htmlData = [html dataUsingEncoding:NSUTF8StringEncoding];
NSMutableAttributedString *as =
[[NSMutableAttributedString alloc] initWithHTML:htmlData
options:#{NSTextEncodingNameDocumentOption: #"UTF-8"}
documentAttributes:nil];
Swift version of accepted answer is:
let htmlString: String = "Hello world contains html</br>"
let data: Data = Data(htmlString.utf8)
let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [
.documentType: NSAttributedString.DocumentType.html,
.characterEncoding: String.Encoding.utf8.rawValue
]
let attributedString = try? NSAttributedString(data: data,
options: options,
documentAttributes: nil)

NSString encoding from strings with chars like \u00f6 to UTF8

I get a NSStrings with characters like \u00f6. I can't find how to encode it to UTF8.
NSString *resultString = [NSString stringWithContentsOfURL:wikiSearchURL usedEncoding:NSUTF8StringEncoding error:&err];
Thanks...
I think you want to do this:
NSString *resultString = [NSString stringWithContentsOfURL:wikiSearchURL encoding:NSUTF8StringEncoding error:&err];
the usedEncoding: one will tell you what encoding it used when it parsed the URL, while the encoding: one will force it to use a particular encoding.
NSString conceptually uses UTF-16 as its inernal format. 0x00F6 is a perfectly valid character to find in an NSString. It's o-umlaut. If you want to convert the string to UTF-8, use -UTF8String
const char* foo = [myString UTF8String];
Note that your line of code which gets a string from a URL and tries to figure out which encoding was used is wrong. You should use something like:
NSStringEncoding theEncoding;
NSString *resultString = [NSString stringWithContentsOfURL: wikiSearchURL usedEncoding: &theEncoding error:&err];
Assuming the returned string is not nil, theEncoding will now contain the encoding that was used to convert the URL content to the string.

NSString isEqualToString: does not work. Why?

I create an NSString using,
NSString *myString = [[NSString alloc] initWithBytes:someBuffer length:sizeof(someBuffer) encoding:NSASCIIStringEncoding];
I used NSLog to output myString and it displays "Hello".
If this is the case, then why does this fail.
NSString *helloString = #"Hello"
BOOL check = [myString isEqualToString:helloString];
Your myString variable is actually an NSString with a length of 64; the additional characters are probably undefined. What you most likely want to do is this:
NSString *myString = [[NSString alloc] initWithBytes:someBuffer length:strlen(someBuffer) encoding:NSASCIIStringEncoding];
This assumes a null-terminated C-string exists in your buffer.
There are probably some trailing characters that you can't see when calling NSLog(). For example: whitespace, linefeeds or even '\0' characters.
Check [myString length] to see if it returns 5.

NSString's isEqualToString: seems to erroneously report non-equality

I'm trying to compare the equality of two multi-line strings. I'm getting one of the strings from a web service, and the other I'm getting from iTunes via the Scripting Bridge. The strings from the web service are eventually transferred to iTunes, so if I do that and then re-compare the strings, ideally they'd be identical.
However, when comparing strings like this, it seems that isEqualToString: always returns non-equality. I'm testing this by testing equality of a string from iTunes that originally came from the web service, and a string directly from the web service.
Logging both strings to the Console produces output from both strings that appears identical. Logging the lengths of the strings produce identical lengths.
I've also tried comparing the strings using some other methods. For example, I converted them to ASCII strings to make sure it wasn't some Unicode issue:
NSData *iTunesStringData = [[self iTunesString] dataUsingEncoding:NSASCIIStringEncoding
allowLossyConversion:YES];
NSData *webServiceStringData = [[self webServiceString] dataUsingEncoding:NSASCIIStringEncoding
allowLossyConversion:YES];
NSString *newiTunesString = [[[NSString alloc] initWithData:iTunesStringData encoding:NSASCIIStringEncoding] autorelease];
NSString *newWebServiceString = [[[NSString alloc] initWithData:webServiceStringData encoding:NSASCIIStringEncoding] autorelease];
BOOL result = [newiTunesString isEqualToString:newWebServiceString];
Same problem, not equal. I've tried comparing just the first character:
NSComparisonResult result = [newiTunesString compare:newWebServiceString
options:NSLiteralSearch
range:NSMakeRange(0,1)
locale:[NSLocale currentLocale]];
Does not return NSOrderedSame. I've logged these first characters to the Console and they seem identical. I also considered differences in carriage returns, and tried replacing #"\r" with #"" in both strings before comparing, which doesn't work (and besides, that shouldn't affect equality of just the first character). I don't want to remove #"\n" characters because I want to preserve the multiple lines.
What's going on? Any other ideas?
It turns out this problem was related to line endings. But since I'm comparing multi-line strings, I didn't want to completely strip out the newlines. I normalized the line endings like so:
NSString *normalizediTunesString = [[[self iTunesString] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#"\n"];
NSString *normalizedWebServiceString = [[[self webServiceString] componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:#"\n"];
Then, comparing the strings via compare: worked as expected.
Just guessing here but maybe use this clean up your strings
stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]

Declare String then open string from Path with Cocoa

I've Declared a string Like so
NSString* fileName = [files objectAtIndex:i];
NSLog(fileName);
NSImage* imageFromBundle = [[NSImage alloc] initWithContentsOfFile:fileName];
and want to use that filename to open a file in a different directory.
I came up with this
NSImage* imageFromBundle2;
imageFromBundle2 = [[NSImage alloc] initWithContentsOfFile:#"/Users/rhaynes/Documents/works4/" filename ];
Any help would be appreciated
I'll assume that your fileName string is actually a file name, like "myImage.png". A lot of the Objective-C docs refer to a file name when they really mean file path - so sometimes it's confusing.
What you want to do is create an NSString that represents the complete path to the file you want to load. For instance, you could say:
NSString * path = [NSString stringWithFormat: #"/Users/rhaynes/Documents/works4/%#", fileName];
That line creates a new NSString using the format string and parameters provided (the %# in the format string indicates that the string value of fileName should be inserted there.) StringWithFormat is a really powerful function, so you should definitely check it out in the docs.
Then you could call initWithContentsOfFile:path, and it should give you the image you want.
NSString* fileName = [files objectAtIndex:i]; NSLog(fileName);
Don't pass non-hard-coded strings as format-string arguments. If they contain format specifiers, you'll get garbage or a crash. (Try this with fileName = #"foo%sbar", for example. Then try it with fileName = #"foo%fbar" for even more fun.)
Your NSLog statement should be:
NSLog(#"%#", fileName);
[I] want to use that filename to open a file in a different directory. I came up with this
NSImage* imageFromBundle2; imageFromBundle2 = [[NSImage alloc] initWithContentsOfFile:#"/Users/rhaynes/Documents/works4/" filename ];
You can only concatenate string literals this way; as you've no doubt seen for yourself, this is a syntax error when one of the strings isn't a literal.
First off, if fileName is actually a pathname, you'll need to use lastPathComponent to get the actual filename. So:
NSString *path = [files objectAtIndex:i];
NSString *filename = [path lastPathComponent];
Then, use stringByAppendingPathComponent: to tack this onto the new superpath.
NSString *desiredFilenamePath = [directoryPath stringByAppendingPathComponent:filename];
Now you have the pathname you wanted to pass to NSImage's initializer.

Resources