Support for Open In ... item in my application for iOS Mail And Safari

I need my applications to open documents from Safari and Mail applications using this "Open in ..." function in the UIDocumentInteractionController class. How to do it?

+48
ios objective-c cocoa-touch uidocumentinteraction
Oct 30 '11 at 0:30
source share
2 answers

I know that it was very unpleasant for me as a novice programmer or even now as an experienced specialist. File I / O through Mail and Safari applications includes very ... interestingly named conventions within the application itself. So let our hands be dirty with the Xcode project for the iPhone. Open Xcode (I will use 4.2 for this tutorial) and select the One View application template (or create an empty project and then add one view using .xib).

Screenshot showing Xcode template selection sheet

In this new application, rename the view controller (and its associated xib) to OfflineReaderViewController , and then we'll move on to the code. (We will touch on each file, but the prefix header and main.m, so keep in mind that you need everything in front of you!)

Enter the AppDelegate header and paste the following code into it:

 #import <UIKit/UIKit.h> @class OfflineReaderViewController; @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (strong, nonatomic) OfflineReaderViewController *viewController; @end 

Then enter the .m delegate file and paste the following code in the transcript:

 #import "AppDelegate.h" #import "OfflineReaderViewController.h" @implementation AppDelegate @synthesize window; @synthesize viewController; -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { // Make sure url indicates a file (as opposed to, eg, http://) if (url != nil && [url isFileURL]) { // Tell our OfflineReaderViewController to process the URL [self.viewController handleDocumentOpenURL:url]; } // Indicate that we have successfully opened the URL return YES; } 
 - (void)dealloc { [window release]; [viewController release]; [super dealloc]; } - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease]; // Override point for customization after application launch. self.viewController = [[[OfflineReaderViewController alloc] initWithNibName:@"ViewController" bundle:nil] autorelease]; self.window.rootViewController = self.viewController; [self.window makeKeyAndVisible]; return YES; } - (void)applicationWillResignActive:(UIApplication *)application { /* Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. */ } - (void)applicationDidEnterBackground:(UIApplication *)application { /* Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. */ } - (void)applicationWillEnterForeground:(UIApplication *)application { /* Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. */ } - (void)applicationDidBecomeActive:(UIApplication *)application { /* Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. */ } - (void)applicationWillTerminate:(UIApplication *)application { /* Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. */ } @end 

It:

 -(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { if (url != nil && [url isFileURL]) { [self.viewController handleDocumentOpenURL:url]; } return YES; } 

It is the single most important part of this lesson. To break it down into appropriate parts: -(BOOL)application:(UIApplication *)application - our -(BOOL)application:(UIApplication *)application application; openURL:(NSURL *)url is the URL that is sent to tell us what to open; sourceApplication:(NSString *)sourceApplication - the application that sent the link; and annotation:(id)annotation is an additional function that we don’t fall into.

Now we have to host our xib. Enter xib (which should have the “OfflineReaderViewController” permission, but that doesn't matter with xib unless we call initWithNibName: (which we won't)) and make it look like the image below:

Screenshot of IB layout

It’s VERY important that you enter the UIWebView attributes and check “Scales Pages To Fit”, as this allows us to zoom in and out on web pages with tongs. Don't worry about the links until we create them.

Enter the OfflineReaderViewController header and paste the following:

 #import <UIKit/UIKit.h> @interface OfflineReaderViewController : UIViewController <UIDocumentInteractionControllerDelegate> { IBOutlet UIWebView *webView; } -(void)openDocumentIn; -(void)handleDocumentOpenURL:(NSURL *)url; -(void)displayAlert:(NSString *) str; -(void)loadFileFromDocumentsFolder:(NSString *) filename; -(void)listFilesFromDocumentsFolder; - (IBAction) btnDisplayFiles; @end 

