obtuse error message when trying to add a tag to a file in the finder.

Hi, caught in a loop of unhelpful error messages. Been trying to find a code example to follow, apparently that is not a thing anymore. the docs... for over 20 years now are only half finished. So Im here in the forums looking for guidance.

I am trying to set a resource for an existing file URL. the intention is to add a tag to the file. Nothing onerous. Should be easy. Getting the list of tags WAS easy.

here's the code as I have it:

func setItemTags(atURL:URL, tags: [String]){
        do {
            try atURL.setResourceValue(tags, forKey: .tagNamesKey)
        } catch let error as NSError {
            print(error)
        }
    }

I get an error that says: Argument type '[String]' expected to be an instance of a class or class-constrained type

ok. seems pretty straight forward, the Array (which is what I get from acquiring the resources... so this doesn't make much sense at all, but whatever.) needs to be a class. clearly it's a Holdover from Obj-c. And it needs to be an NSArray. I SHOULD be able to bridge to an NSArray by replacing 'tags' with 'NSArray(array: tags)'

but that doesn't work at all. I get a different error. 'setResourceValue(:forKey:)' is unavailable: Use struct URLResourceValues and URL.setResourceValues(:) instead

So no error message is really telling us anything. no matter how much I try, I can't just materialize the answer out of thin air.

what I really need is a single example of how to properly use the 'steresourceValue' function of URL, to set the .tagNamesKey. I am astonished that this is not documented anywhere.*

*I have found ancient examples of this, which is now deprecated by changes to swift, and is only useful as a source of confusion.

Accepted Reply

Try something like this:

try (atURL as NSURL).setResourceValue(tags, forKey: .tagNamesKey)

Might be a defect in the Swift overlay bridging of URL to NSURL.setResourceValue(_:forKey:)

  • we have a winner! (URL as NSURL) is ll it took. mikeyh you figured it out.

    to sum up:

    try (atURL as NSURL).setResourceValue(tags as AnyObject, forKey: .tagNamesKey)

    works.

    try atURL.setResourceValue(tags as AnyObject, forKey: .tagNamesKey)

    does not.

Add a Comment

Replies

Try something like this:

try (atURL as NSURL).setResourceValue(tags, forKey: .tagNamesKey)

Might be a defect in the Swift overlay bridging of URL to NSURL.setResourceValue(_:forKey:)

  • we have a winner! (URL as NSURL) is ll it took. mikeyh you figured it out.

    to sum up:

    try (atURL as NSURL).setResourceValue(tags as AnyObject, forKey: .tagNamesKey)

    works.

    try atURL.setResourceValue(tags as AnyObject, forKey: .tagNamesKey)

    does not.

Add a Comment

The modern way to set a resource value is with code like this:

var url = atURL
var rv = URLResourceValues()
rv.contentModificationDate = Date()
try url.setResourceValues(rv)

Changing that middle line to this:

rv.tagNames = tags

fails with the error Cannot assign to property: 'tagNames' is a get-only property. If you look at the docs for the tagNames property you’ll see it declared like this:

var tagNames: [String]? { get }

It really is read-only. Which is weird because the corresponding Objective-C declaration makes it clear that the property is read/write:

FOUNDATION_EXPORT NSURLResourceKey const NSURLTagNamesKey 
API_AVAILABLE(macos(10.9)) API_UNAVAILABLE(ios, watchos, tvos);	
// The array of Tag names (Read-write, value type NSArray of NSString)

This is clearly a bug in the Swift overlay and I encourage you to file it as such. Please post your bug number, just for the record.

In the meantime, doing this via NSURL, as suggested by mikeyh, is a perfectly reasonable workaround.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Oh, I see from that other thread there there’s already a bug on file about this (FB8910801). I just took a quick look and there’s no sign of this being fixed )-:

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • yes. exactly.

    you are very efficient today ;)

    bug report filed. But I guess the modern way to do it is broken, and has been for a WHILE.

  • FB9968452

Add a Comment

If the read only URLResourceValues.tagNames is not intentional is this two defects?

  • get-only URLResourceValues.tagNames
  • URL bridging to NSURL.setResourceValue(_:forKey:) error Argument type '[String]' expected to be an instance of a class or class-constrained type
Add a Comment

is this two defects?

No, just the one.

If we fixed the first then the NSURL stuff would be irrelevant and thus the fact that you have to jump through some Swift to Objective-C interoperability hoops wouldn’t matter.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"