2

I'm having trouble porting this Objective-C code to Swift. The code must sort the contents of a directory by a given property.

NSArray *contents = [fileManager contentsOfDirectoryAtURL:directoryURL includingPropertiesForKeys:@[property] options:kNilOptions error:&error];
if (!contents) return nil;
contents = [contents sortedArrayUsingComparator:^NSComparisonResult(NSURL *url1, NSURL *url2) {
    id value1;
    if ([url1 getResourceValue:&value1 forKey:property error:nil]) return NSOrderedAscending;
    id value2;
    if ([url2 getResourceValue:&value2 forKey:property error:nil]) return NSOrderedDescending;
    return [value1 compare:value2];
}];
return contents

My Swift version so far:

if let contents = fileManager.contentsOfDirectoryAtURL(directoryURL, includingPropertiesForKeys: [property], options: NSDirectoryEnumerationOptions.allZeros, error: &error) as? [NSURL] {

    let sortedContents = contents.sorted({(URL1 : NSURL, URL2 : NSURL) -> Bool in

        var value1 : AnyObject?
        if !URL1.getResourceValue(&value1, forKey: property, error: nil) { return true }
        var value2 : AnyObject?
        if !URL2.getResourceValue(&value2, forKey: property, error: nil) { return false }
        // How do I compare AnyObject?
        return false
        })
    return sortedContents
} else {
    return nil
}

Particularly, I don't know how to compare two AnyObject objects. I can't downcast to the Comparable protocol because it's not marked as @objc and apparently I can't call compare with optional chaining syntax (error: Operand of postfix '?' should have optional type; type is 'NSComparisonResult').

Of course, there's always the brute-force approach of downcasting the values to String, NSDate and NSNumber:

if let string1 = value1 as? String {
    if let string2 = value2 as? String {
        return string1 < string2
    }
}
if let date1 = value1 as? NSDate {
    if let date2 = value2 as? NSDate {
        return date1.compare(date2) == NSComparisonResult.OrderedAscending
    }
}

// And so on...

return false

Is there a better way?

hpique
  • 119,096
  • 131
  • 338
  • 476

2 Answers2

3

Unless they really are AnyObject (ie., they can be swift things) declare them (or cast them) to NSObject instead.

David Berry
  • 40,941
  • 12
  • 84
  • 95
1

The problem is not with the receiver type of AnyObject (that is fine; you can call any known objc method with AnyObject); but with the parameter type. There is no single signature of compare:. There's one that takes NSString; one that takes NSNumber; one that takes NSDate, etc., but none that is general, none that takes AnyObject.

newacct
  • 119,665
  • 29
  • 163
  • 224
  • But if `AnyObject` is like `id` (as per the Swift language guide), shouldn't I be able to call `compare` on an `AnyObject` object anyway? – hpique Aug 26 '14 at 23:43
  • @hpique: `AnyObject` is like `id` in that you can call any method on it. It's not like `id` in that you can pass `id` to a parameter expecting any object pointer type, but you cannot do that with `AnyObject`. – newacct Aug 27 '14 at 02:18