Getting an NSString from an NSCFString - ios

I read around S.O and did Google this a fair bit but I wasn't able to find an answer to my problem...
I have an NSDictionary which is returning NSCFString's and what I need is an NSString so that I can use its doubleValue function. I can't for the life of me figure out how to turn my NSCFString's into NSStrings. I've tried the following with no success:
NSString* lng_coord = (NSString*)[dict objectForKey:key];
if ([lng_coord class] == [NSString class])
NSLog(#"lng coord is an exact match with NSString class");
else {
NSLog(#"lng coord doesn't match NSString class");
NSLog(#"The class of lng_coord is %#", [[dict objectForKey:key] class]);
}
NSLog(#"%#", lng_coord);
[CelebsAnnotation setLongitude:[lng_coord doubleValue]];
And this is the output from console:
lng coord doesn't match NSString class
The class of lng_coord is NSCFString
49.2796758

NSCFString is a private subclass of NSString, you can just use it as one.
Because of such patterns like class clusters, you shouldn't directly compare the classes here - use -isKindOfClass: instead:
if ([lng_coord isKindOfClass:[NSString class]]) {
// ...

Related

Parsing id to NSString

When parsing API responses, sometimes I can not rely on strings being embedded in quotation marks. ID's are a good example of this, where some API's will send the numerical ID as a string while some will send it as a number.
What is a good practice when parsing such a value? If I simply parse it to an NSString like so:
NSString *myID = (NSString *)message["myID"];
I can end up with an NSString object that somehow contains (long)123.
And using stringValue would cause issues when the value is actually already sent as a string (since NSString does not have a stringValue function).
A way that works, but is somewhat ugly, is this:
id myID = (NSString *)message["myID"];
if ([myID respondsToSelector:#selector(stringValue)])
{
myID = [myID stringValue];
}
You could do something like:
id myID = message["myID"];
if ([myID isKindOfClass:[NSString class]]) { ... }
else { ... }
As long as this logic is encapsulated inside data parser and is opaque for your api users (i.e. they will always get a string) any approach is fine, e.g.:
- (NSString*)parseID:(NSDictionary*)message {
id rawID = message["myID"];
if ([rawID isKindOfClass:[NSString class]]){
return rawID;
} else if ([rawID isKindOfClass:[NSNumber class]]) {
return [(NSNumber*)rawID stringValue];
} else {
// We might still want to handle this case.
NSAssert(false, #"Unexpected id type");
return nil;
}
}
Alternative is to define stringValue in extension, so any possible objet will respond to selector:
#implementation NSString(JSONStringParsing)
- (NSString *)stringValue {
return [self copy];
}
#end
Why not just use description?
NSArray *objects = #[
#NSIntegerMin,
#NSIntegerMax,
#"123456789"
];
for (id object in objects) {
NSString *stringObject = [object description];
NSLog(#"%# -> %# | %#", [object className], [stringObject className], stringObject);
}

How to query an NSDictionary using an expression written in NSString?

I want to be able to run the following hypothetical function called evaluateExpression:on: and get "John" as answer.
NSDictionary *dict = #{"result": #[#{#"name": #"John"}, #{#"name": #"Mary"}]};
NSString *expression = #"response['result'][0]['name']";
NSString *answer = [self evaluateExpression: expression on: dict];
Is this possible?
There's an NSObject category that extends valueForKeyPath to give valueForKeyPathWithIndexes. It lets you write this:
NSDictionary *dict = #{#"result": #[#{#"name": #"John"}, #{#"name": #"Mary"}]};
NSString *path = #"result[0].name";
NSString *answer = [dict valueForKeyPathWithIndexes:path];
XCTAssertEqualStrings(answer, #"John");
The category is by psy, here: Getting array elements with valueForKeyPath
#interface NSObject (ValueForKeyPathWithIndexes)
-(id)valueForKeyPathWithIndexes:(NSString*)fullPath;
#end
#import "NSObject+ValueForKeyPathWithIndexes.h"
#implementation NSObject (ValueForKeyPathWithIndexes)
-(id)valueForKeyPathWithIndexes:(NSString*)fullPath
{
NSRange testrange = [fullPath rangeOfString:#"["];
if (testrange.location == NSNotFound)
return [self valueForKeyPath:fullPath];
NSArray* parts = [fullPath componentsSeparatedByString:#"."];
id currentObj = self;
for (NSString* part in parts)
{
NSRange range1 = [part rangeOfString:#"["];
if (range1.location == NSNotFound)
{
currentObj = [currentObj valueForKey:part];
}
else
{
NSString* arrayKey = [part substringToIndex:range1.location];
int index = [[[part substringToIndex:part.length-1] substringFromIndex:range1.location+1] intValue];
currentObj = [[currentObj valueForKey:arrayKey] objectAtIndex:index];
}
}
return currentObj;
}
#end
Plain old valueForKeyPath will get you close, but not exactly what you asked for. It may be useful in this form though:
NSDictionary *dict = #{#"result": #[#{#"name": #"John"}, #{#"name": #"Mary"}]};
NSString *path = #"result.name";
NSString *answer = [dict valueForKeyPath:path];
XCTAssertEqualObjects(answer, (#[#"John", #"Mary"]));
After spending quite a while trying to tackle this problem in the most efficient way, here's another take I came up with: Use javascript.
The above approach posted by Ewan definitely takes care of the problem, but I had some additional custom features I wanted to add, and objective-c approach became too complex very quickly. I ended up writing eval code in javascript and integrating it into objective-c using JavascriptCore. With that, it becomes as simple as one line of eval() call.

Parsing JSON data and handling an Array

I am using Mantle to parse some JSON data from Yelp.
For each business returned I get an NSArray of categories. This would be an example:
yelpCategories = (
(
"Wine Bars",
"wine_bars"
),
(
"Ice Cream & Frozen Yogurt",
icecream
)
);
yelpCategories is the name of the array that I save. Later on I am trying to parse the array into a string:
NSMutableString *yelpCats = [[NSMutableString alloc] init];
for (NSObject * obj in business.yelpCategories)
{
[yelpCats appendString:[NSString stringWithFormat:#"%#,",[obj description]]];
}
The issue is with the above. I am being returned a string just as "(" so I must be accessing the array incorrectly. How can I correctly access each object, ideally I would be looking for the end string o be #"Wine Bars, Ice Cream & Frozen Yogurt".
EDIT
The categories array: (
(
Pubs,
pubs
)
)
FINAL EDIT - Proposed Solution
for (NSArray *cats in business.yelpCategories)
{
NSString *category = [cats objectAtIndex:0];
if ([category length] > 0) {
category = [category substringToIndex:[category length] - 1];
}
if (cats == business.yelpCategories.lastObject) {
[yelpCats appendString:[NSString stringWithFormat:#"%#",category]];
} else {
[yelpCats appendString:[NSString stringWithFormat:#"%#, ",category]];
}
}
cell.yelpCategories.text = yelpCats;
Using the description of the object gives you what you see in the debugger, which includes extra carriage returns.
What you want to do is something like:
yelpCats = [yelpCategories componentsJoinedByString:#", "];
#jeffamaphone 's answer is the correct and best way of doing things however what your doing will almost work, I think your just confused on the contents of the array.
The yelpCategories array is an array of strings so you don't need to call stringWithFormat or call the description method. In fact [obj description] will return a string so you didn't even need stringWithFormat in your example and you would have gotten the same output. To make your original method work change to:
NSMutableString *yelpCats = [[NSMutableString alloc] init];
for (id obj in business.yelpCategories)
{
//obj is a string so we can just append it.
[yelpCats appendString:obj]];
}
Also noticed I changed NSObject *obj to just id obj, this is the idiomatic way and shorthand way of declaring NSObjects in objective-c. In this example however I would actually use (NSString *category in business.yelpCategories) instead for better readability. In this case you are declaring to everyone that you expect each object in the array to be a string and then if you wanted to use NSString methods on it inside the loop then you don't have to cast it.
for (NSArray *cats in business.yelpCategories)
{
NSString *category = [cats objectAtIndex:0];
if ([category length] > 0) {
category = [category substringToIndex:[category length] - 1];
}
if (cats == business.yelpCategories.lastObject) {
[yelpCats appendString:[NSString stringWithFormat:#"%#",category]];
} else {
[yelpCats appendString:[NSString stringWithFormat:#"%#, ",category]];
}
}
cell.yelpCategories.text = yelpCats;

How to convert NSString to NSObject?

In my project i need to compare and NSString with NSObject. How to convert NSString to NSObject. OR Is there any chance to compare NSString with NSObject.
NSString class itself inherits from NSObject class, So you can directly compare them, see below code or check this link
NSString *str = #"xyz";
NSObject *obj = #"xyz";
if([str isEqualToString:(NSString *)obj]) {
NSLog(#"comparision successful");
}
If your NSObject is indeed an NSString, you can just cast it;
NSString *text = (NSString*)someObject;
Where someObject is the object you want to be a string. If you're not sure if the object is an NSString, you can check by doing the following;
if ([someObject class] == [NSString class){
NSString *text = (NSString*)someObject;
}
Hope that helps.

Split NSString and Limit the response

I have a string Hello-World-Test, I want to split this string by the first dash only.
String 1:Hello
String 2:World-Test
What is the best way to do this? What I am doing right now is use componentsSeparatedByString, get the first object in the array and set it as String 1 then perform substring using the length of String 1 as the start index.
Thanks!
I added a category on NSString to split on the first occurrence of a given string. It may not be ideal to return the results in an array, but otherwise it seems fine. It just uses the NSString method rangeOfString:, which takes an NSString(B) and returns an NSRange showing where that string(B) is located.
#interface NSString (Split)
- (NSArray *)stringsBySplittingOnString:(NSString *)splitString;
#end
#implementation NSString (Split)
- (NSArray *)stringsBySplittingOnString:(NSString *)splitString
{
NSRange range = [self rangeOfString:splitString];
if (range.location == NSNotFound) {
return nil;
} else {
NSLog(#"%li",range.location);
NSLog(#"%li",range.length);
NSString *string1 = [self substringToIndex:range.location];
NSString *string2 = [self substringFromIndex:range.location+range.length];
NSLog(#"String1 = %#",string1);
NSLog(#"String2 = %#",string2);
return #[string1, string2];
}
}
#end
Use rangeOfString to find if split string exits and then use substringWithRange to create new string on bases of NSRange.
For Example :
NSString *strMain = #"Hello-World-Test";
NSRange match = [strMain rangeOfString:#"-"];
if(match.location != NSNotFound)
{
NSString *str1 = [strMain substringWithRange: NSMakeRange (0, match.location)];
NSLog(#"%#",str1);
NSString *str2 = [strMain substringWithRange: NSMakeRange (match.location+match.length,(strMain.length-match.location)-match.length)];
NSLog(#"%#",str2);
}

Resources