AirDropSample/APLAppDelegate.m
/* |
File: APLAppDelegate.m |
Abstract: Application Delegate that handles app start up and the AirDropped received files. |
Version: 1.0 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright (C) 2013 Apple Inc. All Rights Reserved. |
*/ |
#import "APLAppDelegate.h" |
#import "APLProfileTableViewController.h" |
#import "APLSharingTypesTableViewController.h" |
#import "APLProfile.h" |
#import "APLUtilities.h" |
NSString * const kCustomScheme = @"adcs"; |
NSString * const kMainStoryboardName = @"Main"; |
NSString * const kProfileViewControllerIdentifier = @"ProfileViewController"; |
NSString * const kDocumentsInboxFolder = @"Inbox"; |
//Custom Notifications used when receiving content |
NSString * const DisplayingSaveWindowNotification = @"DisplayingSaveWindow"; |
NSString * const SavedReceivedProfilesNotification = @"SavedReceivedProfiles"; |
NSString * const SavedCustomURLNotification = @"SavedCustomURL"; |
NSString * const kSavedReceivedProfilesFileNamesKey = @"SavedFileNames"; |
@interface APLAppDelegate() |
@property (strong, nonatomic) UINavigationController *navigationController; |
@property (strong, nonatomic) APLSharingTypesTableViewController *sharingTypesViewController; |
@property (strong, nonatomic) NSMutableArray *receivedProfileQueue; |
@property (strong, nonatomic) UINavigationController *receivedProfilesNavigationController; |
@property (strong, nonatomic) NSMutableArray *savedFileNames; |
@property (strong, nonatomic) UIWindow *saveReceivedWindow; |
@end |
@implementation APLAppDelegate |
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions |
{ |
self.navigationController = (UINavigationController *)self.window.rootViewController; |
self.sharingTypesViewController = self.navigationController.viewControllers[0]; |
self.savedFileNames = [NSMutableArray array]; |
return YES; |
} |
- (void)applicationDidBecomeActive:(UIApplication *)application |
{ |
//Check for orphaned files in the inbox |
[self handleDocumentsInbox]; |
} |
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation |
{ |
if (url) { |
if (url.scheme && [url.scheme isEqualToString:kCustomScheme]) { |
//Save the received profile and notify observers |
[APLUtilities saveCustomURL:url]; |
[[NSNotificationCenter defaultCenter] postNotificationName:SavedCustomURLNotification object:nil]; |
//Show custom URL view controller if currently in the sharing types table, otherwise don't, the user could be in the middle of something |
if ([self.navigationController visibleViewController] == self.sharingTypesViewController && !self.saveReceivedWindow) { |
[self.sharingTypesViewController showCustomURLView]; |
} |
} |
else |
{ |
APLProfile *profile = [APLUtilities securelyUnarchiveProfileWithFile:[url path]]; |
if (profile) { |
//Enqueue profile incase another one is already being handled |
[self enqueueProfile:profile]; |
} |
//Clean up inbox |
[self removeInboxItem:url]; |
} |
} |
return YES; |
} |
#pragma mark - Document Inbox Handling |
- (void)handleDocumentsInbox |
{ |
//All incoming files are stored in Documents/Inbox/ |
NSString *inboxPath = [[APLUtilities documentsDirectory] stringByAppendingPathComponent:kDocumentsInboxFolder]; |
NSFileManager *fileManager = [[NSFileManager alloc] init]; |
NSArray *inboxFiles = [fileManager contentsOfDirectoryAtPath:inboxPath error:nil]; |
for (NSString *path in inboxFiles) { |
//Append file name to path and create URL |
NSURL *url = [NSURL fileURLWithPath:[inboxPath stringByAppendingPathComponent:path]]; |
[self application:[UIApplication sharedApplication] openURL:url sourceApplication:@"" annotation:nil]; |
} |
} |
- (void)removeInboxItem:(NSURL *)itemURL |
{ |
//Clean up the inbox once the file has been processed |
NSError *error = nil; |
[[NSFileManager defaultManager] removeItemAtPath:[itemURL path] error:&error]; |
if (error) { |
NSLog(@"ERROR: Inbox file could not be deleted"); |
} |
} |
#pragma mark - AirDropped Profile Receiving |
/**************************************************** |
Handling Incoming Profiles |
When the app receives a profile it displays it own view to give a chance to look at the profile and decide if they want it. |
There are two considerations when displaying a save view for the user. |
First the user could be doing an important task that this save view is going to interrupt. To account for this the app displays the save view in its own window, which will not disturb the main window's contents. |
Second while the user is deciding if they want to keep the profile, another one could arrive. Because of this possibility, this app keeps a queue of arriving profiles. |
When the first profile arrives a navigation controller is created to display that profile in a APLProfileViewController. |
When each subsequent profile arrives they are enqueued. Once the user decides whether or not to save the displayed profile, the next profile is pushed onto the navigation controller's stack. |
******************************************************/ |
- (void)enqueueProfile:(APLProfile *)profile |
{ |
if (!_receivedProfileQueue) { |
_receivedProfileQueue = [NSMutableArray array]; |
} |
@synchronized(self.receivedProfileQueue) |
{ |
[self.receivedProfileQueue addObject:profile]; |
//If first recevied, profile present |
if (self.receivedProfileQueue.count == 1) { |
[self presentFirstProfile]; |
} |
} |
} |
- (APLProfile *)dequeueProfile |
{ |
@synchronized(self.receivedProfileQueue) { |
APLProfile *profile = nil; |
if (self.receivedProfileQueue.count > 0) { |
profile = [self.receivedProfileQueue firstObject]; |
[self.receivedProfileQueue removeObject:profile]; |
} |
return profile; |
} |
} |
- (void)presentFirstProfile |
{ |
APLProfile *profile = [self.receivedProfileQueue firstObject]; |
if (profile) { |
//Notify observers that the save window will appear |
[[NSNotificationCenter defaultCenter] postNotificationName:DisplayingSaveWindowNotification object:nil]; |
//Create Window |
self.saveReceivedWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; |
[self.saveReceivedWindow setWindowLevel:UIWindowLevelNormal]; |
//Create profileViewController to display received profile |
APLProfileViewController *profileViewController = [self createProfileViewControllerForProfile:profile]; |
//Create a navigation controller to handle displaying multiple incoming profiles |
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:profileViewController]; |
self.receivedProfilesNavigationController = nav; |
[self.saveReceivedWindow setRootViewController:nav]; |
//Set frame below the screen |
CGRect originalFrame = self.saveReceivedWindow.frame; |
CGRect newFrame = self.saveReceivedWindow.frame; |
newFrame.origin.y = newFrame.origin.y + newFrame.size.height; |
self.saveReceivedWindow.frame = newFrame; |
//Create animation to have window slide up from bottom of the screen |
[self.saveReceivedWindow makeKeyAndVisible]; |
[UIView animateWithDuration:0.4f |
delay:0.0f |
options:UIViewAnimationOptionCurveEaseInOut |
animations:^{ |
self.saveReceivedWindow.frame = originalFrame; |
} |
completion:nil]; |
} |
} |
- (void)presentNextProfile |
{ |
APLProfile *profile = [self.receivedProfileQueue firstObject]; |
if (profile) { |
APLProfileViewController *profileViewController = [self createProfileViewControllerForProfile:profile]; |
//Make sure navigation controller exists |
if (self.receivedProfilesNavigationController) { |
[self.receivedProfilesNavigationController pushViewController:profileViewController animated:YES]; |
} |
} |
else if (self.receivedProfilesNavigationController) //Queue is empty |
{ |
//Animate the window away (slide down) |
[UIView animateWithDuration:0.4f |
delay:0.0f |
options:UIViewAnimationOptionCurveEaseInOut |
animations:^{ |
[self.saveReceivedWindow resignKeyWindow]; |
CGRect newFrame = self.saveReceivedWindow.frame; |
newFrame.origin.y = newFrame.origin.y + newFrame.size.height;; |
self.saveReceivedWindow.frame = newFrame; |
} |
completion:^(BOOL finished) { |
self.saveReceivedWindow = nil; |
self.receivedProfilesNavigationController = nil; |
//Only show profile table if at least one profile was saved |
if ([self.savedFileNames count] > 0) { |
NSDictionary *userInfo = @{kSavedReceivedProfilesFileNamesKey : [self.savedFileNames copy]}; |
//Notify observers at least one profile was saved |
[[NSNotificationCenter defaultCenter] postNotificationName:SavedReceivedProfilesNotification object:nil userInfo:userInfo]; |
//Show profile table if currently in the sharing types table, otherwise don't, the user could be in the middle of something |
if ([self.navigationController visibleViewController] == self.sharingTypesViewController) { |
[self.sharingTypesViewController showProfilesTable]; |
} |
[self.savedFileNames removeAllObjects]; |
} |
}]; |
} |
} |
- (void)saveProfile:(id)sender |
{ |
APLProfile *profile = [self dequeueProfile]; |
if (profile) { |
//Save profile to persistent file |
[APLUtilities saveProfile:profile]; |
[self.savedFileNames addObject:[profile filename]]; |
} |
[self presentNextProfile]; |
} |
- (void)discardProfile:(id)sender |
{ |
[self dequeueProfile]; |
[self presentNextProfile]; |
} |
- (APLProfileViewController *)createProfileViewControllerForProfile:(APLProfile *)profile |
{ |
UIStoryboard *sb = [UIStoryboard storyboardWithName:kMainStoryboardName bundle:nil]; |
APLProfileViewController *profileViewController = [sb instantiateViewControllerWithIdentifier:kProfileViewControllerIdentifier]; |
profileViewController.profile = profile; |
profileViewController.interactive = NO; |
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle:@"Discard" |
style:UIBarButtonItemStylePlain |
target:self |
action:@selector(discardProfile:)]; |
UIBarButtonItem *saveButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemSave |
target:self |
action:@selector(saveProfile:)]; |
profileViewController.navigationItem.leftBarButtonItem = cancelButton; |
profileViewController.navigationItem.rightBarButtonItem = saveButton; |
return profileViewController; |
} |
@end |
Copyright © 2013 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2013-10-17