A simple way to store an NSURL into a property list?

I know that a NSURL cannot be stored into a property list (this is also true for NSColor, for example) as plist can only store. what fits to a property list: string, numbers and so on.

I’d like to use an NSPathControl, because it’s rather simple for a lambda user to drag a certain file and drop it on the control. More simple than enter “/Documents/myFolder/myFile” into a text field.

The point is: my plist can store such a path (as string). Why. why for the gods of Apple couldn’t this damn control store its value as a string! Don’t tell me that Preference files never use file paths.

Ok, so I’ll have to store this path as NSString or NSData. What is the simplest way to do this, please? If you answer “There is no simple way to do it” I’ll use my text field.

Thanks,

If you do not use bindings and the plist file is a preference file, NSUserDefaults has methods (10.6. and higher) to read/save NSURL instances by converting the URL to string path

Otherwise using bindings NSValueTransformer is the easiest way to make NSPathControl string path compliant

There’s two ways. First one is rather simple, just save the NSString version of the NSURL with the path method. Works well, unless you want your app thru sandboxing, or you wish the URL to be resolved by the OS and relinked correctly, say for example the user changed the folder or file’s location between launches.

Then you need to encode the URL and decode it. I only have Obj-C versions of this, so you’ll have to translate them to ASOC if this is what you need:

- (void)registerApplicationDefaults
{
    NSArray *docpathresults = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docpath = [docpathresults objectAtIndex:0];
    NSData *docpathdata =  [self bookmarkFromURL:[NSURL fileURLWithPath:[docpathresults objectAtIndex:0] isDirectory:YES]];
    [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
                                                             docpathdata, @"pathOfDestinationFolder", nil]];
}
- (void)readUserDefaults
{
    NSURL* urlFromDefaults = [self urlFromBookmark:[[NSUserDefaults standardUserDefaults] dataForKey:@"prefLastUsedDestinationPath"]];
}
- (void)writeUserDefaults
{
    [[NSUserDefaults standardUserDefaults] setObject:[self bookmarkFromURL:destinationPathControl.URL] forKey:@"prefLastUsedDestinationPath"];
    [[NSUserDefaults standardUserDefaults] synchronize];
}
- (NSData *)bookmarkFromURL:(NSURL *)url 
{
    NSData *bookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationMinimalBookmark
                     includingResourceValuesForKeys:NULL
                                      relativeToURL:NULL
                                              error:NULL];
    return bookmark;
}
- (NSURL *)urlFromBookmark:(NSData *)bookmark 
{
    NSURL *url = [NSURL URLByResolvingBookmarkData:bookmark
                                           options:NSURLBookmarkResolutionWithoutUI
                                     relativeToURL:NULL
                               bookmarkDataIsStale:NO
                                             error:NULL];
    return url;
}

You need these last two methods to encode and decode the NSData into a URL. Found that somewhere on the internets… :slight_smile:

Is this what you are looking for?

Model: MacBookPro8,2
Browser: Safari 534.51.22
Operating System: Mac OS X (10.7)

Users, by definition, always do what programmers don’t think about. :confused:

OK, this don’t sound simple. Stefan, so you have a code example of such a NSValueTranformer? If I understand what the docs say, it would be a subclass of NSValueTranformer. So far, even in ASOC, I never created a new class.

NSValueTranformer is for the case I use bindings. You know me, if I can bind something I never hesitate.

Is it possible to make the Transformer make NSURL <–> NSString? I suppose it must be visible to KVO. It’s persian of me.

Of course


@implementation PathTransformer

+ (Class)transformedValueClass
{
    return [NSURL class];
}

+ (BOOL)allowsReverseTransformation
{
    return YES;
}

- (id)transformedValue:(id)value
{
	return (value) ? [NSURL fileURLWithPath:value] : nil;
}

- (id)reverseTransformedValue:(id)value
{
	return [value path];
}

@end


Ok, I created a .h file and made the PathTransformer a subclass of NSValueTransformer. I put "#import “BFPathTransformer.h” into my app delegate file (necessary? I suppose.). It appeared in IB, so I put it in the appropriate field.

I drag and dropped a file on the NSPathControl, relaunched the application and the control had the correct value.

I looked into the plist file and the path was stored as string.

I could almost cry. For me it’s a kind of magic.

For me, who love bindings, NSTransformers could really open new worlds. «He was not quite sure what to do next. But he would think of something».

Once more, thank you, Stefan, and everyone of you!

You might find this useful:

www.cocoalab.com/?q=node/26

From the site above:

. I didn’t and it worked.?

I think that might have changed. I notice they show up in the IB menu now, too, which never used to happen.

I first had the idea to put a blue cube for the transformer in IB, but I finally removed it and everything worked fine. I don’t like when things are a bit too magic. :confused:

At least that’s the reason why the transformer gets initialized (aside from the new self-initializing feature)