title: MLMediaLibrary - Displaying the Photo Library -Swift Code Wrong - macOS
body: <p>Trying to show photos from the Photo Library.  The Apple Sample Code below does not work properly.  It doesn't wait for the notifications but tries to launch the collection view which crashes at Number of Items in Section because there is no photo data to show.</p>
  
  <p>Thanks for any help from you Swift guru's out there!  </p>
  
  <pre><code> /*
   Copyright (C) 2016 Apple Inc. All Rights Reserved.
   See LICENSE.txt for this sample’s licensing information
  
   Abstract:
   The `ViewController` class is a subclass to NSViewController responsible for managing the app's content.
   */
  
  import Cocoa
  import MediaLibrary
  
  class IconViewBox : NSBox {
      override func hitTest(_ aPoint: NSPoint) -&gt; NSView? {
          // Don't allow any mouse clicks for subviews in this NSBox.
          return nil
      }
  }
  
  // MARK: -
  
  class ViewController: NSViewController, NSCollectionViewDelegate {
  
      // MARK: - Types
  
      // Keys describing the dictionary for each photo loaded.
      private struct ItemKeys {
          static let imageKey = "icon"
          static let nameKey = "name"
      }
  
      // MLMediaLibrary property values for KVO.
      private struct MLMediaLibraryPropertyKeys {
          static let mediaSourcesKey = "mediaSources"
          static let rootMediaGroupKey = "rootMediaGroup"
          static let mediaObjectsKey = "mediaObjects"
          static let contentTypeKey = "contentType"
      }
  
      // MARK: - Properties
  
      /**
       The KVO contexts for `MLMediaLibrary`.
       This provides a stable address to use as the `context` parameter for KVO observation methods.
       */
      private var mediaSourcesContext   = 1
      private var rootMediaGroupContext = 2
      private var mediaObjectsContext   = 3
  
      private var photoSize = CGSize(width: 168, height: 145)
  
      // Contains an array of dictionaries describing each photo (refer to ItemKeys for key/values).
      @IBOutlet weak var arrayController: NSArrayController!
  
      @IBOutlet weak var collectionView: NSCollectionView!
      @IBOutlet private weak var noPhotosLabel: NSTextField!
      @IBOutlet private weak var activityIndicator: NSProgressIndicator!
  
      // MLMediaLibrary instances for loading the photos.
      private var mediaLibrary: MLMediaLibrary!
      private var mediaSource: MLMediaSource!
      private var rootMediaGroup: MLMediaGroup!
  
      // MARK: - View Controller Lifecycle
  
      override func viewDidLoad() {
  
          super.viewDidLoad()
          print ("VDL1...................")
          // Start progress indicator in case fetching the photos from the photo library takes time.
          self.activityIndicator.isHidden = false
          self.activityIndicator.startAnimation(self)
  
          self.collectionView.minItemSize = self.photoSize
          self.collectionView.maxItemSize = self.photoSize
  
          self.arrayController.setSelectionIndex(-1)  // No selection to start out with.
  
          // Setup the media library to load only photos, don't include other source types.
          let options: [String : AnyObject] =
              [MLMediaLoadSourceTypesKey: MLMediaSourceType.image.rawValue as AnyObject,
               MLMediaLoadIncludeSourcesKey: [MLMediaSourcePhotosIdentifier, MLMediaSourceiPhotoIdentifier] as AnyObject]
  
          // Create our media library instance to get our photo.
          mediaLibrary = MLMediaLibrary(options: options)
  
          // We want to be called when media sources come in that's available (via observeValueForKeyPath).
          self.mediaLibrary.addObserver(self,
                                        forKeyPath: MLMediaLibraryPropertyKeys.mediaSourcesKey,
                                        options: NSKeyValueObservingOptions.new,
                                        context: &amp;mediaSourcesContext)
  
          if (self.mediaLibrary.mediaSources != nil) {
  
              print ("VDL2...................")
          } // Reference returns nil but starts the asynchronous loading.
      }
  
      deinit {
  
          // Make sure to remove us as an observer before "mediaLibrary" is released.
          self.mediaLibrary.removeObserver(self, forKeyPath: MLMediaLibraryPropertyKeys.mediaSourcesKey, context:&amp;mediaSourcesContext)
      }
  
      // MARK: - NSCollectionViewDataSource
  
      func numberOfSectionsInCollectionView(_ collectionView: NSCollectionView) -&gt; Int {
  
          return 1
      }
  
      func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -&gt; Int {
  
          let photos = self.arrayController.arrangedObjects as! NSArray
          return photos.count
      }
  
      func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAtIndexPath indexPath: IndexPath) -&gt; NSCollectionViewItem {
  
          let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "IconItem"), for:indexPath)
          let photos = self.arrayController.arrangedObjects as! NSArray
          let iconInfo = photos[(indexPath as NSIndexPath).item]
          item.representedObject = iconInfo
          return item
      }
  
      // MARK: - NSCollectionViewDelegate
  
      func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set&lt;IndexPath&gt;) {
  
          if let itemIndexPath = indexPaths.first {
              let photos = self.arrayController.arrangedObjects as! NSArray
              let itemDict = photos[((itemIndexPath as NSIndexPath).item)] as! NSDictionary
              if let itemTitle = itemDict[ItemKeys.nameKey] as? String {
                  if (itemTitle.characters.count &gt; 0) {
                      print("selected photo: '\(itemTitle)'")
                  }
                  else {
                      print("selected photo: &lt;no title&gt;")
                  }
              }
          }
      }
  
      // MARK: - Utilities
  
      /// Helps to make sure the media object is the photo format we want.
      private func isValidImage(_ mediaObject: MLMediaObject) -&gt; Bool {
  
          var isValidImage = false
  
          let attrs = mediaObject.attributes
          let contentTypeStr = attrs[MLMediaLibraryPropertyKeys.contentTypeKey] as! String
  
          // We only want photos, not movies or older PICT formats (PICT image files are not supported in a sandboxed environment).
          if ((contentTypeStr != kUTTypePICT as String) &amp;&amp; (contentTypeStr != kUTTypeQuickTimeMovie as String))
          {
              isValidImage = true
          }
  
          return isValidImage
      }
  
      /// Obtains the title of the MLMediaObject (either the meta name or the last component of the URL).
      func imageTitle(_ fromMediaObject: MLMediaObject) -&gt; String {
  
          guard let title = fromMediaObject.attributes["name"] else {
              return fromMediaObject.url!.lastPathComponent
          }
          return title as! String
      }
  
  
      // MARK: - Photo Loading
  
      /// Observer for all key paths returned from the MLMediaLibrary.
      override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
          print ("Observe1...................")
          if (keyPath == MLMediaLibraryPropertyKeys.mediaSourcesKey &amp;&amp; context == &amp;mediaSourcesContext &amp;&amp; object! is MLMediaLibrary) {
  
              // The media sources have loaded, we can access the its root media.
  
              if let mediaSource = self.mediaLibrary.mediaSources?[MLMediaSourcePhotosIdentifier] {
                  self.mediaSource = mediaSource
              }
              else if let mediaSource = self.mediaLibrary.mediaSources?[MLMediaSourceiPhotoIdentifier] {
                  self.mediaSource = mediaSource
              }
              else {
                   print ("Observe2...................")
                  // Can't find any media sources.
                  self.noPhotosLabel.isHidden = false
  
                  // Stop progress indicator.
                  self.activityIndicator.isHidden = true
                  self.activityIndicator.stopAnimation(self)
  
                  return  // No photos found.
              }
  
              // Media Library is loaded now, we can access mediaSource for photos
              self.mediaSource.addObserver(self,
               forKeyPath: MLMediaLibraryPropertyKeys.rootMediaGroupKey,
               options: NSKeyValueObservingOptions.new,
               context: &amp;rootMediaGroupContext)
  
              // Obtain the media grouping (reference returns nil but starts asynchronous loading).
              if (self.mediaSource.rootMediaGroup != nil) {}
          }
          else if (keyPath == MLMediaLibraryPropertyKeys.rootMediaGroupKey &amp;&amp; context == &amp;rootMediaGroupContext &amp;&amp; object! is MLMediaSource) {
               print ("Observe3...................")
              // The root media group is loaded, we can access the media objects.
  
              // Done observing for media groups.
              self.mediaSource.removeObserver(self, forKeyPath: MLMediaLibraryPropertyKeys.rootMediaGroupKey, context:&amp;rootMediaGroupContext)
  
              self.rootMediaGroup = self.mediaSource.rootMediaGroup
              self.rootMediaGroup.addObserver(self,
                                              forKeyPath: MLMediaLibraryPropertyKeys.mediaObjectsKey,
                                              options: NSKeyValueObservingOptions.new,
                                              context: &amp;mediaObjectsContext)
  
              // Obtain the all the photos, (reference returns nil but starts asynchronous loading).
              if (self.rootMediaGroup.mediaObjects != nil) {}
          }
          else if (keyPath == MLMediaLibraryPropertyKeys.mediaObjectsKey &amp;&amp; context == &amp;mediaObjectsContext &amp;&amp; object! is MLMediaGroup) {
               print ("Observe4...................")
              // The media objects are loaded, we can now finally access each photo.
  
              // Done observing for media objects that group.
              self.rootMediaGroup.removeObserver(self, forKeyPath: MLMediaLibraryPropertyKeys.mediaObjectsKey, context:&amp;mediaObjectsContext)
  
              // Stop progress indicator since we know if we have photos (or not).
              self.activityIndicator.isHidden = true
              self.activityIndicator.stopAnimation(self)
  
              let mediaObjects = self.rootMediaGroup.mediaObjects
              if (mediaObjects != nil &amp;&amp; mediaObjects!.count &gt; 0) {
                   print ("Observe5...................")
                  // Add photos to the array, to be used in our NSCollectionView.
                  for mediaObject in mediaObjects! {
                      if (self.isValidImage(mediaObject)) {    // Make sure the media object is a photo.
  
                          let title = self.imageTitle(mediaObject)
  
                          if let image = NSImage.init(contentsOf: mediaObject.thumbnailURL!) {
                              let iconItem : Dictionary = [ItemKeys.imageKey: image, ItemKeys.nameKey: title] as [String : Any]
                              self.arrayController.addObject(iconItem)
                          }
                      }
                  }
  
                  self.collectionView.reloadData()
  
              }
              else {
                  // No photos available.
                  self.noPhotosLabel.isHidden = false
              }
  
              self.rootMediaGroup = nil // We are done with this.
          }
          else {
              super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
          }
      }
  
  }
  </code></pre>