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.
Related
I'm having a little difficulty with a JSON service that I'm consuming and iterating over. When I consume the service I am looping over the data as you would expect because of the number of records.
I'm saving that loop'ed data into an NSArray which I use later in a UITableView. Next I'm simply allowing the user to tap the selected row (from the json data result) to show more detail. Pretty simple so far.
Every element from the JSON service is NSString. So far nothing tricky. However, one element within the NSArray after the service has been put into the NSObject is showing HEX code, see below.
altitude NSString * 0x7ff8d4cd3d30 0x00007ff8d4cd3d30
Of course the app has a meltdown because it can't figure out what HEX is when I'm using that NSArray object to display key elements i.e. altitude. Now the odd thing is every other element within the NSArray looks like this see below.
latitude __NSCFString * #"21.45852" 0x00007ff8d4ca54f0
I have read a few suggestions stating this is normal for NSString and JSON data. But not really how to fix it.
What I have found is that NSArray after the JSON is complete is changing just that one element. I have also tried changing it from an INT to an NSString however same result (I know its a NSString in the first place btw, I was just trying different ideas.)
Abstract of JSON Call and loop to add into NSArray object.
//Do something with returned array
dispatch_async(dispatch_get_main_queue(), ^{
NSDictionary *pilotJson = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
//Loop through the JSON array
NSArray *currentPilotsArray = [pilotJson valueForKeyPath:#""];
//set up array and json call
pilotsArray = [[NSMutableArray alloc]init];
NSArray *keys=[pilotJson allKeys];
for (NSString *key in keys){
NSDictionary *elementDictionary=pilotJson[key];
NSString *altitude = elementDictionary[#"altitude"];
NSInteger n = [altitude intValue];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterDecimalStyle;
NSString *string = [formatter stringFromNumber:#(n)];
NSString *nAltitude = [NSString stringWithFormat:#"%# ft", string];
[pilotsArray addObject:[[LiveMap alloc]initWithaltitude:nAltitude ]];
.
.
.
So when I get to this point of the code where the user taps the relevant record I get a crash and the application aborts. I'm assuming this is from the above NSString vs __NSCFString
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//Pass the details the the detail view controller
PilotsFlightDetailViewController *detail = [self.storyboard instantiateViewControllerWithIdentifier:#"FlightDetails"];
NSLog(#"array: %#", pilotsArray);
NSString *AltitudeString = [[self.pilotsArray valueForKey:#"altitude"]objectAtIndex:indexPath.row]; <-----WIGS OUT HERE
I find this super odd as every other element works normally, but this one simply has issues. Any suggestions?
UPDATE:
NSLog of pilotsArray as per the request.
[1] LiveMap * 0x7f8b0265e1b0 0x00007f8b0265e1b0
altitude __NSCFString * #"21 ft" 0x00007f8b02664860
Also the jsonArray from the Service Directly.
Okay. I got it working. There was nothing wrong with the code. It however recognised another NSObject that had a same name "altitude" and for some reason it was getting mixed up.
I changed the name in the NSObject to something entirely unique and updated the instances in the relevant places. This did it. Lesson learnt always make sure you have named your variables appropriately.
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
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path stringByAppendingPathComponent:#"HighScore.plist"];
NSMutableArray* plistDict = [[NSMutableArray alloc]
initWithContentsOfFile:finalPath];
[plistDict addObject:[highScoreLabel text]];
NSArray *regArray = [NSMutableArray arrayWithArray:plistDict];
[regArray writeToFile:#"HighScore.plist" atomically: YES];
Does the file exist and contains valid data?
See Apple's documentation about NSMutableArray initWithContentsOfFile::
Return Value
An array initialized to contain the contents of the file specified by aPath or nil if the file can’t be opened or the contents of the file can’t be parsed into an array. The returned object might be different than the original receiver.
Discussion
The array representation in the file identified by aPath must contain only property list objects (NSString, NSData, NSArray, or NSDictionary objects). The objects contained by this array are immutable, even if the array is mutable.
If initWithContentsOfFile: returns nil, it's clear that the rest of the code won't work either.
p list write to file means to write the p list or to save the data ,,,for retrieving view write the content of file .
If I have a NSString that came back from a web service in the form of a plist structure, how can I initialize a NSMutableArray with this NSString. I want to know if there is a similar way to initWithContentsOfFile for NSString.
My first thought was to save the NSString to a file and then use initWithContentsOfFile; but I am trying to avoid save to file first. It seems like there should be a simpler way.
Untested, should work like this:
NSData *data = [myString dataUsingEncoding:NSUTF8StringEncoding];
NSMuableArray *array = [NSPropertyListSerialization
propertyListWithData:data
options:NSPropertyListMutableContainers
format:NSPropertyListXMLFormat_v1_0
error:NULL];
See the Property List Programming Guide "Reading and Writing Property-List Data". It covers how to turn NSData into a property list. If you already have NSData from the network, just don't convert it to NSString. If you only have an NSString, use dataUsingEncoding: to convert it to NSData.
Check the documentation for the -propertyList and -mutableCopy methods.
You could use NSXMLParser to parse the XML (which is what a plist is) and turn it into a dictionary (and retrieve the array from there)
For the sake of simplicity, let's assume that we have the following simple class interface:
#interface Person : NSObject <NSCoding> {
NSString *firstname;
NSString *lastname;
}
#property (copy) NSString *firstname;
#property (copy) NSString *lastname;
#end
Is it possible to serialize this object into a plist (assuming that the NSCoding protocol is implemented correctly)?
Update December, 31st 2010 14:40
I have some follow up questions on this. Is it possible to have NSKeyedArchiver export the plist as XML? Furthermore, is it possible to dump the XML into variable instead of a file?
As for setting the output format to XML, I suggest:
[myKeyedArchiver setOutputFormat: NSPropertyListXMLFormat_v1_0];
As for getting it into a variable and not a file, I would suggest creating your NSKeyedArchiver with the initForWritingWithMutableData: initializer. Once finished encoding,
make sure to call finishEncoding on it, and then the XML will be in the NSMutableData that you passed in at init time.
If you then need to get an NSString from that, you can get that from your NSMutableData like so:
NSString* xmlString = [[NSString alloc] initWithData: myMutableData encoding: NSUTF8StringEncoding];
That should do the trick. Good luck!
Yes, it is. However, its not really convenient as you have to encode the object first and then save the NSData object into the array/dictionary which you want to save into the plist.
NSKeyedArchiver will automatically save the object to a plist when you give it a filename.plist file path.
This appears to be a defect in iOS. I am wondering if it is specific to the handling of the user defaults plist though.
As a possible workaround, perhaps you could try managing an NSMutableDictionary for your settings and read/write the dictionary as a plist file directly.
To write the data:
NSString* error;
NSData* pListData = [NSPropertyListSerialization dataFromPropertyList:settingsDictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
[data writeToFile:settingsFilePath atomically:YES];
Then to read the data back:
NSString* error;
NSData* pListData = [NSData dataWithContentsOfFile:settingsFilePath];
settingsDictionary = [NSPropertyListSerialization propertyListFromData:pListData mutabilityOption:NSPropertyListImmutable format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
Try this magic:
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:person];
plist[#"personData"] = data;
To get it back out just use:
Person* p = [NSKeyedUnarchiver unarchiveObjectWithData:plist[#"personData"]];