3

I'm trying to add an extension to PHFetchResult to pull the result objects out as a correctly typed array - and so far I can't figure out how to avoid the replication seen below. Aside from the type constraint the following extensions are identical. If I use PHObject as the constraint it would work, but the result would need to be cast to the more specific type...

extension PHFetchResult where ObjectType == PHAsset {
    var objects: [ObjectType] {
        var _objects: [ObjectType] = []
        enumerateObjects { (object, _, _) in
            _objects.append(object)
        }
        return _objects
    }
}    
extension PHFetchResult where ObjectType == PHCollection {
    var objects: [ObjectType] {
        var _objects: [ObjectType] = []
        enumerateObjects { (object, _, _) in
            _objects.append(object)
        }
        return _objects
    }
}    
extension PHFetchResult where ObjectType == PHAssetCollection {
    var objects: [ObjectType] {
        var _objects: [ObjectType] = []
        enumerateObjects { (object, _, _) in
            _objects.append(object)
        }
        return _objects
    }
}
Mike Bedar
  • 632
  • 5
  • 14

1 Answers1

5

This is a general issue with generic types defined in ObjC and imported to Swift. Some relevant compiler bugs:

In short: ObjC generics are type-erasing, Swift generics are type-preserving. You need the latter for tasks like defining type-constrained extensions, but you can't because Swift doesn't know anything about the ObjectType of any particular PHFetchResult instance.

The swift-evolution proposal that brought widespread ObjC generics import to Swift 3 included a provision for letting ObjC classes provide information about their generic type parameters at runtime... but as of today, that provision has never been implemented.

AFIAK, there's not really a solution to this problem (short of working on the Swift compiler project to improve ObjC generics support), nor similar problems like making PHFetchResult conform to Sequence or Collection.

(I've tried a few angles on extending PhotoKit classes to be more Swifty myself, without much success. About the only thing I haven't tried — largely because it's such a drastic measure — is wrapping/replacing large chunks of the API. In theory, though, you could wrap PHFetchResult in a non-generic ObjC class, then wrap that in a generic Swift type, and replace all of the PhotoKit fetch methods with ones that provide appropriately specialized versions of your wrapper type.)

rickster
  • 124,678
  • 26
  • 272
  • 326
  • Thanks for your answer - very informative - I'm at least glad in this case the ugly solution works. – Mike Bedar Mar 28 '18 at 03:21
  • Hey @rickster! I am wondering if there are _any_ docs about the "ObjectType" at all. I want to understand where it comes from and how it behaves. – Hans Knöchel Jul 27 '18 at 13:51
  • ObjectType is a generic type parameter, not a type it itself. It’s a placeholder for any type, so it doesn’t have docs like a true type. Take a look at the declaration for PHFetchResult in Xcode, and/or the section on Generics in the Swift language guide. – rickster Jul 27 '18 at 13:55