Does NSNumber automatically cast strings? - ios

When creating an SKStoreProductViewController, I pass a dictionary with a parameter for the store identifier. :
#{ SKStoreProductParameterITunesItemIdentifier : #010101010 };
This value is supposed to be an NSNumber (as it is above):
The value associated with this key is an instance of NSNumber, representing the iTunes identifier for the item you want the store to display when the view controller is presented.
But it works without complaint when I pass the value as a string:
#{ SKStoreProductParameterITunesItemIdentifier : #"010101010" };
What's going on here? Is NSNumber automatically creating the correct number type from the string that it's given? Is this occurring in the NSNumber or is StoreKit doing this?

Actually, thinking about it...
Initially I thought they must be converting the NSString into an NSNumber before doing whatever they need to do to get the information you are looking for.
However, on second thought...
I would guess that StoreKit is using the value against SKStoreProductParameterITunesItemIdentifier in a string. In which case they would do something like...
NSString *someStringToGetTheResults = [NSString stringWithFormat:#"thisIsThePath...?storeKitID=%#", dictionary[SKStoreProductParameterITunesItemIdentifier]];
This will be the same whether you pass in #12345 or #"12345".
Possibly...
No real way to tell though.

The docs say that the value stored in the SKStoreProductParameterITunesItemIdentifier key is supposed to be an NSNumber. Saving anything else in that key may work today, but may also stop working after any OS release, so don't do it.
As others have suggested, it's pretty likely that the store kit is fetching the value of the SKStoreProductParameterITunesItemIdentifier key, assuming it's an NSNumber, and sending it an integerValue method to get it's numeric value. You got lucky since NSNumber and NSString both have an integerValue method.

Related

What is the content of character attributes like NSFontAttributeName?

I was reading the apple documentation to learn how to format text for iOS. I came across an attributed string and most of it made sense to me. However, while looking up the different kinds of attributes, I saw that they are all declared as NSString.
For example:
NSString *const NSFontAttributeName;
NSString *const NSParagraphStyleAttributeName;
...
...
We pass in these string objects in a dictionary with the value being that particular attribute (e.g., a UIFont object). However, what I do not understand is what the content of that string has to do with the attribute itself. Do they just contain the name of the attribute? (i.e NSFontAttributeName might contain a string like #"NSFontAttribute")
Surely there must be a reason why has apple to chosen to do it this way?
Edit: My question isn't about why they use a string object as a key to the dictionary but why they use a predefined constant string object named NSFontAttributeName instead of allowing us to manually pass in a string #"NSFontAttribute" as the key. That's why I wondered whether the contents of their predefined string object has anything to do with this.
In this case, Apple uses an NSDictionary to contain an arbitrary set of key/value pairs. Doing it this way means you have a lot of flexibility because you can have no attributes, one attribute, or two dozen attributes with the same programming interface. And if in two years time there are attributes available that you haven't even thought about, Apple doesn't have to introduce any new APIs to support you setting these attributes.
The reason for using a constant instead of a string literal is that the compiler can save you if you misspell a name. If you wrote #"NSFontattribute" instead of #"NSFontAttribute", the compiler wouldn't know that you got it wrong.
As the attributes are expressed in an NSDictionary and you can only hold Objective-C objects in Objective-C collection classes, you have to use an object of some sort. They could have used enumerated integer values, wrapped in NSNumber objects, instead:
typedef enum {
KEYONE,
KEYTWO
} KeyValues;
NSDictionary *attributes = [NSDictionary dictionary];
attributes[#(KEYONE)] = #"The attribute";
One advantage of using an NSString as a key is that it's easier to debug at the slight expense of generating the dictionary key hash from the string (which must be very slightly more expensive than generating it from an NSNumber object).

NSNumber to BOOL

I am using Parse.com as a backend server, and am uploading data. I have a PFObject called person which has a column called 'Email' of Boolean type. I have a Core Data object with a BOOL property called email, which is set by: [NSNumber numberWithBool:[self.email isOn]];. When I try to set the PFObject as follows:
Person[#"email"] = [person.email boolValue];,
it gives me an error saying I am assigning a bool to id (Person is a PFObject). Am I doing something wrong?
What is the type of a PFObject's email property?
The fact that you can send it a boolValue message makes me think it might be an NSNumber. (Either that or NSString)
If it's already an NSNumber, you don't have to do anything. Simply assign the person.email directly to your destination key/value pair:
aPerson[#"email"] = person.email;
If person.email is a string, then you could use:
aPerson[#"email"] = #([person.email boolValue]);
Which would convert person.email to a bool, and then create an NSNumber using that bool.
BTW, you should not name variables, properties, or methods starting with an upper case letter. Objective-C has the strong convention that only classnames should start with an upper case letter.
I also cringe at having 2 different variables that only differ based on case ("Person" and "person"). That's a typing mistake away from a future bug.
According to the iOS Documentation, boolValue returns a primitive BOOL. id is not primitive, and can only be assigned objects such as NSNumber.
When using Parse, I normally send NSNumbers up to the server, as you cannot send BOOLs. Try writing this instead.
Person[#"email"] = [NSNumber numberWithBool:self.email];
This will send the NSNumber value up to the server, and when you need to retrieve it in BOOL form, just use NSNumber's boolValue

Valueforkey in NSUserDefaults

I am new to IOS programming and am trying to modify some code a developer wrote for me. I'm having problems in the following code
NSUserDefaults *pref=[NSUserDefaults standardUserDefaults];
NSString *strUrl=[pref valueForKey:#"HistoryUrl"];
if (strUrl.length>0)
{
newUrl=strUrl;
}
else
{
newUrl=#"http://www.google.com";
}
The HistoryUrl parameter seems to have the value 'http://www.yahoo.com' stored in it. I've looked everywhere and searched the net on how to replace this value to google's address. I have even gone through all the code in XCode and can't find where historyurl is declared:
Where is HistoryUrl declared?
How can it be modified?
Thanks in advance!
#"HistoryUrl" is an NSString* containing the string HistoryURL. Thats' how you write an NSString* with fixed data.
pref is an object representing the user's preferences.
The user's preference contain multiple key - value pairs. For example there might be a key named "HistoryUrl" which might have some value.
The valueForKey: method reads the value that is stored under the key "HistoryUrl" and stores it into strUrl. If there is no key named "HistoryUrl" then the result will be nil. (The use of valueForKey: is strange, because it is not a method of NSUserDefaults itself; typically one would use objectForKey:)
The following code checks whether the value read has any characters in it (length is roughly speaking the number of characters); if there are any characters then newUrl is set to that value; if there were no characters then newUrl is set to the NSString* "http:/www.google.com".
So someone at some time has stored a value under the name "HistoryUrl" into the application's preference file. You remove that value by calling
[pref removeObjectForKey:#"HistoryUrl"]
Or, since you don't seem to want anything other than "google", remove all the code and just write NSString* newUrl = #"http://www.google.com" if that's what you want.
HistoryURL is an arbitrary key and it is being used in your code to retrieve a value from NSUserDefaults. At some stage in your code, you will want to use setObject:forKey: to update the value stored in NSUesrDefaults. You will also need to call synchronize to save the new value after it has been set.
Where is HistoryUrl declared?
It's not. #"HistoryUrl" is just a string. Read up on NSUserDefaults to learn how it all works, but in a nutshell the defaults system is like an associative array (also known as a dictionary or map), where you can key/value pairs. In this case, #"HistoryUrl" is the key, and #"http://www.yahoo.com" is the value. You can make up whatever keys you want for storing your values.
How can it be modified?
Do you want to modify the key, or the value associated with the key? If the former, just make up a different key and use it. If the latter, use the methods of NSUserDefaults to set a different value:
NSUserDefaults *pref=[NSUserDefaults standardUserDefaults];
[pref setObject:#"http://google.com" forKey:#"HistoryUrl"];
[pref synchronize];
Note: the -synchronize call isn't strictly necessary, as the system will generally write your change eventually. But a lot of people like to call it whenever they make a change to the defaults system.

CoreData entity with Boolean attribute being saved as an NSNumber object

Wondering if anyone else had come across this, or if there's a reason and I'm doing something wrong.
I have an app with CoreData. In the schema I have a 'content' entity with an 'unlocked' attribute which is set to Boolean.
However when I save out the Obj C class for the entity though Xcode, unlocked appears within content.h as:
#property (nonatomic, retain) NSNumber * unlocked;
If I change it to Boolean in content.h, I get an ARC compiling error. However if I leave it as an NSNumber object when I try and fetch it, it's coming back inconsistently (as in if I have an NSLog printing it, it comes back as a different value each time I run).
I can figure out a fairly obvious work-around, setting unlocked as an NSString to 'yes' or 'no' and compare that at the relevant point, but I wanted to know if anyone knew why this was happening or if there is way to keep it as a Boolean.
Thanks in advance.
CoreData stores objects, which BOOL is not.
[NSNumber numberWithBool:YES]
Is the way to set the attribute and you can use it by reading mybool = [content.unlocked boolValue];
BOOLs are stored like NSNumbers in Core Data (if you look at your sqlite tables, you'll see they're stored as integers. So, you convert the BOOL to NSNumber before storing and convert the NSNumber to BOOL (or just us it as-is 0/1) when retrieving.
I've not seen any inconsistency, however -- if the stored NSNumber is zero, it is equivalent to NO and if nonzero, it is YES.

NSDictionary with NSObjects as keys

I'm trying to create an NSDictionary that keeps track of calling objects for a function. I'd like to create a unique string for each object without knowing anything about it. My first thought is to use the memory address of the object's pointer, but I'm not sure how to do that.
Any thoughts? I need to use some sort of unique id from an NSObject as the keys in my dictionary.
If your application supports iOS6 only check the NSDictionaryOfVariableBindings macro.
The code would be something like :
// Create the dictionary
NSObject *firstObject = [NSString stringWithString:#"My first item"];
NSObject *secondObject = #"[#"an", #"array", #"of", #"strings"]";
NSDictionary *theDic = NSDictionaryOfVariableBindings(firstObject, secondObject);
// Access data
NSString *singleString = [theDic objectForKey:#"firstObject"];
NSArray *listOfStrings = [theDic objectForKey:#"secondObject"];
My suggestion is not to use a dictionary. If you were to place them into an array, you could think of it as a dictionary with automatically generated unique keys (the indexes). It's really exactly what you are describing. If for some reason you have to use a dictionary, my suggestion is to implement that same model I'm speaking of, but you would have to generate and maintain the keys.
While I agree that your solution sounds like it may not be the best approach, have you considered -hash in the NSObject protocol? All NSObjects should return one. Be forewarned that it's a hash, so there's a chance that two different objects could have the same hash.
You could also consider a category on NSObject that your collection implements. The category could generate a UUID to use as a key.

Resources