Now .m:

 #import "OfflineReaderViewController.h" @implementation OfflineReaderViewController UIDocumentInteractionController *documentController; -(void)openDocumentIn { NSString * filePath = [[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"]; documentController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]]; documentController.delegate = self; [documentController retain]; documentController.UTI = @"com.adobe.pdf"; [documentController presentOpenInMenuFromRect:CGRectZero inView:self.view animated:YES]; } -(void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application { } -(void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application { } -(void)documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *)controller { } -(void) displayAlert:(NSString *) str { UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:str delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; [alert release]; } - (void)handleDocumentOpenURL:(NSURL *)url { [self displayAlert:[url absoluteString]]; NSURLRequest *requestObj = [NSURLRequest requestWithURL:url]; [webView setUserInteractionEnabled:YES]; [webView loadRequest:requestObj]; } -(void)loadFileFromDocumentsFolder:(NSString *) filename { //---get the path of the Documents folder--- NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *filePath = [documentsDirectory stringByAppendingPathComponent:filename]; NSURL *fileUrl = [NSURL fileURLWithPath:filePath]; [self handleDocumentOpenURL:fileUrl]; } -(void)listFilesFromDocumentsFolder { //---get the path of the Documents folder--- NSArray *paths = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSFileManager *manager = [NSFileManager defaultManager]; NSArray *fileList = [manager contentsOfDirectoryAtPath:documentsDirectory error:nil]; NSMutableString *filesStr = [NSMutableString stringWithString:@"Files in Documents folder \n"]; for (NSString *s in fileList){ [filesStr appendFormat:@"%@ \n", s]; } [self displayAlert:filesStr]; [self loadFileFromDocumentsFolder:@"0470918020.pdf"]; } - (IBAction) btnDisplayFiles { [self listFilesFromDocumentsFolder]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Release any cached data, images, etc that aren't in use. } #pragma mark - View lifecycle - (void)viewDidLoad { [super viewDidLoad]; [self openDocumentIn]; } - (void)viewDidUnload { [super viewDidUnload]; // Release any retained subviews of the main view. // eg self.myOutlet = nil; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; } - (void)viewDidDisappear:(BOOL)animated { [super viewDidDisappear:animated]; } - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { // Return YES for supported orientations return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown); } @end 

Those of you who are actively watching and not just copying everything that I tell you (just joking) will know that this line: [[NSBundle mainBundle] pathForResource:@"Minore" ofType:@"pdf"]; will give us SIGABRT because, well, the file does not exist! So, drag it into any shared PDF file that you pulled from anywhere (I recommend here because who does not spend their free time reading huge volumes of documentation?), Then copy its name and paste it with the removed suffix (.pdf) ; part ofType:@"pdf" will take care of this for us. The line should look like as soon as you finish: [[NSBundle mainBundle] pathForResource:@"//file name//" ofType:@"pdf"];

Now go back to xib and plug in those IBOutlets ! That's it, this is what your Owner File tab should look like:

Screenshot showing established connections

It seems we are done ... but wait! We did nothing to open the "Open in ..." menu! Well, it turns out that there is some kind of error in the .plist file. Open the .plist application (quick right-click, then select "Open As> Source Code") and paste the following:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>en</string> <key>CFBundleDisplayName</key> <string>${PRODUCT_NAME}</string> <key>CFBundleExecutable</key> <string>${EXECUTABLE_NAME}</string> <key>CFBundleIconFiles</key> <array/> <key>CFBundleIdentifier</key> <string>CodaFi.${PRODUCT_NAME:rfc1034identifier}</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>${PRODUCT_NAME}</string> <key>CFBundlePackageType</key> <string>APPL</string> <key>CFBundleShortVersionString</key> <string>1.0</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleVersion</key> <string>1.0</string> <key>LSRequiresIPhoneOS</key> <true/> <key>UIRequiredDeviceCapabilities</key> <array> <string>armv7</string> </array> <key>UISupportedInterfaceOrientations</key> <array> <string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeRight</string> </array> <key>UIFileSharingEnabled</key> <true/> <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeName</key> <string>PDF Document</string> <key>LSHandlerRank</key> <string>Alternate</string> <key>CFBundleTypeRole</key> <string>Viewer</string> <key>LSItemContentTypes</key> <array> <string>com.adobe.pdf</string> </array> </dict> </array> </dict> </plist> 

[Lateral note: be careful when cheating the source code of any plist, if you don’t know what you are doing, you may get the terrible "This file was damaged" error from Xcode]

