0

Here is my code :

    var galleryController: GalleryController!
    var albums: PHFetchResult = PHFetchResult()
    var cameraRoll: [PHFetchResult] = [PHFetchResult]()

    albums=getAlbums()
    cameraRoll=getcameraRoll()

func getAlbums()->PHFetchResult<AnyObject>
{
    let fetchOptions=PHFetchOptions()
    fetchOptions.predicate=NSPredicate(format:"estimatedAssetCount > 0")
    return PHAssetCollection.fetchAssetCollections(with: PHAssetCollectionType.album, subtype: PHAssetCollectionSubtype.albumSyncedAlbum, options: fetchOptions) as! PHFetchResult<AnyObject>
}    

func getcameraRoll()->[PHFetchResult<AnyObject>]
{
    self.photosFromCameraRoll=PHAsset.fetchAssets(with: PHAssetMediaType.image, options: nil)
    return [self.photosFromCameraRoll]
}

For the line var albums: PHFetchResult = PHFetchResult() Xcode is complaning with this message: "Cannot convert value of type PHFetchResult<_> to specified PHFetchResult"

For the line var cameraRoll: [PHFetchResult] = [PHFetchResult]() Xcode is also complaning with the same message: "Cannot convert value of type PHFetchResult<_> to specified PHFetchResult"

Zipette
  • 79
  • 7

1 Answers1

5

In Swift 3, PHFetchResult is a generic class. (It has been in ObjC since the iOS 9 SDK last year, and Swift 3 now imports ObjC generics.) Generic types must always be specialized. In ObjC a generic class works with or without a type parameter (that is, you can have a PHFetchResult<SomeObjectType *> * or a PHFetchResult *, and in the latter case the type parameter is assumed to be id). But in Swift you can't — you always have to declare an element type.

So, if you're declaring a variable to hold Photos fetch results, you need to declare the expected object type of those fetch results.

  • Your first one looks like it's expected to be a list of albums — perhaps to be fetched via fetchTopLevelUserCollections(with:)? So its type should be PHFetchResult<PHCollection> or PHFetchResult<PHAssetCollection> depending on exactly which fetch method you're using and what you intend to do with it next.

  • The second is an array of fetch results? That alone sounds fishy given the variable name cameraRoll. If you just want to fetch all the assets in the camera roll, just use fetchAssets(in:options:), passing the camera roll as the collection to fetch from. (You can get the camera roll with fetchAssetCollections(with:subtype:options:), using the .smartAlbum type and .smartAlbumUserLibrary subtype.)

    Whether it's a single fetch result or an array of fetch results, if you expect the fetch(es) to contain assets the type (or element type of the array) should be PHFetchResult<PHAsset>.


A second, more minor, problem with your code is that you're creating dummy, empty fetch results only for them to (presumably) be overwritten by an actual fetch later. (PHFetchResult is an immutable collection, so its initializer creates an empty collection that you can't usefully do anything with.)

You'd be better off declaring those variables with optional types (or, if you plan to initialize them early and expect all code paths after that to come after initialization, implicitly unwrapped optional types). Or, if it fits your use case, make them lazy:

class PhotosAlbumsController: UITableViewController {

    var galleryController: GalleryController!

    lazy var albums = PHCollection.fetchTopLevelUserCollections(with: nil)
    // ^- implicitly typed PHFetchResult<PHCollection>

    lazy var cameraRoll = PHAssetCollection.fetchAssetCollections(with: .smartAlbum,
        subtype: .smartAlbumUserLibrary, options: nil).firstObject!
    // ^- implicitly typed PHAssetCollection
    // safe to force unwrap because there's always a Camera Roll / Saved Photos album

    // ...
}

The one thing to watch out for with lazy initialization here (whether you do it with lazy or by directly fetching at some other time) is to make sure you have user authorization for Photos access first, and/or make sure you register as a PHPhotoLibrary observer first. If you fetch before getting authorization, your fetches will be empty. If you register as an observer first, your fetches will still be empty to start with, but as soon as the user accepts authorization you'll get a photoLibraryDidChange call that populates them.

rickster
  • 124,678
  • 26
  • 272
  • 326