11

In Objective-C, there's a Photos Framework a.k.a. PhotoKit which enables iOS developers to access the photos library on iPhone and iPad and to retrieve the pictures/videos along with their metadata.

How would Mac developers perform a similar task? It seems PhotoKit is only available in iOS 8.0. Is there an equivalent of the Photos Framework for Mac OS X?

Cesare
  • 9,139
  • 16
  • 78
  • 130
Pierre F
  • 1,332
  • 15
  • 33
  • https://developer.apple.com/library/mac/documentation/MediaLibrary/Reference/MediaLibraryFrameworkReference/index.html#//apple_ref/doc/uid/TP40012956 – matt May 09 '15 at 20:22
  • 1
    The above mentioned web page of PhotoKit says that it is also available for "macOS 10.11+". – Gab Oct 04 '16 at 12:54
  • @Gabriel Just to clarify, there is PhotoKit on macOS 10.11+, but only limit to build extensions from Photos app. Unlike what is providing on iOS, you won't be able to access Photos Library through it on macOS. – Hao Xi Dec 01 '16 at 02:57

3 Answers3

11

The Media Library Framework is the place to go.

Usage:

@import MediaLibrary;

- (void) awakeFromNib
{
  NSDictionary *options = @{
     MLMediaLoadSourceTypesKey: @(MLMediaSourceTypeImage),
     MLMediaLoadIncludeSourcesKey: @[MLMediaSourcePhotosIdentifier]
  };

  MLMediaLibrary *mediaLibrary = [[MLMediaLibrary alloc] initWithOptions:options];
  self.mediaLibrary = mediaLibrary;

  [mediaLibrary addObserver:self
                 forKeyPath:@"mediaSources"
                    options:0
                    context:(__bridge void *)@"mediaLibraryLoaded"];

  [mediaLibrary mediaSources]; // returns nil and starts asynchronous loading
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                        change:(NSDictionary *)change context:(void *)context
{
   if (context == (__bridge void *)@"mediaLibraryLoaded") {
      // Media Library is loaded now, we can access mediaSources
      MLMediaSource *mediaSource = [self.mediaLibrary.mediaSources objectForKey:@"com.apple.Photos"];
   }
}

The concept behind the library is that you have to request it to read an attribute of an object, which returns an empty reference. Then you subscribe to this attribute with a key-value-observer and you wait till it is loaded. Then you can retrieve the next child with the same principle and so on...

Pierre F
  • 1,332
  • 15
  • 33
  • I'm having a hard time getting access to the individual photos from this code sample. Any change you could elaborate on how to use the MLMediaSource? The documentation is [really bad](http://stackoverflow.com/q/34835821/1418688)... – Nerrolken Jan 20 '16 at 23:17
  • I agree, the documentation isn't clear and you have to try things out with the debugger to find out how to get down to the MLMediaObjects... – Pierre F Jan 24 '16 at 16:29
  • how to access Photos Library images from path? Example Photos library is in /Users/admin/Desktop/xyz.photoslibrary. – Amit Khandelwal Dec 23 '16 at 07:18
  • ⚠️ Media Library Framework is now Deprecated – Mikeumus Apr 10 '21 at 20:51
4

Based on Pierre F answer I extend code for displaying url's for all photos:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {

    NSDictionary *options = @{
                              MLMediaLoadSourceTypesKey: @(MLMediaSourceTypeImage),
                              MLMediaLoadIncludeSourcesKey: @[MLMediaSourcePhotosIdentifier]
                              };

    self.mediaLibrary = [[MLMediaLibrary alloc] initWithOptions:options];

    [self.mediaLibrary addObserver:self
                        forKeyPath:@"mediaSources"
                           options:0
                           context:(__bridge void *)@"mediaLibraryLoaded"];

    [self.mediaLibrary.mediaSources objectForKey:MLMediaSourcePhotosIdentifier];
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
                        change:(NSDictionary *)change context:(void *)context
{
     MLMediaSource *mediaSource = [self.mediaLibrary.mediaSources objectForKey:MLMediaSourcePhotosIdentifier];

    if (context == (__bridge void *)@"mediaLibraryLoaded")
    {
        [mediaSource addObserver:self
                            forKeyPath:@"rootMediaGroup"
                               options:0
                               context:(__bridge void *)@"rootMediaGroupLoaded"];

        [mediaSource rootMediaGroup];
    }
    else if (context == (__bridge void *)@"rootMediaGroupLoaded")
    {
        MLMediaGroup *albums = [mediaSource mediaGroupForIdentifier:@"TopLevelAlbums"];

        for (MLMediaGroup *album in albums.childGroups)
        {
            NSString *albumIdentifier = [album.attributes objectForKey:@"identifier"];

            if ([albumIdentifier isEqualTo:@"allPhotosAlbum"])
            {
                self.allPhotosAlbum = album;

                [album addObserver:self
                        forKeyPath:@"mediaObjects"
                           options:0
                           context:@"mediaObjects"];

                [album mediaObjects];

                break;
            }
        }
    }
    else if (context == (__bridge void *)@"mediaObjects")
    {
        NSArray * mediaObjects = self.allPhotosAlbum.mediaObjects;

        for(MLMediaObject * mediaObject in mediaObjects)
        {
            NSURL * url  = mediaObject.URL;

            NSLog(url.path);
        }
    }
}
toohtik
  • 1,892
  • 11
  • 27
  • This looks awesome, I'll be trying it out as soon as I get home! I'm curious, do you have any ideas on how to get the GPS location from an MLMediaObject? I have [a question about that](http://stackoverflow.com/q/34932712/1418688) open right now, and there's surprisingly little information out there about the Media Library framework in general. – Nerrolken Jan 21 '16 at 19:35
  • Btw, I tried to accept your answer on my other question, but it seems to have been deleted? If you repost it with this code, I'll happily award the bounty to you tonight! :) – Nerrolken Jan 21 '16 at 19:36
  • I was trying to follow your example, but what is @keypath? Xcode (7.2.1) complains, and google has been to no help :( – Jens Peter Mar 09 '16 at 19:57
  • sorry, keypath can be found here https://github.com/jspahrsummers/libextobjc/blob/master/extobjc/EXTKeyPathCoding.h or try to replace just with string value – toohtik Mar 10 '16 at 11:37
  • Nice answer except for @keypath. – zaph Apr 16 '16 at 18:52
1

Although this is not an officially documented API, w0lfschild published the headers of Apple's PhotoKit framework for macOS. The API looks very similar to the iOS one, so maybe you can figure it out using just the iOS docs. And in addition to MediaLibrary Framework you can also make changes to the library.

Damiaan Dufaux
  • 4,427
  • 1
  • 22
  • 33