If you need to right-click and select Open As> Property List, it will look like this:

Shot of Xcode plist editor window

There's still a VERY important field in the title "Application supports iTunes file sharing." This must be set to "YES" or your application will not be displayed in iTunes as support for file sharing.

The "Document types" field indicates the types of documents that our example can open. Expand the arrow to find its role and UTI. These are unique identifiers (unique type identifiers, it seems obvious what this abbreviation means, right?), What each kind of file has. UTI is what allows finder to replace the overall document image with this beautiful localized file type image (don’t believe me, rename the non-essential file extension to .ouhbasdvluhb and try to get a beautiful image!) If I wanted to open (say, a .code file) then I would put something like com.CodaFi.code (reverse DNS record for those who don’t have a hint) in the UTI field and the Document Type Name would be “Document CodaFi”. The rank and role of the handler should be understood, because our handler rank is alternative (because we don’t have a file), and our role is the viewer (because we don’t need anything more important. Our example is just a viewer, not an editor, therefore we will leave it as such.

For future use, UTI has official system-declared naming schemes when they come from reputable sources (Oracle, Microsoft, even Apple itself), which can be found in the Unified Name Type Reference Guide , but are listed here for the radiant.

Now, let run 'er! The code should be built without errors, assuming that you copied the shorthand and received these damned xib connections. Now, when you first start the application, you should be given the opportunity to open the document in iBooks. Deselect the real meat code opens other documents! Launch Safari and find any PDF file that Safari can open QuickLook or open. Then in the menu "Open in ..." our application will appear! Click on it. You will get a small switcheroo animation and a file location warning will appear. When you reject it, UIWebView download the PDF file. The Mail application has similar functionality with attachments. You can also call these PDF files for your application.

What is it all done. Enjoy and happy coding!

+97
Nov 05 2018-11-11T00:
source share

There is an excellent answer in this question. I have copied some of the answers below for clarity, but you should refer to this question for a complete answer.

File type handling is new with iPhone OS 3.2 and differs from existing custom URL schemes. You can register your application for processing certain types of documents, and any application that uses a document controller can transfer the processing of these documents to your own application.

To register support in your Info.plist you will need something like the following:

 <key>CFBundleDocumentTypes</key> <array> <dict> <key>CFBundleTypeIconFiles</key> <array> <string>Document-molecules-320.png</string> <string>Document-molecules-64.png</string> </array> <key>CFBundleTypeName</key> <string>Molecules Structure File</string> <key>CFBundleTypeRole</key> <string>Viewer</string> <key>LSHandlerRank</key> <string>Owner</string> <key>LSItemContentTypes</key> <array> <string>com.sunsetlakesoftware.molecules.pdb</string> <string>org.gnu.gnu-zip-archive</string> </array> </dict> </array> 

One of the UTIs used in the above example was systemic, but the other was the application UTI. UTI applications must be exported so that other applications in the system can be aware of this. To do this, you would add a section to your Info.plist as follows:

 <key>UTExportedTypeDeclarations</key> <array> <dict> <key>UTTypeConformsTo</key> <array> <string>public.plain-text</string> <string>public.text</string> </array> <key>UTTypeDescription</key> <string>Molecules Structure File</string> <key>UTTypeIdentifier</key> <string>com.sunsetlakesoftware.molecules.pdb</string> <key>UTTypeTagSpecification</key> <dict> <key>public.filename-extension</key> <string>pdb</string> <key>public.mime-type</key> <string>chemical/x-pdb</string> </dict> </dict> </array> 

This particular example exports the com.sunsetlakesoftware.molecules.pdb UTI with a .pdb file extension corresponding to the MIME type chemical/x-pdb .

In this case, your application will be able to process documents attached to emails or other applications in the system. In Mail, you can press and hold to open a list of applications that can open a specific attachment.

When the application is open, your application will be launched, and you will need to process the processing of this file in your delegate -application:didFinishLaunchingWithOptions: It appears that files downloaded this way from Mail are copied to your Documents directory under a subdirectory corresponding to the mailbox they arrived in.

+6
Nov 04 '11 at 17:05
source share



All Articles