Writing FileURL's name to Pasteboard - objective-c

I want to write some fileURL's to general pasteboard using [pasteboard writeObjects:pasteboardArray]; where pasteboard is an object of general pasteboard and pasteboardArray is a NSArray of NSURL's. It writes urls on pasteboard like
file://localhost/Usr/
file://localhost/Vol/
...and so on
I want to write these URL's as following
Usr
Vol
..and so on
i.e only the name of file/Directory as the system does on copying any file/Directory.

For each item on the pasteboard, there can be multiple types of data. When you write an NSURL to the pasteboard, it puts types such as public.file-url (the URL as UTF-8 text), NSFilenamesPboardType (an array of file path strings, serialized to an XML property list), "Apple URL pasteboard type" (an array of file URL strings, serialized to an XML property list), and public.utf8-plain-text (the URL as UTF-8 text).
When you copy a file in the Finder, it puts many of those same types, although it seems to convert the URLs to file reference URLs first. In addition, it puts the file's icon as an ICNS and a TIFF image. However, the public.utf8-plain-text type has just the file's name, not its URL. That string is also present with type public.utf16-plain-text.
So, presumably, the Finder is not simply using -writeObjects: to populate the pasteboard. It may be using the older approach of -declareTypes:owner: and/or -addTypes:owner: followed by -set...:forType: (e.g. -setString:forType:). Or it may be constructing instances of NSPasteboardItem with the desired types and data using its -set...:forType: methods and then writing those items to the pasteboard.
For your needs it would probably be sufficient to do:
[pasteboard writeObjects:arrayOfURLs];
NSMutableString* names = [NSMutableString string];
for (NSURL* url in arrayOfURLs)
{
NSString* name;
if ([url getResourceValue:&name forKey:NSURLLocalizedNameKey error:NULL])
{
if (names.length)
[names appendString:#"\r"];
[names appendString:name];
}
}
if (names.length)
{
[pasteboard addTypes:#[(__bridge NSString*)kUTTypeUTF8PlainText] owner:nil];
[pasteboard setString:names forType:(__bridge NSString*)kUTTypeUTF8PlainText];
}
Note: when you write multiple URLs to the pasteboard, there are multiple items, each with multiple types. However, certain types are for the collection as a whole and those only go on the first item. The plain string types and NSFilenamesPboardType are like this. The above code puts the plain string on the first item, and includes display names for all of the URLs separated by a carriage return (which is what the Finder does).
All of the above said, it's not clear if you wanted the pasteboard to contain just the display names for the URLs and not the URLs themselves in any representation. In that case, just create an array of string from the display names of the URLs and pass that array to -writeObjects: and you're done.
Update:
I've found a way to more closely match what the Finder does while taking a bit of a shortcut:
NSMutableArray* arrayOfPaths = [arrayOfURLs valueForKey:#"path"];
[pasteboard setPropertyList:arrayOfPaths forType:NSFilenamesPboardType];
NSMutableString* names = [NSMutableString string];
BOOL first = YES;
for (NSURL* url in arrayOfURLs)
{
NSString* name;
if (![url getResourceValue:&name forKey:NSURLLocalizedNameKey error:NULL])
name = #"";
if (first)
first = NO;
else
[names appendString:#"\r"];
[names appendString:name];
}
if (names.length)
{
[pasteboard addTypes:#[(__bridge NSString*)kUTTypeUTF8PlainText] owner:nil];
[pasteboard setString:names forType:(__bridge NSString*)kUTTypeUTF8PlainText];
}
When you set the data for type NSFilenamesPboardType, Cocoa automatically sets up multiple items, one for each element in the array. The second and subsequent ones each have just the single type public.file-url. The first item has that type plus most of the others that the Finder puts on the pasteboard, other than the image types. Then, it's just a matter of setting the string value on the first item.
It's important to include the NSFilenamesPboardType type because that's how multiple files were represented on the pasteboard before support for multiple pasteboard items was added. Some apps may still depend on that.
Also, if you provide the string data for each item separately (as done in your own answer), then an app which asks for the string from the pasteboard as a whole (i.e. [pasteboard stringForType:NSStringPboardType]) gets them concatenated with newlines. That's slightly different from what the app would get when files are copied from the Finder, where the strings are concatenated with carriage returns. Some apps may not cope well with the difference.

NSMutableArray *archive=[NSMutableArray array];
for (int i = 0; i < [pasteboardArray count]; i++) {
NSPasteboardItem *item = [[NSPasteboardItem alloc] init];
NSURL *url = [pasteboardArray objectAtIndex:i];
if (url != nil) {
[item setString:[url lastPathComponent] forType:(__bridge NSString*)kUTTypeUTF8PlainText];
NSString *str = [NSString stringWithFormat:#"%#",url];
[item setData:[str dataUsingEncoding:NSUTF8StringEncoding] forType:(__bridge NSString*)kUTTypeFileURL];
}
[archive addObject:item];
}
[pasteboard writeObjects:archive];
This worked for me, where pasteboard is an object of general pasteboard and pasteboardArray is a NSArray of NSURL's. Any better solutions are welcome...

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.

How do I concatenate strings together in Objective-C?

So I am trying to concatenate a bunch of input strings together as one string so I can save that to a text file.
So far I am trying to write something like this
NSString *tempString = [[NSString alloc]initWithFormat:#"%#%#%#", text1, text2, text3];
The only problem with this is that I need a total of 30 strings stored this way. I need a way to do this without typing out each string name. Is there a way to use a for loop or something to accomplish this? Type the strings like this perhaps?
text(i)
So that the variable name would change each time it went through the for loop. I've tried doing something like this and I can't get it to work. If you can help me with this method or another way that you know to do it I would be very thankful.
Okay, so all of the answers here take the wrong approach (sorry guys).
The fundamental problem is that you are using your "text boxes" as a data source, when they should simply be views. When someone changes the text, you should immediately store them in your model (which could be a simple array) and then reference that model later. (This is part of MVC. Look it up if you aren't familiar, as you should be if you are programming for iOS!)
Here is what I would do. (I'm assuming that your "text boxes" are UITextField's.)
Set the delegate for each text field to your view controller.
Set the tag for each text field to a number which represents the order that you want the strings joined in. (ie 1-30)
If you don't have a separate class for your data model, then setup a declared property in your view controller which stores a reference to a NSMutableArray which can contain all of the strings in order. Let's call it dataSource. In viewDidLoad: set this to an actual mutable array filled with empty values (or previously stored values if you are saving them). The reason that we store empty values is so that we can replace them with the user entered strings for any index, even if they are entered out of order:
- (void)viewDidLoad
{
[super viewDidLoad];
self.dataSource = [[NSMutableArray alloc] initWithCapacity:20];
for(int i = 0; i < 30; i++)
[self.dataSource addObject:#""];
}
Then, use the following text field delegate method which stores the strings into the array as they are entered:
// This is called every time that a text field finishes editing.
- (void)textFieldDidEndEditing:(UITextField *)textField
{
if (textField.tag > 0)
[self.dataSource replaceObjectAtIndex:textField.tag-1 withObject:textField.text];
}
Congratulations! All of your strings are now stored in one array. Now we just have to combine them all:
NSMutableString *theString = [self.dataSource componentsJoinedByString:#""];
Note that I have NOT tested all of this so there may be typos. This should get you pointed in the right direction though!
If you set up your text boxes in Interface Builder with an IBOutletCollection(UITextField) you would have an array of text boxes that you could access the text value using KVC and join them.
//interface
...
#property (nonatomic, strong) IBOutletCollection(UITextField) NSArray *textBoxes;
//implementation
...
NSString *tempString = [[textBoxes valueForKey:#"text"]
componentsJoinedByString:#""];
Using iOS 4's IBOutletCollection
If you programmatically create your text boxes then add them to an array as you create them.
NSMutableString's appendString: method is your friend.
NSArray *strings = [NSArray arrayWithObjects: #"Hi", #" there", #" dude", nil];
NSMutableString *result = [[NSMutableString alloc] init];
for (NSString *string in strings) {
[result appendString:string];
}
NSLog(#"result: %#", result); // result: Hi there dude

Won't write to plist array Xcode. This was the code used and it doesn't work, does anyone know why?

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 .

Reading in from a plist, but accepting only certain strings

The goal is to have an array where all strings are of length n.
So at the moment what I have my code doing is reading in a plist (which is just 250,000 strings) into an array, and then iterating over the array in order to find which ones are/aren't of length n. Of course, for the sake of efficiency, I'd prefer being able to read in from the plist STRING BY STRING so, as I'm reading in, I may the length then before inserting into the array. I'm just starting to learn objective-c, but I was struggling to Google around for a solution =P
EDIT: Well I just found out I can find much more documentation typing property list rather than plist into google :) so I may be able to figure this out myself
You can parse plist into tree (NSMutableDictionary). Dict will have keys with name of string length.
for example
NSMutableDictionary *result = [NSMutableDictionary dictionary];
for (NSString *str in [plistDict allObjects]) {
NSString *key = [NSString stringWithFormat:#"%d", [str length]];
NSMutableArray *array = [result objectForKey:key];
if (!array) {
array = [NSMutableArray array];
}
[array addObject:str];
[result setObject:array forKey:key];
}
than you can access array with needed strings length
NSArray *string4Lenght = [result objectForKey:#"4"];
Apple doesn't provide an API for incrementally parsing a plist.
If you store your plist in XML format, you could use NSXMLParser to parse it. The schema is pretty simple and somewhat described in the Property List Programming Guide.
If you want to incrementally parse the binary format, you're going to have to do more work. There's no official documentation for the format. Apple's source code for reading and writing the format is open source (CFBinaryPList.c) and there are some useful comments along with the actual code.
If you really need to do it incrementally, I suggest going the XML route. If you do, you might want to subclass NSInputStream to be able to read from a gzip or bzip2 file and decompress on the fly.

Parsing XML file using NSXMLParser

I have to parse an XML file using NSXMLParser. There are so many HTML tags in them, so when I am trying to parse it, it will store the string up to that and then again go to found character method and starts to append it.
my code is:
if (Bio_CResults) {
[BioResults appendString: string];
[Info appendString:string];
[stringarr addobject:Info];
NSLog(#"bio==%#",BioResults);
NSLog(#"string==%#",string);
}
and I want to add it in string array, but here it will make create extra object of array. i.e.
stringarr objectAtIndex 0 = abc
stringarr objectAtIndex 1 = def
stringarr objectAtIndex 2 = ghi
but actually I want all of them together in one object because they are actually one string only..
plz help me for that
If you don't even need multiple string objects you can use an NSMutableString instead of an array. Just use the appendString: method to add to the end of the string:
NSMutableString *string = [NSMutableString string];
[string appendString:#"abc"];
[string appendString:#"def"];
NSLog(#"New string is %#", string);
This will log "New string is abcdef".
If you really want an array, use an NSMutableArray instead of an NSArray. That way you can change an object in-place (replace a string with a new string created by appending another string). So for example:
// First create an array with #"abc"
NSMutableArray *mArray = [NSMutableArray arrayWithObject:#"abc"];
// Next get the first object as a string and append #"def" to it in-place
NSString *string = (NSString *)[mArray objectAtIndex:0];
[mArray replaceObjectAtIndex:0 withObject:[string stringByAppendingString:#"def"]];
// Now get the new first object
NSLog(#"First object is %#", (NSString *)[mArray objectAtIndex:0]);
This will log the message "First object is abcdef".
I really dont like NSXmlParser. You might want to read my blog post on using RegxKitLite
http://blog.bluespark.co.nz/?p=51
It might be of some help. Hopefully it wont lead you in the wrong direction.
Cheers, John.
There are so many HTML tags in them…
Be aware that HTML is usually not valid XML. If you're parsing HTML, an XML parser will throw an error some part of the way through the document.
You may be better off creating a WebView, loading the HTML content into it, and then getting a DOMDocument from the view's main frame and traversing the DOM hierarchy. You don't have to put the view into a window; you can just use it to get a DOMDocument.
If you are parsing XML and meant to say “XML tags”, you can disregard this answer.