2
0

Active user switching for for password window.

[RENAMED]   MPAppDelegate -> MPMacAppDelegate for Mac to avoid class name confusion in workspace.
[FIXED]     Window behavior when switching user.
This commit is contained in:
Maarten Billemont 2013-04-24 21:23:53 -04:00
parent 44b4e5430a
commit ceb0333fcf
6 changed files with 64 additions and 168 deletions

View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:MasterPassword/ObjC/iOS/MasterPassword-iOS.xcodeproj">
</FileRef>
<FileRef
location = "group:MasterPassword/ObjC/Mac/MasterPassword-Mac.xcodeproj">
</FileRef>

View File

@ -1,5 +1,5 @@
//
// MPAppDelegate.h
// MPMacAppDelegate.h
// MasterPassword
//
// Created by Maarten Billemont on 04/03/12.
@ -10,7 +10,7 @@
#import "MPAppDelegate_Shared.h"
#import "MPPasswordWindowController.h"
@interface MPAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate>
@interface MPMacAppDelegate : MPAppDelegate_Shared<NSApplicationDelegate>
@property(nonatomic, strong) NSStatusItem *statusItem;
@property(nonatomic, strong) MPPasswordWindowController *passwordWindow;

View File

@ -1,23 +1,23 @@
//
// MPAppDelegate.m
// MPMacAppDelegate.m
// MasterPassword
//
// Created by Maarten Billemont on 04/03/12.
// Copyright (c) 2012 Lyndir. All rights reserved.
//
#import "MPAppDelegate.h"
#import "MPMacAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
#import <Carbon/Carbon.h>
@interface MPAppDelegate ()
@interface MPMacAppDelegate()
@property(nonatomic) BOOL wasRunning;
@end
@implementation MPAppDelegate
@implementation MPMacAppDelegate
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfour-char-constants"
@ -46,11 +46,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
// Check which hotkey this was.
if (hotKeyID.signature == MPShowHotKey.signature && hotKeyID.id == MPShowHotKey.id) {
[((__bridge MPAppDelegate *)userData) activate:nil];
[((__bridge MPMacAppDelegate *)userData) activate:nil];
return noErr;
}
if (hotKeyID.signature == MPLockHotKey.signature && hotKeyID.id == MPLockHotKey.id) {
[((__bridge MPAppDelegate *)userData) lock:nil];
[((__bridge MPMacAppDelegate *)userData) lock:nil];
return noErr;
}
@ -64,7 +64,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[[self.usersItem submenu] removeItem:obj];
}];
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextForThreadIfReady];
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
if (!moc) {
self.createUserItem.title = @"New User (Not ready)";
self.createUserItem.enabled = NO;
@ -106,7 +106,7 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)selectUser:(NSMenuItem *)item {
NSError *error = nil;
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextForThreadIfReady];
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
self.activeUser = (MPUserEntity *)[moc existingObjectWithID:[item representedObject] error:&error];
if (error)
@ -139,11 +139,11 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
if (sender == self.rememberPasswordItem)
[MPConfig get].rememberLogin = [NSNumber numberWithBool:![[MPConfig get].rememberLogin boolValue]];
if (sender == self.savePasswordItem) {
MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread];
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserForThread];
if ((activeUser.saveKey = !activeUser.saveKey))
[[MPAppDelegate get] storeSavedKeyFor:activeUser];
[[MPMacAppDelegate get] storeSavedKeyFor:activeUser];
else
[[MPAppDelegate get] forgetSavedKeyFor:activeUser];
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
[activeUser.managedObjectContext saveToStore];
}
if (sender == self.dialogStyleRegular)
@ -155,11 +155,6 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (IBAction)newUser:(NSMenuItem *)sender {
}
- (IBAction)signOut:(id)sender {
[self signOutAnimated:YES];
}
- (IBAction)lock:(id)sender {
self.key = nil;
@ -205,9 +200,10 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
[[NSNotificationCenter defaultCenter] addObserverForName:MPCheckConfigNotification object:nil queue:nil usingBlock:
^(NSNotification *note) {
self.rememberPasswordItem.state = [[MPConfig get].rememberLogin boolValue]? NSOnState: NSOffState;
self.savePasswordItem.state = [[MPAppDelegate get] activeUserForThread].saveKey? NSOnState: NSOffState;
self.savePasswordItem.state = [[MPMacAppDelegate get] activeUserForThread].saveKey? NSOnState: NSOffState;
self.dialogStyleRegular.state = ![[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
self.dialogStyleHUD.state = [[MPMacConfig get].dialogStyleHUD boolValue]? NSOnState: NSOffState;
if ([note.object isEqual:NSStringFromSelector( @selector(dialogStyleHUD) )]) {
if (![self.passwordWindow.window isVisible])
self.passwordWindow = nil;
@ -238,9 +234,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)setActiveUser:(MPUserEntity *)activeUser {
[self.passwordWindow close];
[super setActiveUser:activeUser];
BOOL reopenPasswordWindow = [self.passwordWindow.window isVisible];
if (![[self activeUserForThread].objectID isEqual:activeUser.objectID]) {
[self.passwordWindow close];
self.passwordWindow = nil;
[super setActiveUser:activeUser];
}
[[[self.usersItem submenu] itemArray] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([[obj representedObject] isEqual:[activeUser objectID]])
@ -250,6 +250,9 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
}];
[MPMacConfig get].usedUserName = activeUser.name;
if (reopenPasswordWindow)
[self showPasswordWindow];
}
- (void)updateMenuItems {
@ -331,13 +334,13 @@ static OSStatus MPHotKeyHander(EventHandlerCallRef nextHandler, EventRef theEven
- (void)applicationWillResignActive:(NSNotification *)notification {
if (![[MPConfig get].rememberLogin boolValue])
self.key = nil;
[self lock:nil];
}
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
// Save changes in the application's managed object context before the application terminates.
NSManagedObjectContext *moc = [MPAppDelegate managedObjectContextForThreadIfReady];
NSManagedObjectContext *moc = [MPMacAppDelegate managedObjectContextForThreadIfReady];
if (!moc)
return NSTerminateNow;

View File

@ -7,7 +7,7 @@
//
#import "MPPasswordWindowController.h"
#import "MPAppDelegate.h"
#import "MPMacAppDelegate.h"
#import "MPAppDelegate_Key.h"
#import "MPAppDelegate_Store.h"
@ -26,16 +26,18 @@
- (void)windowDidLoad {
[self updateDialogStyle];
if ([[MPMacConfig get].dialogStyleHUD boolValue])
self.window.styleMask = NSHUDWindowMask | NSTitledWindowMask | NSUtilityWindowMask | NSClosableWindowMask;
else
self.window.styleMask = NSTexturedBackgroundWindowMask | NSResizableWindowMask | NSTitledWindowMask | NSClosableWindowMask;
[self setContent:@""];
[self.tipField setStringValue:@""];
[[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
[self.userLabel setStringValue:PearlString( @"%@'s password for:", [[MPAppDelegate get] activeUserForThread].name )];
} forKeyPath:@"activeUser" options:NSKeyValueObservingOptionInitial context:nil];
// [[MPAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
// [MPAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
// if (![MPAlgorithmDefault migrateUser:[[MPAppDelegate get] activeUserInContext:moc]])
[self.userLabel setStringValue:PearlString( @"%@'s password for:", [[MPMacAppDelegate get] activeUserForThread].name )];
// [[MPMacAppDelegate get] addObserverBlock:^(NSString *keyPath, id object, NSDictionary *change, void *context) {
// [MPMacAppDelegate managedObjectContextPerformBlock:^(NSManagedObjectContext *moc) {
// if (![MPAlgorithmDefault migrateUser:[[MPMacAppDelegate get] activeUserInContext:moc]])
// [NSAlert alertWithMessageText:@"Migration Needed" defaultButton:@"OK" alternateButton:nil otherButton:nil
// informativeTextWithFormat:@"Certain sites require explicit migration to get updated to the latest version of the "
// @"Master Password algorithm. For these sites, a migration button will appear. Migrating these sites will cause "
@ -61,33 +63,23 @@
[super windowDidLoad];
}
- (void)updateDialogStyle {
if ([[MPMacConfig get].dialogStyleHUD boolValue]) {
self.window.styleMask = NSHUDWindowMask | NSTitledWindowMask | NSUtilityWindowMask | NSClosableWindowMask;
}
else {
self.window.styleMask = NSTexturedBackgroundWindowMask | NSResizableWindowMask | NSTitledWindowMask | NSClosableWindowMask;
}
}
- (void)unlock {
MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread];
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserForThread];
if (!activeUser)
// No user to sign in with.
return;
if ([MPAppDelegate get].key)
if ([MPMacAppDelegate get].key)
// Already logged in.
return;
if ([[MPAppDelegate get] signInAsUser:activeUser usingMasterPassword:nil])
if ([[MPMacAppDelegate get] signInAsUser:activeUser usingMasterPassword:nil])
// Load the key from the keychain.
return;
if (![MPAppDelegate get].key)
if (![MPMacAppDelegate get].key)
// Ask the user to set the key through his master password.
dispatch_async( dispatch_get_main_queue(), ^{
if ([MPAppDelegate get].key)
if ([MPMacAppDelegate get].key)
return;
self.content = @"";
@ -114,7 +106,7 @@
return;
}
if (contextInfo == MPAlertUnlockMP) {
MPUserEntity *activeUser = [[MPAppDelegate get] activeUserForThread];
MPUserEntity *activeUser = [[MPMacAppDelegate get] activeUserForThread];
switch (returnCode) {
case NSAlertAlternateReturn:
// "Change" button.
@ -128,8 +120,8 @@
@"Your current sites and passwords will then become available again."] runModal]
== 1) {
activeUser.keyID = nil;
[[MPAppDelegate get] forgetSavedKeyFor:activeUser];
[[MPAppDelegate get] signOutAnimated:YES];
[[MPMacAppDelegate get] forgetSavedKeyFor:activeUser];
[[MPMacAppDelegate get] signOutAnimated:YES];
}
break;
@ -144,7 +136,7 @@
[self.progressView startAnimation:nil];
self.inProgress = YES;
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0 ), ^{
BOOL success = [[MPAppDelegate get] signInAsUser:activeUser
BOOL success = [[MPMacAppDelegate get] signInAsUser:activeUser
usingMasterPassword:[(NSSecureTextField *)alert.accessoryView stringValue]];
self.inProgress = NO;
@ -176,16 +168,16 @@
forPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger *)index {
NSString *query = [[control stringValue] substringWithRange:charRange];
if (![query length] || ![MPAppDelegate get].key)
if (![query length] || ![MPMacAppDelegate get].key)
return nil;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:NSStringFromClass( [MPElementEntity class] )];
fetchRequest.sortDescriptors = [NSArray arrayWithObject:[[NSSortDescriptor alloc] initWithKey:@"uses_" ascending:NO]];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"(name BEGINSWITH[cd] %@) AND user == %@",
query, [[MPAppDelegate get] activeUserForThread]];
query, [[MPMacAppDelegate get] activeUserForThread]];
NSError *error = nil;
self.siteResults = [[MPAppDelegate managedObjectContextForThreadIfReady] executeFetchRequest:fetchRequest error:&error];
self.siteResults = [[MPMacAppDelegate managedObjectContextForThreadIfReady] executeFetchRequest:fetchRequest error:&error];
if (error)
err(@"While fetching elements for completion: %@", error);
@ -320,14 +312,14 @@
// For when the app should be able to create new sites.
/*
else
[[MPAppDelegate get].managedObjectContext performBlock:^{
[[MPMacAppDelegate get].managedObjectContext performBlock:^{
MPElementEntity *element = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([MPElementGeneratedEntity class])
inManagedObjectContext:[MPAppDelegate get].managedObjectContext];
inManagedObjectContext:[MPMacAppDelegate get].managedObjectContext];
assert([element isKindOfClass:ClassFromMPElementType(element.type)]);
assert([MPAppDelegate get].keyID);
assert([MPMacAppDelegate get].keyID);
element.name = siteName;
element.keyID = [MPAppDelegate get].keyID;
element.keyID = [MPMacAppDelegate get].keyID;
NSString *description = [element.content description];
[element use];

View File

@ -51,7 +51,7 @@
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAC1724A667003798D8 /* MPEntities.m */; };
DA5E5D011724A667003798D8 /* MPKey.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CAE1724A667003798D8 /* MPKey.m */; };
DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB11724A667003798D8 /* MPUserEntity.m */; };
DA5E5D031724A667003798D8 /* MPAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPAppDelegate.m */; };
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */; };
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB61724A667003798D8 /* MPMacConfig.m */; };
DA5E5D051724A667003798D8 /* MPPasswordWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5E5CB81724A667003798D8 /* MPPasswordWindowController.m */; };
DA5E5D061724A667003798D8 /* MPPasswordWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = DA5E5CB91724A667003798D8 /* MPPasswordWindowController.xib */; };
@ -242,8 +242,8 @@
DA5E5CAF1724A667003798D8 /* MPTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPTypes.h; sourceTree = "<group>"; };
DA5E5CB01724A667003798D8 /* MPUserEntity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPUserEntity.h; sourceTree = "<group>"; };
DA5E5CB11724A667003798D8 /* MPUserEntity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPUserEntity.m; sourceTree = "<group>"; };
DA5E5CB31724A667003798D8 /* MPAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAppDelegate.h; sourceTree = "<group>"; };
DA5E5CB41724A667003798D8 /* MPAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAppDelegate.m; sourceTree = "<group>"; };
DA5E5CB31724A667003798D8 /* MPMacAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacAppDelegate.h; sourceTree = "<group>"; };
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacAppDelegate.m; sourceTree = "<group>"; };
DA5E5CB51724A667003798D8 /* MPMacConfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPMacConfig.h; sourceTree = "<group>"; };
DA5E5CB61724A667003798D8 /* MPMacConfig.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPMacConfig.m; sourceTree = "<group>"; };
DA5E5CB71724A667003798D8 /* MPPasswordWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPPasswordWindowController.h; sourceTree = "<group>"; };
@ -534,8 +534,8 @@
DA5E5CB21724A667003798D8 /* Mac */ = {
isa = PBXGroup;
children = (
DA5E5CB31724A667003798D8 /* MPAppDelegate.h */,
DA5E5CB41724A667003798D8 /* MPAppDelegate.m */,
DA5E5CB31724A667003798D8 /* MPMacAppDelegate.h */,
DA5E5CB41724A667003798D8 /* MPMacAppDelegate.m */,
DA5E5CB51724A667003798D8 /* MPMacConfig.h */,
DA5E5CB61724A667003798D8 /* MPMacConfig.m */,
DA5E5CB71724A667003798D8 /* MPPasswordWindowController.h */,
@ -1136,7 +1136,7 @@
DA5E5D001724A667003798D8 /* MPEntities.m in Sources */,
DA5E5D011724A667003798D8 /* MPKey.m in Sources */,
DA5E5D021724A667003798D8 /* MPUserEntity.m in Sources */,
DA5E5D031724A667003798D8 /* MPAppDelegate.m in Sources */,
DA5E5D031724A667003798D8 /* MPMacAppDelegate.m in Sources */,
DA5E5D041724A667003798D8 /* MPMacConfig.m in Sources */,
DA5E5D051724A667003798D8 /* MPPasswordWindowController.m in Sources */,
DA5E5D0C1724A667003798D8 /* main.m in Sources */,

View File

@ -39,7 +39,7 @@
<string key="NSName">_NSMainMenu</string>
</object>
<object class="NSCustomObject" id="976324537">
<string key="NSClassName">MPAppDelegate</string>
<string key="NSClassName">MPMacAppDelegate</string>
</object>
<object class="NSUserDefaultsController" id="705910970">
<bool key="NSSharedInstance">YES</bool>
@ -635,109 +635,7 @@
<nil key="sourceID"/>
<int key="maxID">774</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<array class="NSMutableArray" key="referencedPartialClassDescriptions">
<object class="IBPartialClassDescription">
<string key="className">MPAppDelegate</string>
<string key="superclassName">MPAppDelegate_Shared</string>
<dictionary class="NSMutableDictionary" key="actions">
<string key="activate:">id</string>
<string key="lock:">id</string>
<string key="newUser:">NSMenuItem</string>
<string key="signOut:">id</string>
<string key="togglePreference:">NSMenuItem</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="actionInfosByName">
<object class="IBActionInfo" key="activate:">
<string key="name">activate:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo" key="lock:">
<string key="name">lock:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo" key="newUser:">
<string key="name">newUser:</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBActionInfo" key="signOut:">
<string key="name">signOut:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo" key="togglePreference:">
<string key="name">togglePreference:</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
</dictionary>
<dictionary class="NSMutableDictionary" key="outlets">
<string key="createUserItem">NSMenuItem</string>
<string key="dialogStyleHUD">NSMenuItem</string>
<string key="dialogStyleRegular">NSMenuItem</string>
<string key="lockItem">NSMenuItem</string>
<string key="rememberPasswordItem">NSMenuItem</string>
<string key="savePasswordItem">NSMenuItem</string>
<string key="showItem">NSMenuItem</string>
<string key="statusMenu">NSMenu</string>
<string key="useICloudItem">NSMenuItem</string>
<string key="usersItem">NSMenuItem</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
<object class="IBToOneOutletInfo" key="createUserItem">
<string key="name">createUserItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="dialogStyleHUD">
<string key="name">dialogStyleHUD</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="dialogStyleRegular">
<string key="name">dialogStyleRegular</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="lockItem">
<string key="name">lockItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="rememberPasswordItem">
<string key="name">rememberPasswordItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="savePasswordItem">
<string key="name">savePasswordItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="showItem">
<string key="name">showItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="statusMenu">
<string key="name">statusMenu</string>
<string key="candidateClassName">NSMenu</string>
</object>
<object class="IBToOneOutletInfo" key="useICloudItem">
<string key="name">useICloudItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
<object class="IBToOneOutletInfo" key="usersItem">
<string key="name">usersItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
</dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/MPAppDelegate.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">MPAppDelegate_Shared</string>
<string key="superclassName">NSObject</string>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/MPAppDelegate_Shared.h</string>
</object>
</object>
</array>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">