Read from iPhoto Library programmatically - objective-c

I want to create an Application that connects to the iPhoto Library. So now I would like to read the Events and the pictures themselves from the library.
Is there an elegant / easy way to do this or do I have to manually read the Bundle Structure of the iPhoto User Data?
So far I have only found a picture taker: Is there a UIImagePicker for the Mac Desktop
Update: I found another relevant SO post: Selecting iPhoto images within a cocoa application

You can do it with NSAppleScript. This is some copy/paste from my app, hacked up a bit just to show the idea.
NSAppleEventDescriptor d = .. compile this script ..
#"tell application \"iPhoto\" to properties of albums"
for (int i = 0; i < [d numberOfItems]; i++)
{
NSAppleEventDescriptor *albumDesc = [d descriptorAtIndex:i];
// <NSAppleEventDescriptor: 'ipal'{
// 'ID ':4.265e+09,
// 'purl':'utxt'("http://www.flickr.com/photos/..."),
// 'pnam':'utxt'("Vacation"),
// 'alTy':'pubs',
// 'alCh':[ ],
// 'alPx':'msng' }>
NSString *albumName = [[albumDesc descriptorForKeyword:'pnam'] stringValue];
NSString *albumId = [[albumDesc descriptorForKeyword:'ID '] stringValue];
You can do the same thing to find the images
NSString *scp =
[NSString stringWithFormat:#"tell application \"iPhoto\" to properties of photos of album id %#",
[album objectForKey:#"id"]];
NSAppleEventDescriptor *d = ... compile scp ...
// 1 based!?
for (int i = 1; i <= [d numberOfItems]; i++)
{
NSAppleEventDescriptor *photoDesc = [d descriptorAtIndex:i];
// Yes.. this happens. Not sure why?!
if (!photoDesc)
continue;
// <NSAppleEventDescriptor: 'ipmr'{
// 'pnam':'utxt'("IMG_0058.JPG"),
// 'pwid':768,
// 'pdim':[ 768, 1024 ],
// 'alti':1.79769e+308,
// 'filn':'utxt'("3133889525_10975ba071_b.jpg"),
// 'ipth':'utxt'("/Users/lagnat/Pictures/iPhoto Library/Masters/2010/11/10/20101110-002341/3133889525_10975ba071_b.jpg"),
// 'idat':'ldt '($F57C69C500000000$),
// 'rate':0,
// 'titl':'utxt'("IMG_0058.JPG"),
// 'phit':1024,
// 'itpt':'utxt'("/Users/lagnat/Pictures/iPhoto Library/Thumbnails/2010/11/10/20101110-002341/3133889525_10975ba071_b.jpg.jpg"),
// 'ID ':4.295e+09,
// 'lati':'msng',
// 'pcom':'utxt'(""),
// 'opth':'utxt'("/Users/lagnat/Pictures/iPhoto Library/Masters/2010/11/10/20101110-002341/3133889525_10975ba071_b.jpg"),
// 'lngt':'msng',
// 'tiln':'utxt'("3133889525_10975ba071_b.jpg.jpg") }>
NSString *path = [[photoDesc descriptorForKeyword:'ipth'] stringValue];
NSString *imgname = [[photoDesc descriptorForKeyword:'pnam'] stringValue];

If releasing apps on the App Store you are now required now required to use the Sandbox, this stops the previous AppleScript method from working (the iPhoto app launches but an empty set is returned).
iPhoto libraries consist of a directory structure containing photos, databases and XML files. The contents changes with each version of iPhoto so be careful if manually accessing these files.
If you just want the album details you can parse the file AlbumData.xml
If you would like photos you can browse the Masters folder. The files structure follows date rather than by the sets configured in iPhoto.
More information can be found on the internals of the iPhoto library here:
http://www.fatcatsoftware.com/iplm/Help/iphoto%20library%20internals.html
The majority of the databases are in SQLite format and so can be programmatically accessed through Objective C, though again you can expect schema changes between different versions of iPhoto. The main databases of interest are Library.apdb and Properties.apdb in Database/apdb.
If you still want to use the Apple Script method, here's a version of the previous answer with the Apple script execution part included:
NSAppleScript *script = [[NSAppleScript alloc] initWithSource:#"tell application \"iPhoto\" to properties of albums"];
NSAppleEventDescriptor *d = [script executeAndReturnError:nil];
NSLog(#"photo library count: %ld", (long)[d numberOfItems]);
for (int i = 0; i < [d numberOfItems]; i++)
{
NSAppleEventDescriptor *albumDesc = [d descriptorAtIndex:i];
NSString *albumName = [[albumDesc descriptorForKeyword:'pnam'] stringValue];
NSLog(#"%#", albumName);
}

Related

Upgrading an Xcode Objective-C IOS game with Unity3d based project

I have an old Xcode/ObC quiz game that I launched quite a few years ago, pre-swift, that has been, and still is quite successful for me. At least the local version.
I am now at the finish line rewriting this game in Unity3d c#.
Something I have been thinking about lately is how to to maintain the "old" statistics that is saved in a plist file in IOS. I have been google this but I would need some more information to really understand how to proceed.
What will happen with the stats-plist file when I upgrade the current Xcode/ObC with the new Unity-based project, will it still be there and is it possible to easily find it? This particular plist is added when the first player is added and then updated with stats and new players.
Is there a good, and easy, way reading plist from Unity and convert to a normal text file?
To be able to find the file from Unity I am thinking of launching a maintenance release of the ObC based game and only copy this plist file to another directory (Document) to prepare for the new big release. When starting the Unity-based game for the first time I could then read the copied file and process so the player do not lose his/her stats.
The problem I have is that the only time I have updated the actual ObC code the last 5 - 6 years is when I updated the app from 32 to 64 bit so my skills on ObC is very limited at the moment.
I have been thinking of using something like this for the plist:
NSFileManager *filemgr;
filemgr = [NSFileManager defaultManager];
if ([filemgr copyItemAtPath: #"/tmp/myfile.txt" toPath: #"/Users/demo/newfile.txt" error: NULL] == YES)
NSLog (#"Copy successful");
else
NSLog (#"Copy failed");
I would really appreciate some advise how I should process this.
Here is some code you can use to list the doc and app support contents. I guard this with the #define as I do not want this in the final app. I also use it to perform some cleanup (the commented out stuff) that you can use if you need to delete something.
#ifdef DEBUG
// Auxiliary function to list directory contents
+ (void) docList
{
NSFileManager * fileManager = NSFileManager.defaultManager;
NSURL * docUrl = [NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask].firstObject;
NSString * docPath = docUrl.path;
NSLog( #"User document directory" );
NSLog( #"%# listing follows", docPath );
[fmUtil traverse:docPath tab:#"\t" fileManager:fileManager];
}
// Auxiliary function to list application support path
+ (void) supList
{
NSFileManager * fileManager = NSFileManager.defaultManager;
NSURL * docUrl = [NSFileManager.defaultManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask].firstObject;
NSString * docPath = docUrl.path;
NSLog( #"Application support directory" );
NSLog( #"\t%#", docPath );
NSLog( #"\tAppending bundle identifier" );
docPath = [docPath stringByAppendingPathComponent:NSBundle.mainBundle.bundleIdentifier];
NSLog( #"\t%#", docPath );
NSLog( #"%# listing follows", docPath );
[fmUtil traverse:docPath tab:#"\t" fileManager:fileManager];
}
+ (void) traverse:(NSString *)root tab:(NSString *)tab fileManager:(NSFileManager *)fileManager
{
NSArray * dir = [fileManager contentsOfDirectoryAtPath:root error:NULL];
for ( NSString * s in dir )
{
// See if this is a directory or not
NSString * t = [root stringByAppendingPathComponent:s];
BOOL isDir = NO;
[fileManager fileExistsAtPath:t isDirectory:& isDir];
if ( isDir )
{
// Report
NSLog(#"%#%#/",tab,s);
// Traverse
[fmUtil traverse:t tab:[tab stringByAppendingString:#"\t"]
fileManager:fileManager];
}
else
{
// Get size of normal file
NSDictionary * fa = [fileManager attributesOfItemAtPath:t error:NULL];
NSNumber * fz = [fa objectForKey:NSFileSize];
NSDate * fm = [fa objectForKey:NSFileModificationDate];
unsigned long long n = fz.unsignedLongLongValue;
// Some formatting
NSString * f = s;
while ( f.length < 50 )
{
f = [f stringByAppendingString:#" "];
}
NSLog(#"%#%# %15llu bytes (%#)", tab, f, n, [fm descriptionWithLocale:NSLocale.currentLocale] );
// To delete something for test purposes ...
/*
if ( [t.lastPathComponent isEqualToString:#"P5041-1-BuildingStatistics"] && [fileManager removeItemAtPath:t error:NULL] )
{
NSLog( #"%#%# now xxxxxxxxxxxxxxxxxx", tab, f );
}
if ( [t.lastPathComponent isEqualToString:#"index"] && [fileManager removeItemAtPath:t error:NULL] )
{
NSLog( #"%#%# now xxxxxxxxxxxxxxxxxx", tab, f );
}
*/
}
}
}
#endif
Put this in your app delegate e.g. inside application:didFinishLaunchingWithOptions:.
Well, today I started to look back on this "problem" and after testing it turned out not to be a problem at all. I tried initially to find the path via my Objective-C code (this post) but when testing I realised that the files were stored in the (IOS game) Document directory, the same as "Application.persistentDataPath". In Unity I used the following path's:
filePathPlayerData = Application.persistentDataPath + "/playerdata.plist";
filePathPlayerStats = Application.persistentDataPath + "/playerstatistics.plist";
Reading and process the file is easy as well as it is XML structure. However due to the simple format I opened the files as text files wrote my own simple parser in c# (unity), rather than using an XML parser. The whole "problem" turned out not to be any problem.

PHPhotoLibrary getting album and photo info

I am trying to get info on all the albums/photos using the PHPhotoLibrary. I barely know objective C, and i've looked at some tutorial/sample but couldn't find everything that I needed.
Here is a link to the sample code I based my code on.
https://developer.apple.com/library/ios/samplecode/UsingPhotosFramework/Introduction/Intro.html#//apple_ref/doc/uid/TP40014575-Intro-DontLinkElementID_2
So far I was able to get the albums name and identifier. And I am getting a list of photos, I am able to get their identifier as well, but not the filename. But if I put a break point in my fonction and look at my PHAsset pointer values, I can see the filename there (inside _filename), but if I try to call the variable with the filename in it, the variable does not exist.
So if anyone can provide a sample code to get all info on albums/photos/thumbnail that would be awesome. Or just getting the filename would be a good help.
Here is the code I have tried so far:
-(void)awakeFromNib{
NSMutableArray *allPhotos = self.getAllPhotos;
for (int x = 0; x < allPhotos.count; x ++)
{
PHAsset *photo = [self getPhotoAtIndex:x];
PHAssetSourceType source = photo.sourceType;
NSString *id = photo.localIdentifier;
NSString *description = photo.description;
NSUInteger height = photo.pixelHeight;
NSUInteger width = photo.pixelWidth;
NSLog(#"Test photo info");
}
}
-(PHAsset*) getPhotoAtIndex:(NSInteger) index
{
return [self.getAllPhotos objectAtIndex:index];
}
-(NSMutableArray *) getAllPhotos
{
NSMutableArray *photos = [[NSMutableArray alloc] init];
PHFetchOptions *allPhotosOptions = [[PHFetchOptions alloc] init];
allPhotosOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *allPhotos = [PHAsset fetchAssetsWithOptions:allPhotosOptions];
PHFetchResult *fetchResult = #[allPhotos][0];
for (int x = 0; x < fetchResult.count; x ++) {
PHAsset *asset = fetchResult[x];
photos[x] = asset;
}
return photos;
}
As you can see, I can get the image height and width, its id, but cannot get the url to it.
I have found a way to get the url of my photo.
-(void)getImageURL:(PHAsset*) asset
{
PHContentEditingInputRequestOptions *options = [[PHContentEditingInputRequestOptions alloc] init];
[options setCanHandleAdjustmentData:^BOOL(PHAdjustmentData *adjustmentData) {
return [adjustmentData.formatIdentifier isEqualToString:AdjustmentFormatIdentifier] && [adjustmentData.formatVersion isEqualToString:#"1.0"];
}];
[asset requestContentEditingInputWithOptions:options completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info)
{
NSURL* url = contentEditingInput.fullSizeImageURL;
}];
}
Filenames in the Photos library are an implementation detail and subject to change. There are various private API for discovering them (or ways to use valueForKey or other public introspection APIs to find where they're hidden), they aren't something to be relied upon. In particular, an asset that's been edited is likely to have a different filename than the original.
What do you need a filename/URL for? If you're just uniquely identifying the asset across launches of your app, use localIdentifier. If you're showing it to the user... why? Something like IMG_0234.jpg vs IMG_5672.jpg has little meaning to the average user.
To fetch the assets in a specific album, use fetchAssetsInAssetCollection:options:. To fetch the album(s) containing a specific asset, use fetchAssetCollectionsContainingAsset:withType:options:. To discover the list(s) of albums, use other APIs on PHAssetCollection and its superclass PHCollection.

List / Scan for available WiFis iPhone

I'm searching for a way to present available WiFis in an iPhone App.
So far my research resulted in the following:
Apps that implement(ed) such a functionality were removed from the AppStore (means you can't deploy the App via AppStore which is fine for me)
Apple hides the functionality that is necessary for a scan in a private framework and you can't find any explanations/comments/examples on "how to use"
http://code.google.com/p/iphone-wireless seems to be most promising. anyway, i can't figure out how to include the delivered sources in my code so that it runs on a device
Even the adaptions that are mentioned htt ://code.google.com/p/iphone-wireless/issues/detail?id=26 didn't get me the desired results. The most progress was ending up with a
dlopen error: dlopen(/System/Library/SystemConfiguration/Aeropuerto.bundle/Aeropuerto, 1): image not found
failed: __Apple80211Associate
message after launching the app on a device (iPhone 3GS; iOS 3.1.3).
Used source code that procudes the error is here:
NSMutableDictionary *networks;
bool scanning;
void *libHandle;
void *airportHandle;
int (*open)(void *);
int (*bind)(void *, NSString *);
int (*close)(void *);
int (*scan)(void *, NSArray **, void *);
networks = [[NSMutableDictionary alloc] init];
// libHandle = dlopen("/System/Library/Frameworks/Preferences.framework/Preferences", RTLD_LAZY);
// libHandle = dlopen("/System/Library/PrivateFrameworks/Apple80211.framework/Preferences", RTLD_LAZY);
libHandle = dlopen("/System/Library/SystemConfiguration/WiFiManager.bundle/WiFiManager", RTLD_LAZY);
open = dlsym(libHandle, "Apple80211Open");
bind = dlsym(libHandle, "Apple80211BindToInterface");
close = dlsym(libHandle, "Apple80211Close");
scan = dlsym(libHandle, "Apple80211Scan");
open(&airportHandle);
bind(airportHandle, #"en0");
NSLog(#"Scanning...");
scanning = true;
NSArray *scan_networks;
NSDictionary *parameters = [[NSDictionary alloc] init];
scan(airportHandle, &scan_networks, parameters);
bool changed;
for (int i = 0; i < [scan_networks count]; i++) {
if([networks objectForKey:[[scan_networks objectAtIndex: i] objectForKey:#"BSSID"]] != nil
&& ![[networks objectForKey:[[scan_networks objectAtIndex: i] objectForKey:#"BSSID"]] isEqualToDictionary:[scan_networks objectAtIndex: i]])
changed = true;
[networks setObject:[scan_networks objectAtIndex: i] forKey:[[scan_networks objectAtIndex: i] objectForKey:#"BSSID"]];
}
if(changed) {
NSLog(#"NetworksUpdated");
}
scanning = false;
NSLog(#"Scan Finished...");
NSLog(#"Found %i networks: %#", [networks count], networks);
Even if trying one of the other commented lines, it doesn't work:
program received EXC_BAD_ACCESS and several
warning: check_safe_call: could not restore current frame
warning: Unable to restore previously selected frame.
What i'm searching are hints how to include iphone-wireless in my project and how to modify the given code?
An alternative would be a tip on how to scan for WiFis in your environment.
Would be nice if someone could help.
path has changed in 3.X and beyond, from :
/System/Library/SystemConfiguration/Aeropuerto.bundle/Aeropuerto
to:
/System/Library/SystemConfiguration/IPConfiguration.bundle/IPConfifuration

Editing Mac OS X login items in Objective-C through AppleScript

In my Cocoa program, I want to examine what programs are registered to run at startup and modify that list as I feel appropriate. In order to be compatible with Tiger it seems like I need to work through AppleScript. I currently have the following code:
NSDictionary* errorDict;
NSAppleEventDescriptor* returnDescriptor = NULL;
NSString *appleSource = #"tell application \"System Events\"\n\
get every login item\n\
end tell";
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource: appleSource];
returnDescriptor = [appleScript executeAndReturnError: &errorDict];
If I run that command in AppleScript, I get back an array of login items. However, I can't figure out how to iterate through this array in Objective-C. More specifically, I want to examine the names and paths of the programs registered to run at startup.
Any ideas?
Edit: I figured this out. Here is some sample code. The key is using AEKeyword's, which are very poorly documented. The best reference is here: http://developer.apple.com/mac/library/releasenotes/AppleScript/ASTerminology_AppleEventCodes/TermsAndCodes.html
const AEKeyword aeName = 'pnam';
const AEKeyword aePath = 'ppth';
...
NSDictionary* errorDict;
NSAppleEventDescriptor* getLoginItemsRD = NULL;
NSString *getLoginItemsSrc = #"tell application \"System Events\"\n\
get properties of every login item\n\
end tell";
NSAppleScript *getLoginItemsScript = [[NSAppleScript alloc] initWithSource: getLoginItemsSrc];
getLoginItemsRD = [getLoginItemsScript executeAndReturnError: &errorDict];
[getLoginItemsScript release];
int i;
int numLoginItems = [getLoginItemsRD numberOfItems];
for (i = 1; i <= numLoginItems; i++)
{
NSAppleEventDescriptor *loginItem = [getLoginItemsRD descriptorAtIndex:i];
NSString *loginItemName = [[loginItem descriptorForKeyword:aeName] stringValue];
NSString *loginItemPath = [[loginItem descriptorForKeyword:aePath] stringValue];
}
Apple has some source code which can manage login items for Tiger and earlier. I believe you're supposed to get it from ADC but I found it floating around here:
LoginItemAPI.h
LoginItemAPI.c

How do I get the details of an application using Objective-C?

I have the following code to detect the current window. How can I get 1) application internal name, 2) location, 3) publisher and 4) description of the window/application?
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
//Get info about the currently active application.
NSWorkspace* workspace = [NSWorkspace sharedWorkspace];
NSDictionary* currentAppInfo = [workspace activeApplication];
//Get the PSN of the current application.
UInt32 lowLong = [[currentAppInfo objectForKey:#"NSApplicationProcessSerialNumberLow"] longValue];
UInt32 highLong = [[currentAppInfo objectForKey:#"NSApplicationProcessSerialNumberHigh"] longValue];
ProcessSerialNumber currentAppPSN = {highLong,lowLong};
//Grab window information from the window server.
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
ProcessSerialNumber myPSN = {kNoProcess, kNoProcess};
//Loop through the windows, the window list is ordered from front to back.
for (NSMutableDictionary* entry in (NSArray*) windowList)
{
int pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
GetProcessForPID(pid, &myPSN);
//If the process of the current window in the list matches our process, get the front window number.
if(myPSN.lowLongOfPSN == currentAppPSN.lowLongOfPSN && myPSN.highLongOfPSN == currentAppPSN.highLongOfPSN)
{
NSNumber *windowNumber = [entry objectForKey:(id)kCGWindowNumber];
windowNumber = [entry objectForKey:(id)kCGWindowNumber];
NSString* applicationName = [entry objectForKey:(id)kCGWindowOwnerName];
NSLog(#"Capture the window: %# with window ID: %#.",applicationName,windowNumber);
return applicationName;
//Break because we only want the front window.
break;
}
}
CFRelease(windowList);
[pool release];
You should use the ProcessInformationCopyDictionary function from the Process Manager API. Give it &myPSN and kProcessDictionaryIncludeAllInformationMask as arguments and you will get the information you are looking for.
I was looking for something related with this topic. I need a WindowRef of the window or window part at a certain location (mouse position) and it has to be over all the windows of all running applications...
I´ve tried it with Carbon (´Cos my App is entirely written in C++) but I´ve found that Some Carbon Functions Doesn´t work properly (MacFindWindow, FindWindow, HIWindowFindAtLocation, FindWindowOfClass, HIWindowGetCGWindowID...)
Maybe I´m doing it wrong, It´s difficult to believe that those Carbon functions won´t work any more in 64 bits architectures...
So, related with your question I found the same code and I tried this but it isn´t what I need, I hope it helps you in any way and I´ll keep searching and trying till I get it (If the O.S can do it everybody should).
//if the process of the current window in the list matches our process, get the front window number
if(myPSN.lowLongOfPSN == currentAppPSN.lowLongOfPSN && myPSN.highLongOfPSN == currentAppPSN.highLongOfPSN)
{
NSNumber* windowNumber = [entry objectForKey:(id)kCGWindowNumber];
NSString* applicationName = [entry objectForKey:(id)kCGWindowOwnerName];
NSLog(#"The current app is %# and the window number of its front window is %#.",applicationName,windowNumber);
CGRect bounds;
CGRectMakeWithDictionaryRepresentation((CFDictionaryRef)[entry objectForKey:(id)kCGWindowBounds], &bounds);
NSLog(#"WINDOW RECT BOUNDS; (x,y,width, height) = (%d,%d, %d, %d)", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
break;
}
Also, follow this link, I´t will help you. I´m sure:
http://code.google.com/p/blazingstars/source/browse/trunk/PokerHK/HKLowLevel.m?r=70