nsnumber into uipasteboard - objective-c

I'd like to copy a float into the pasteboard, but the important thing is the value, as I want to paste it later in numbers, as a number.
Tried with :
[pasteboard setValue:SomeNSNumberWhereIStoredTheFloat forPasteboardType:#"NSNumber"];
With that, it got nothing to paste, and with pasteboard.string = numberInStringValue, it pastes the number as a series of characters, in what I'm not interested.
Thanks for your help

The "type" of pasteboard data is not the name of a class, it's a Uniform Type Identifier (UTI, or just UT if you remember what else UTI stands for.) In this case, your data does not have an associated UTI (numbers are abstract concepts, not data formats.) You'll have to figure out the best way to store that number and retrieve it.
I think in this case, formatting the number into a string will suffice:
NSString *numString = [NSString stringWithFormat:#"%f", theFloatValue];
pasteboard.string = numString;
And later, when getting it back:
float theFloatValue2 = [pasteboard.string doubleValue];
This does not take into account checking for nil or other error handling.
If you need very high precision, you may need to investigate an NSData-based storage technique.

You can store an NSNumber directly. You can used the following methods from the API
setValue:forPasteboardType:
Use this method to put an object on the pasteboard that is a standard property-list object that is an object of the NSString, NSArray, NSDictionary, NSDate, NSNumber, or NSURL class.
valueForPasteboardType:
This method attempts to return an object that is of a class type appropriate to the representation type, which typically is a UTI. For example, if the representation type is kUTTypePlainText (public.plain-text), the method returns an NSString object. If the method cannot determine the class type from the representation type, it returns the object as a generic property-list object. Property-list objects include NSString, NSArray, NSDictionary, NSDate, or NSNumber objects, with NSURL objects also as a possibility. If the method cannot decode the value as a property-list object, it returns the pasteboard item as an NSData object.
The real problem comes in finding the correct UTI so the class will automatically give you back an NSNumber and not give you back an NSData object instead.
To make matters worse, the code doest not appear to work as the advertised by the documentation. I've heard from several people the method will always return you NSData. You can find an example (and a workaround) of such issue in this answer.

You can store your float value as NSNumber.
But NSNumber is not stored in UIPasteboard correctly although docs states it does (bug?).
To keep NSNumber in UIPasteboard you should archive NSNumber to NSData, and to retrieve NSNumber from UIPasteboard you should unarchive NSData back to NSNumber.
// adding data to pasteboard
NSNumber *number = [NSNumber numberWithDouble:floatValue]; // store your value here
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:number]; // archive NSNumber to NSData
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:data, #"yourKey",nil];
[[UIPasteboard generalPasteboard] addItems:[NSArray arrayWithObject:dict]];
// retrieving data
NSData *data = [dict valueForKey:#"yourKey"]; // here dict is properly obtained NSDictionary of pasteboard object
NSNumber *number = [NSKeyedUnarchiver unarchiveObjectWithData:data]; // unarchive NSData to NSNumber

Related

how to detect pasteboard item type

I am trying to identify between three types of objects:
if it is a URL of a file
If it is a URL of a directory
if it is a simple string
up till now, I have just this code, which does not work!
NSArray * classes = nil;
classes = [[NSArray alloc] initWithObjects:[NSURL class],
[NSAttributedString class],[NSString class], nil];
NSDictionary *options = [NSDictionary dictionary];
NSArray * copiedItems = nil;
copiedItems = [pb readObjectsForClasses:classes options:options];
Now I try to take the first object of the array copiedItems and try to call "types" property and i get a crash!
Check here and here:
You would need to use these pasteboard types, instead of the ones you're using.
NSString *NSStringPboardType;
NSString *NSFilenamesPboardType;
NSString *NSPostScriptPboardType;
NSString *NSTIFFPboardType;
NSString *NSRTFPboardType;
NSString *NSTabularTextPboardType;
NSString *NSFontPboardType;
NSString *NSRulerPboardType;
NSString *NSFileContentsPboardType;
NSString *NSColorPboardType;
NSString *NSRTFDPboardType;
NSString *NSHTMLPboardType;
NSString *NSPICTPboardType;
NSString *NSURLPboardType;
NSString *NSPDFPboardType;
NSString *NSVCardPboardType;
NSString *NSFilesPromisePboardType;
NSString *NSMultipleTextSelectionPboardType;
There's an pasteboard type for URLs. To distinguish between a file and a folder, you would need to instantiate an NSURL object with the pasteboard data, and find out if it is a directory by querying its attributes.
EDIT:
You also need to consider if the pasteboard data is being put there by your own application or other applications. If it's being put by other applications, I'm not sure the pasteboard types with the classes will work.
I use something like this in one of my projects:
supportedTypes = // array with supported types, maybe from the list
NSString *type = [pasteboard availableTypeFromArray:supportedTypes];
NSData *data = [pasteboard dataForType:type];
types is a method on NSPasteboard used to tell you what is available from the pasteboard. So, you shouldn't call it on the items you get back from the pasteboard.
If you're going to request multiple class types, iterate over the response and check the class type of each item, then decide how to interact with it.
Alternatively, decide which class type of data is most useful and make individual class type requests to the pasteboard. If you get a result back, use it and carry on, if not, try the next most useful class type. Look at using canReadObjectForClasses:options: to make this easier.

EXC_BAD_ACCESS while filling in the dictionary (?)

void CountlyRecordEventSegmentationCountSum(const char * key, const char * segmentation, int count, double sum)
{
NSString * seg = CreateNSString(segmentation);
NSArray * entries = [seg componentsSeparatedByString:#"`"];
NSDictionary * dict = [NSDictionary dictionary];
for (id entry in entries)
{
NSArray * keyValue = [entry componentsSeparatedByString:#"|"];
[dict setValue:[keyValue objectAtIndex:1] forKey:[keyValue objectAtIndex:0]];
}
[[Countly sharedInstance] recordEvent:CreateNSString(key) segmentation:dict count:count sum:sum];
}
I put "?" in the title because I'm not entirely sure if the problem is in the code above but that's my best guess. I'm integrating Countly iOS plugin with Unity and one of Countly plugin's methods take NSDictionary * as argument. As I don't know how to send a dictionary from C# to Objective-C I'm storing my dict in a string, sending it to Objective-C and then recreating the dictionary (the code above).
But that's probably even not relevant. I know EXC_BAD_ACCESS usually has something to do with unfreed resources or sth so maybe you can see what I'm doing wrong (I don't know Objective-C at all, just writing a few lines needed by the plugin).
Edit:
From Unity sample:
// Converts C style string to NSString
NSString * CreateNSString (const char * string)
{
if (string)
return [NSString stringWithUTF8String: string];
else
return [NSString stringWithUTF8String: ""];
}
The error you've made is that you are trying to modify immutable version of NSDictionary.
One cannot modify contents of the NSDictionary after it's initialization. You should use NSMutableDictionary instead.
Here is a documentation on NSMutableDictionary.
And here is an example of how to create mutable version of an immutable object that conforms to NSMutableCopying protocol.
You need to be using NSMutableDictionary, you can't modify an NSDictionary.
Also, you should use setObject:forKey: because setValue:forKey: is a KVC method. It happens to do the same thing on an NSMutableDictionary for most keys, but it is marginally slower.
Finally, you should check that [keyValue count] >= 2 before trying to access the objects at indexes 0 and 1.
Edit Also, CreateNSString() looks suspicious. It might be either leaking or prematurely releasing the string. But you need to post the code. In any case, I'd use
seg = [NSString stringWithUTF8String: segment];
or, other appropriate method if segment is not encoded in UTF-8.

Why set types in Obj-c fast enumeration loops?

NSMutableArray *array = [[NSMutableArray alloc] init];
NSString *string = #"string";
[array addObject:string];
NSDate *date = [[NSDate alloc] init];
[array addObject:date];
for (*placeholder* stuff in array)
NSLog(#"one");
If I change placeholder to either NSString* or NSDate*, I expect to see "one", because the for loop should just ignore a non-matching type. However, the result is "one one".
Doesn't this imply that you should just have placeholder be id whatever the situation, since it doesn't seem to matter anyhow?
fast enumeration always iterates over all object in a collection. it does not filter.
The only thing that happens is, that you will have some strange casts.
if your array contains objects of differnt classes, you can determine the class for each object with isMemberOfClass:
if you would do for (NSDate *obj in array), any object in the array will be casts to NSDate, no matter if that is sense-full or not. and due to the nature of objective-c it will even work, as-long as you dont send a message that is only understandable by NSDate objects or send the object as an argument to a method that needs to receive a date object, as a cast does not change the object in anyway. A cast is just a promise you make to the compiler that you know what you are doing. Actually you also can call it a lie.
To answer your question title itself: You dont have to set the class inside the loop statement. the generic object type id is sufficient. But usually you have objects of one kind in an array — views, numbers, string, dates,…. by declaring the right class you gain some comfort like better autocompletion.
Yes, using id (or some other common ancestor class) is the correct approach, and then it's necessary to determine which type of class has been enumerated in order to handle it differently:
for (id obj in array)
{
if ([obj isMemberOfClass:[NSString class]])
{
NSString *str = (NSString *)obj;
NSLog("obj is a string: %#", str);
}
else if ([obj isMemberOfClass:[NSDate class]])
{
NSDate *date = (NSDate *)obj;
NSLog("obj is a date: %#", date);
}
}
The problem has nothing to do with fast enumeration, but with collections which can contain any type of object. The same question arises when you access an individual element of an array:
id lastObject = [array lastObject];
or
NSString *string = [array lastObject];
Which will you chose? It all depends on your code. If you're sure that array only contains strings, then in my opinion it is better to use the second choice, because you get additional type checking, autocompletion, and method matching from the compiler (i.e. you won't get warnings if you call a method that has different signatures for two different objects). The same applies to fast enumeration: if your collection can contain any kind of object, use id. If you know what it contains, use the specific type. (And the same also applies to block tests. In NSArray's method
- (NSUInteger)indexOfObjectPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate
if you know it only contains strings for instance, you can replace id with NSString * in the block arguments. It won't change at all the compiled code or the behavior of your application, it will only change the compiler type checking.

How to store/retrieve NSDictionary into NSUserDefaults?

in my app I have no problem withs saving/retrieving string values(like myTextField.text) into NSUserDefaults but whatever i do to store NSDictionary i coldn't succeed. And i tried a lot of answer from this site+ Google. Can anyone please help? Here is my code too:
id result=[NSJSONSerialization JSONObjectWithData:data options:
NSJSONReadingMutableContainers error:&error];
NSLog(#"Result: %#", result);
NSDictionary *dict= [result objectForKey:#"message"];
//i want to store dict sub dictionary
*EDIT*Note:The dictionary contains a kind of array of objects, and the objects are all members of property list(NSString, NSDate, ...)
What the other answers are trying to tell you is, you have to get rid of your (id). NSUserDefaults and the compiler will complain and throw warnings if you try and put an id into the user defaults. Try it out this way.
NSDictionary *result = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (result) {
[[NSUserDefaults standardUserDefaults] setObject:result forKey:#"yourUniqueKey"];
}
This is because some of the objects in your NSDictionary are not Objective C primitive objects, or to put it a better way, iOS doesn't know how to convert the custom data in the NSDictionary to a form that can be saved and loaded again.
So you need to learn how to get those custom objects to conform to NSCoding.
Here is a related question that should have some useful information for you.
It depends on what you are storing. Apple's documentation says
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData. For more details, see Preferences and Settings Programming Guide.
I assume you are trying to store a type other than the listed ones.

Cant store int or double in dictionary

When trying to store a double or an int in a dictionary I get an error
error: Semantic Issue: Sending 'double' to parameter of incompatible type 'id'
I have the following code
[data setValue:longitude forKey:#"longitude"];
longitude is a double.
How should I store this? should I just create a pointer to an int or a double?
As the other posts have stated, you need to use an NSNumber to wrap your double value in an object. The reason for this is that all the Cocoa foundation collection classes are designed to work with objects rather than primitive values. As you suggested, with some work you could in fact pass a pointer to a double value (I think, if you managed to cast it as an id type pointer), but as soon as your method finished and the double went out of scope it would be released and your pointer would now be pointing to garbage. With an object, the collection (NSDictionary, in this case) will retain your object when it's added and release it when it's removed or the collection is dealloc'ed, ensuring your value will survive until you don't need it anymore.
I would do it as follows:
NSNumber *tempNumber = [[NSNumber alloc] initWithDouble:longitude];
[data setValue:tempNumber forKey:#"longitude"];
[tempNumber release];
Which will leave your NSNumber object with only a +1 reference count (the dictionary retaining it) and no autoreleases
The other suggested method of doing:
[data setValue:[NSNumber numberWithDouble: longitude] forKey:#"longitude"];
will also work fine but your object will end up with +1 reference count an an autorelease from the numberWithDouble method. When possible I try to avoid autoreleases, but the code is more concise. YMMV.
Try using an NSNumber:
[data setValue:[NSNumber numberWithDouble: longitude] forKey:#"longitude"];
A dictionary wants an NSObject, not a number or a pointer. However it's easy to create an object containing a number:
NSNumber *mylongitudeObject = [NSNumber numberWithDouble:myLongitude];
which you can thence store in an NSDictionary.
Under the hood: mylongitudeObject will actually be a pointer to an opaque structure containing a copy of your number; but the structure also contains information so that the Objective C runtime knows what it can do with this object, such as how to copy it into a dictionary, etc.
You must use an NSNumber object instead. Try declaring longitude as follows
NSNumber longitude = [NSNumber numberWithDouble:myLongitude]
With the newer version of the compiler, you can use the Objective-C literal syntax to create a NSNumber from a variable of type double:
double longitude = 2.0;
dict[#"longitude"] = #(longitude);