IOS extensions development (I) – Share Extension

1. what is extension?

Extension (Extension) is a very important new feature introduced in iOS 8. Extensions allow data interaction between app to become possible. Users can use the functionality provided by other applications in app without having to leave the current application. Before the iOS 8 system, each app was physically independent of each other, and app could not exchange private data with each other. After introducing extensions, other app can exchange data with extensions. Based on security and performance considerations, each extension runs in a separate process that has its own bundle, and the bundle suffix is.Appex. Extended bundle must be contained within the bundle of a common application.

The iOS 8 system has 6 systems that support extensions, namely, Today, Share, Action, Photo, Editing, Storage, Provider, Custom, keyboard. Extended system areas are also referred to as extension points.

Today Widget

For events such as stock, weather, express, and real-time information, a Today extension can be created in the Today view of the notification center. Today extensions, also known as Widget.

IOS extensions development (I) - Share Extension
Today extended renderings

Share

Before iOS 8, users only had several limited sharing options, such as Facebook, Twitter, etc.. If you want to share content with Pinterest, developers need some extra effort. In iOS 8, developers can create custom share options.

IOS extensions development (I) - Share Extension
Share extended renderings

Action

One of the most extensible extensions of action at all supported extensions. It enables you to convert content in another app context. Apple demonstrated a Bing translation action extension at the WWDC conference, which translates the selected text in Safari into different languages.

IOS extensions development (I) - Share Extension
Action extended renderings

Photo Editing

Before iOS 8, if you wanted to add a special filter to your picture, you would have to go into the third party app, which was pretty complicated. In iOS 8, you can directly use the third party app in Photos, such as Instagram, VSCO, cam, and Aviary, to provide Photo Editing extensions to complete the editing of the picture without leaving the current app.

IOS extensions development (I) - Share Extension
Photo Editing extended renderings

Storage Provider

Storage Provider allows management across multiple file storage services to become simpler. Similar to Dropbox, Google Drive and other storage providers through in iOS 8 provides a Storage Provider extension, app can directly use the extended retrieval and storage of documents no longer need to create unnecessary copy.

IOS extensions development (I) - Share Extension
Storage Provider extended renderings

Custom Keyboard

The Apple Corp introduced the touch-screen keyboard in 2007, but it hasn’t improved much. In this respect, Android has opened keyboard privileges to third party developers, so many excellent keyboard input methods like Swype and SwiftKey have appeared. In iOS 8, apple finally developed the keyboard rights to third party developers, the custom keyboard input method allows users to use the entire system range.

IOS extensions development (I) - Share Extension
Custom Keyboard extended renderings

The following is a new extension in iOS 9

Network expansion

Developers can expand the custom VPN client, transparent network proxy client and implement dynamic device side network content filtering through expansion.

Safari Extensions

This extension allows users to see your content via Safari’s shared links. Alternatively, provide a screen list that lets your users mask the content when using your App to browse Web content.

Spotlight extensions

This extension can index the data in App and can rebuild the data index without restarting the App.

Audio Unit extensions

This extension allows App to provide musical instruments, audio effects, and voice synthesis capabilities similar to those offered by GarageBand, Logic, and App.

2 turn to the question Share Extension

This article mainly discusses the development and application of Share Extension. The following example will be a comprehensive discussion and in-depth understanding of it.

2.1 create Share Extension extensions Target

Note: Extensions cannot be created separately. They must depend on application engineering projects, so if you haven’t created an application project yet, create one.

1, open the project settings, click the “+” number in the TARGETS sidebar to create a new Target, as shown in fig.:

IOS extensions development (I) - Share Extension
add Target

2, and then select “iOS”, “-&gt”, “Application Extension”, “-&gt”, “Share Extension”, and click “Next””. As shown:

IOS extensions development (I) - Share Extension
creates Share extensions

3, expand the name, fill in “Share”, and click “Finish””. As shown:

IOS extensions development (I) - Share Extension
fill out extended information

4, at this time will prompt to create a Scheme, click “Activate””. As shown:

IOS extensions development (I) - Share Extension

Well, the job of creating Share Extension here is finished. Next, you can compile and run first. This is a little different when you’re developing App. Because Extension requires Host App (host application) to run. So, XCode pops up the interface, so let’s select a iOS App to run Extension. As shown:

IOS extensions development (I) - Share Extension
selects host applications

Here, I chose the application XCode recommended by Safari, and then click Run to debug it. XCode starts Safari, as shown in fig.:

IOS extensions development (I) - Share Extension

You can see that the sharing button in the middle of Safari is grayed out. Don’t worry, you have to open a web page ^_^. We randomly open a web page, and you can see that the share button becomes active. Click the share button to pop up the sharing menu, as shown in the figure:

IOS extensions development (I) - Share Extension
running effect diagram

You can see just a Share extension has been displayed on the panel, if you did not find their own extensions, you can slide to the right menu, activate their expansion in the “more” option. As shown:

IOS extensions development (I) - Share Extension

We clicked on the sharing item created by ourselves to pop up a share window. As shown:

IOS extensions development (I) - Share Extension
sharing interface renderings

2.2. configuration Share Extension

Next, we need to give him some settings. We expand the Share directory on the left column of the XCode and find the Info.plist file. Such as:

IOS extensions development (I) - Share Extension
extends Info.plist

We only need to focus on the settings for the following fields:

Name Explain
Bundle display name The extended display name, which is the same as your project name, can control the extended display name by modifying this field.
NSExtension Extended description fields are used to describe extended attributes, settings, and more. This field must be included as an extension project.
NSExtensionAttributes Extended property collection field. Used to describe extended attributes.
NSExtensionActivationRule Activate extended rules. The default is string TRUEPREDICATE, indicating that the extension is always displayed in the share menu. The type can be changed to Dictionary type, and then add the following fields:
NSExtensionActivationSupportsAttachmentsWithMaxCount
NSExtensionActivationSupportsAttachmentsWithMinCount
NSExtensionActivationSupportsImageWithMaxCount
NSExtensionActivationSupportsMovieWithMaxCount
NSExtensionActivationSupportsWebPageWithMaxCount
NSExtensionActivationSupportsWebURLWithMaxCount
NSExtensionMainStoryboard Set the Storyboard of the main interface. If you do not want to use storyboard, you can also specify the custom UIViewController child class name using NSExtensionPrincipalClass
NSExtensionPointIdentifier Extended markup in the shared extension: com.apple.share-services
NSExtensionPrincipalClass Customize the class name of the UI
NSExtensionActivationSupportsAttachmentsWithMaxCount The attachment is limited to the numeric type at most. Accessories include File, Image and Movie three categories, a single, mixed selection of total quantity does not exceed the specified quantity
NSExtensionActivationSupportsAttachmentsWithMinCount The minimum number of attachments is numeric type. When setting NSExtensionActivationSupportsAttachmentsWithMaxCount comes into effect, by default, at least 1 attachments are selected, and the extension icon is displayed in the share menu.
NSExtensionActivationSupportsFileWithMaxCount File most limited to numeric type. Documents refer to attachments other than Image/Movie, such as [mailing] attachments, [voice memos], etc..

single and mixed selection does not exceed the specified quantity.

NSExtensionActivationSupportsImageWithMaxCount Most pictures are limited to numeric types. Single or mixed selection shall not exceed the specified quantity.
NSExtensionActivationSupportsMovieWithMaxCount Video most limited to numeric type. Single or mixed selection shall not exceed the specified quantity.
NSExtensionActivationSupportsText Do you support text type, boolean type, and default support?. Share [memorandum]
NSExtensionActivationSupportsWebURLWithMaxCount Web links are most limited to numeric types. Default does not support sharing hyperlinks. You need to set a value yourself.
NSExtensionActivationSupportsWebPageWithMaxCount Web pages are limited to numeric types at most. Default does not support Web page sharing, and you need to set a value yourself.

For different applications there may appear only allowed to accept certain types of content, so Share Extension cannot always appear in the share menu, because share content with different applications is not the same, which need to set the NSExtensionActivationRule field to decide whether to display the Share Extension. For example, if you just want to accept other applications and share links to your own applications, you can set them up through the following steps:

  1. Change the NSExtensionActivationRule field type from “String” to “Dictionary”.
  2. Expand the NSExtensionActivationRule field, create its child item NSExtensionActivationSupportsWebURLWithMaxCount, and set a limit to the quantity.

After adjustment, as shown in the following figure:

IOS extensions development (I) - Share Extension
Info.plist

2.3 processing data in Share Extension

In fact, in Share Extension, the default will have a data display of the UI interface. This interface inherits the type of SLComposeServiceViewController, such as:

@interface ShareViewController: SLComposeServiceViewController @end

The display effect is as follows:

IOS extensions development (I) - Share Extension
sharing interface

The top includes the title, cancel (Cancel) button and Post button. Then follow the left, is a text edit box, to the right is a picture display control. Then, each time the user clicks the Cancel button or the submit button, the following method is triggered:

Click the Cancel button * / / * * * - (void) didSelectCancel {[super didSelectCancel];} / * * * * click on the submit button - (void) didSelectPost is called after {/ / This the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments. the host that / Inform / we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context. [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];}

In these two methods, you can do some custom operation. In general, when the user clicks on the submit button when extended to do is to put the data out and put in a container (App and Containing program, although Apple opened Extension, but in iOS extension does not exist alone, to be submitted to the AppStore, the Extension must be included in the submission. A App and App implementation can not be empty, including the Extension App called Containing app. Extension will be installed as the Containing App is installed and uninstalled as the ContainingApp is uninstalled.) The shared data medium (including NSUserDefault, Sqlite, CoreData) needs to be supported by the AppGroups service to interact with the container program. The following sections describe this section in detail. Let’s first see how to get the data in the expansion.

In ShareExtension, UIViewController contains a context object like extensionContext:

@interface UIViewController (NSExtensionAdditions) < NSExtensionRequestHandling> the extension context. Also / / Returns acts as a convenience method for a view controller to check if it participating in an extension request. @property (nullable, nonatomic, readonly, strong) NSExtensionContext *extensionContext NS_AVAILABLE_IOS (8_0); @end

By manipulating it, you can get the shared data and return to the interface of the host application. We can look at the definition of extensionContext first.

NS_CLASS_AVAILABLE (10_10, 8_0) @interface NSExtensionContext: NSObject list of input NSExtensionItems / / The associated with the context. If the context has no input items, this array will be empty. @property (readonly, copy, NS_NONATOMIC_IOSONLY) NSArray *inputItems the host to; / / Signals complete the app extension request with the supplied result items. The completion handler optionally contains any work which the extension may need to perform after the request has been completed, as a background-priority task. The `expired` parameter will be YES if the system decides to prematurely terminate a previous non-expiration invocation of the completionHandler. Note: calling this method will eventually dismiss the associated view controller. - (void) completeRequestReturningItems: (nullable * NSArray) items (completionHandler: (void ^ __nullable) (BOOL expired) completionHandler); / / Signals the host to cancel the app extension request with the, supplied error, which should be non-nil. The userInfo of the NSError will contain a key NSExtensionItemsAndErrorsKey which will have as its value a dictionary of NSExtensionItems and associated NSError instances. (void) cancelRequestWithError: (NSError * error); / / Asks the host to open an URL on the extension's behalf (void) openURL: (NSURL * URL) (completionHandler: (void ^ __nullable) (BOOL success) completionHandler @end); / / Key in userInfo. Value is a dictionary of NSExtensionItems and associated NSError instances. FOUNDATION_EXTERN NSS Tring * __null_unspecified const NSExtensionItemsAndErrorsKey NS_AVAILABLE (10_10, 8_0); / / The host process will enter the foreground FOUNDATION_EXTERN NSString __null_unspecified const NSExtensionHostWillEnterForegroundNotification * NS_AVAILABLE_IOS (8_2); / / The host process did enter the background FOUNDATION_EXTERN NSString __null_unspecified const NSExtensionHostDidEnterBackgroundNotification * NS_AVAILABLE_IOS (8_2); / / The host process will resign active status (stop receiving events). The extension may be suspended FOUNDATION_EXTERN NSString __null_unspecified const NSExtensionHostWillResignActiveNotification * NS_AVAILABLE_IOS (8_2); / / The host process did become active (begin receiving events) FOUNDATION_EXTERN NSString * __nu Ll_unspecified, const, NSExtensionHostDidBecomeActiveNotification, NS_AVAILABLE_IOS (8_2);

The structure of NSExtensionContext is simple and contains one attribute and three methods. The instructions are as follows:

Method Explain
InputItems The array stores the NSExtensionItem array passed by the container application to NSExtensionContext. Each of these NSExtensionItem identifies a type of data. To get data, start with this property.
CompleteRequestReturningItems:
completionHandler:
Notifies the host that the extensions have completed the request. When this method is called, the extended UI closes and returns to the container program. The items is the data entry that returns the host program.
CancelRequestWithError: Notifies the host that the extension of the program has been canceled. When this method is called, the extended UI closes and returns to the container program. Error is the wrong description information.
NSExtensionItemsAndErrorsKey The error message keys corresponding to the userInfo attribute in NSExtensionItem.

The following classes define some notifications that are related to the behavior of the host program, which can be done accordingly when designing extensions. The instructions are as follows:

Notification name Explain
NSExtensionHostWillEnterForegroundNotification The host program will return the foreground notification
NSExtensionHostDidEnterBackgroundNotification The host program enters the background notification
NSExtensionHostWillResignActiveNotification The host program will be pending notifications
NSExtensionHostDidBecomeActiveNotification Notification of activation of host program

2.3.1 gets data from inputItems

InputItems is an array containing NSExtensionItem type objects. Well, to deal with the data inside, you have to understand the structure of NSExtensionItem first:

@interface NSExtensionItem: NSObject< NSCopying, NSSecureCoding> / / Title (optional) for the item @property (nullable, copy, NS_NONATOMIC_IOSONLY) NSAttributedString *attributedTitle (optional); / / content text @property (nullable, copy, NS_NONATOMIC_IOSONLY) NSAttributedString *attributedContentText (optional); / / Contains images, videos, URLs, etc. This is not meant to be an array of alternate data formats/types, but instead a collection to include in a social media post for example. These items are always typed NSItemProvider. @property (nullable, copy, NS_NONATOMIC_IOSONLY) NSArray *attachments (optional); / / dictionary of key-value data. The key/value pairs accepted by the service are expected to be specified in the extension's Info.plist. The values of NSExtensionItem's properties will be reflected into the dictionary. @property (nullable, copy, NS_NONATOMIC_IOSONLY) NSDictionary *userInfo @end Keys corresponding to properties; / / exposed on the NSExtensionItem interface FOUNDATION_EXTERN NSString __null_unspecified const NSExtensionItemAttributedTitleKey * NS_AVAILABLE (10_10, 8_0); FOUNDATION_EXTERN NSString __null_unspecified const NSExtensionItemAttributedContentTextKey * NS_AVAILABLE (10_10, 8_0); FOUNDATION_EXTERN NSString * __null_unspecified const NSExtensionItemAttachmentsKey NS_AVAILABLE (10_10, 8_0);

NSExtensionItem contains four attributes

attribute Explain
AttributedTitle Title。
AttributedContentText Content.
Attachments An array of attachments, including pictures, videos, links, and other resources, enclosed in the NSItemProvider type.
UserInfo A Keyvalue data structure. The attributes in NSExtensionItem are mapped in this property.

The NSExtensionItem property in the userInfo structure of the key is as follows:

Name Explain
NSExtensionItemAttributedTitleKey The title of the key
NSExtensionItemAttributedContentTextKey The contents of the key
NSExtensionItemAttachmentsKey Key accessories

As you can see from the above definition, in addition to the text content, other types of content are stored as attachments, and the attachments are enclosed in a type called NSItemProvider, defined as follows:

Typedef void (^NSItemProviderCompletionHandler) (__nullable ID < NSSecureCoding> item, NSError * __null_unspecified error); typedef void (^NSItemProviderLoadHandler) (__null_unspecified NSItemProviderCompletionHandler completionHandler, __null_unspecified Class expectedValueClass, NSDictionary __null_unspecified * options); / / An NSItemProvider is a high level abstraction for file-like data objects supporting multiple representations and preview images. NS_CLASS_AVAILABLE (10_10, 8_0) @interface NSItemProvider: NSObject < NSCopying> an NSItemProvider with a / / Initialize single handler for the given item. (instancetype) - initWithItem: (nullable ID < NSSecureCoding> item typeIdentifier: (nullable) NSString * typeIdentifier NS_DE) SIGNATED_INITIALIZER an NSItemProvider with load; / / Initialize handlers for the given file URL, and the file content. (nullable instancetype) - initWithContentsOfURL: (null_unspecified * NSURL) fileURL a load handler; / / Sets block for a specific type identifier. Handlers are invoked on demand through loadItemForTypeIdentifier:options:completionHandler:. To complete loading, the implementation has to call the given completionHandler. Both expectedValueClass and options parameters are derived from the completionHandler block. (void) registerItemForTypeIdentifier: (NSString *) typeIdentifier loadHandler: (NSItemProviderLoadHandler) loadHandler the list of; / / Returns registered type identifiers @property (copy, readonly, NS_NONATOMIC_IOSONLY) N SArray *registeredTypeIdentifiers YES if the item; / / Returns provider has at least one item that conforms to the supplied type identifier. (BOOL) hasItemConformingToTypeIdentifier: (NSString * typeIdentifier); / / Loads the best matching item for a type identifier. The client's expected value class is automatically derived from the blocks item parameter. Returns an error if the returned item class does not match the expected value class. Item providers will perform simple type coercions (eg. NSURL to NSData, NSURL to NSFileWrapper, NSData to UIImage) - (void) loadItemForTypeIdentifier: (NSString *) typeIdentifier options: (nullable NSDictionary *) options completionHandler: (nullable NSItemProviderCompletionHandler completionHandler); / / @end Common keys for the item provider options dictionary. FOUNDATION_EXTERN NSString __null_unspecified const NSItemProviderPreferredImageSizeKey * NS_AVAILABLE (10_10, 8_0); / / NSValue of CGSize or NSSize, specifies image size in pixels. @interface NSItemProvider (NSPreviewSupport) Sets a custom preview image handler / block for this item provider. The returned item should preferably be NSData or a file NSURL. @property (nullable copy, NS_NONATOMIC_IOSONLY, NSItemProviderLoadHandler) previewImageHandler NS_AVAILABLE (10_10, 8_0); / / Loads the preview image for this item by either calling the supplied preview block or falling back to a QuickLook-based handler. This method, like loadItemForTypeIdentifier:options: completionHandler:, supports im Plicit type coercion for the item parameter of the completion block. Allowed value classes are: NSData, NSURL, UIImage/NSImage. - (void) loadPreviewImageWithOptions: (null_unspecified * NSDictionary) options completionHandler: (null_unspecified NSItemProviderCompletionHandler) completionHandler NS_AVAILABLE (10_10, 8_0); / / Keys used in property list @end items received from or sent to JavaScript code JavaScript code passes / / If an object to its completionFunction, it will be placed into an item of type kUTTypePropertyList, containing an NSDictionary, under this key. FOUNDATION_EXTERN NSString __null_unspecified const NSExtensionJavaScriptPreprocessingResultsKey * NS_AVAILABLE (10_10, 8_0); / / Arguments to be passed to a JavaScript finalize Method should be placed in an item of type kUTTypePropertyList, containing an NSDictionary, under this key. FOUNDATION_EXTERN NSString __null_unspecified const NSExtensionJavaScriptFinalizeArgumentKey * NS_AVAILABLE_IOS (8_0); / / Errors / / Constant used by NSError to distinguish errors belonging to the NSItemProvider domain FOUNDATION_EXTERN NSString __null_unspecified const NSItemProviderErrorDomain * NS_AVAILABLE (10_10, 8_0); / / NSItemProvider-related error codes typedef (NS_ENUM NSInteger, NSItemProviderErrorCode) {NSItemProviderUnknownError = -1, NSItemProviderItemUnavailableError = -1000, NSItemProviderUnexpectedValueClassError = -1 100, NSItemProviderUnavailableCoercionError, NS_AVAILABLE (10_11, 9_0) = -1200,} NS_ENUM_AVAILABLE (10_10, 8_0);

NSItemProvider structure description

Method Explain
InitWithItem:typeIdentifier: The initialization method, item, is the data of the attachment, and typeIdentifier is the type identifier corresponding to the attachment, corresponding to the description of the UTI.
InitWithContentsOfURL: Initializes based on the file path that is set.
RegisterItemForTypeIdentifier:loadHandler: Customize the loading process for a resource type. This method is used primarily for custom resources, such as classes or file formats that you define. When you call the loadItemForTypeIdentifier:options:completionHandler: method, the definition of the loading process is triggered.
HasItemConformingToTypeIdentifier: Used to determine whether there is a resource specified by typeIdentifier (UTI). The presence returns YES, otherwise NO is returned.
this method combines loadItemForTypeIdentifier:options:completionHandler: usage.
LoadItemForTypeIdentifier:options:completionHandler: Load the typeIdentifier specified resource. Loading is an asynchronous process that triggers completionHandler when loaded.
LoadPreviewImageWithOptions:completionHandler: Load the preview picture of the resource.

Thus, the structure is shown in the following figure:

Hierarchy diagram of IOS extensions development (I) - Share Extension

To get the array provided by the host program, just focus on the use of the loadItemTypeIdentifier:options:completionHandler method. With this understanding, then the next step is to analyze and extract the data from the inputItems, where the didSelectPost method in the view controller is rewritten as a link share. Look at the code below:

- (void) didSelectPost BOOL hasExistsUrl {__block = NO; [self.extensionContext.inputItems enumerateObjectsUsingBlock:^ (NSExtensionItem * _Nonnull extItem, NSUInteger IDX, BOOL * _Nonnull stop) {[item.attachments enumerateObjectsUsingBlock:^ (NSItemProvider * _Nonnull itemProvider, NSUInteger IDX, BOOL * _Nonnull stop if ([itemProvider) {hasItemConformingToTypeIdentifier:@ "public.url"]) {[itemProvider loadItemForTypeIdentifier:@ "public.url options:nil completionHandler:". (id< NSSecureCoding> _Nullable item, NSError * _Null_unspecified error (if) {[ (NSObject *) item isKindOfClass:[NSURL class]] (NSLog) {@ share URL =% @ ", item);}}]; hasExistsUrl = YES; *stop = YES;}}]; if (hasExistsUrl) {*stop = YES;} / / This is called}]; after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments. Inform the host that we're / done so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context. / / [self.extensio NContext completeRequestReturningItems:@[] completionHandler:nil];}

The above example iterates over all the NSExtensionItem objects in the inputItems array of extensionContext, and then iterates through all the NSItemProvider objects in the attachments array from these objects. Matches the first attachment that contains the public.url identity (what resources to match and how many are available). Note: in the above code notes [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil]; this is the main line of code to the view controller is not shut down until the implementation of the corresponding treatment before calling the method, close to share in view. This will be explained in the following sections.

2.3.2 will share data to the container program

The above chapter describes how to get the content shared by the host application. Then, the next thing to do is to pass the content to the container program for the appropriate operation (for example, in a social application, you might publish a user dynamic for the shared content). By default, the application of iOS exists in a sandbox, which does not allow applications to interact directly with applications. To this end, apple offers a service called App Groups, which allows developers to transfer data between their applications via NSUserDefaults, NSFileManager, or CoreData. The following describes how to activate the App Groups service:

  • First of all, there is an independent AppID (with wildcards * AppID is not allowed to activate App Groups)
IOS extensions development (I) - Share Extension
uses AppGroup
  • Then open the Capabilities tab of the project configuration for the container application and activate the App Groups feature, as shown in the figure:
The IOS extensions development (I) - Share Extension
activates the AppGroup feature
  • Click the + number to add a App Groups, and click the OK button
IOS extensions development (I) - Share Extension
sets the Group name
  • Once created, XCode automatically adds the application to the new group. As shown:
The IOS extensions development (I) - Share Extension
container program enables AppGroup
  • When the above step is completed, the container program’s App Groups is set to complete. And then turn to the Share Extension plugin requires activation of App Groups services, with the same steps for setting up the container program, the only difference is that the plug-in does not need to create a new App Group, just added to the container program just created Group (here can be understood as what applications to realize data sharing, so they must be in the same Group.). As shown:
The IOS extensions development (I) - Share Extension
extension program enables AppGroup

At this point, the application and extension of the App Groups service have started, and now share the contents of the transmission operation. The following describes NSUserDefaults, NSFileManager and CoreData, the three way is how to implement data operations under App Groups:

  • NSUserDefaults: to set or access data from Group, you cannot use the standardUserDefaults method to obtain a NSUserDefaults object. The initWithSuiteName: method should be used to initialize a NSUserDefaults object, which is created by the name of Group SuiteName, and then use this object to achieve cross application data read and write code:
/ / initialize a NSUserDefaults object for the App Groups NSUserDefaults *userDefaults [[NSUserDefaults alloc] initWithSuiteName:@ = "group.cn.vimfung.ShareExtensionDemo"]; / / write data [userDefaults setValue:@ "value" forKey:@ "key"]; / / read data (NSLog @ "% @" [userDefaults, valueForKey:@ "key"]);
  • NSFileManager: by calling the containerURLForSecurityApplicationGroupIdentifier: method, you can get the shared directory of AppGroup, and then implement arbitrary file operations based on this directory. The code reads as follows:
NSURL *groupURL / / shared directory = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@ "group.cn.vimfung.ShareExtensionDemo"] to obtain packet; NSURL *fileURL = [groupURL URLByAppendingPathComponent:@ "demo.txt"]; / / [@ file "ABC" writeToURL:fileURL atomically:YES encoding:NSUTF8StringEncoding error:nil]; / / *str = [NSString stringWithContentsOfURL:fileURL read the file NSString encoding:NSUTF8StringEncoding error:nil]; NSLog (STR =% @, @ str);
  • CoreData: in fact, CoreData is based on NSFileManager to obtain shared directories, and later to achieve data sharing. That is, when you initialize CoreData, you first use NSFileManager to obtain the shared directory, and then specify the shared directory to store the directory of the data file (such as the stored SQLite file). The code reads as follows:
*containerURL = [[NSFileManager NSURL / share project defaultManager] containerURLForSecurityApplicationGroupIdentifier:@ "group.cn.vimfung.ShareExtensionDemo"] get group; *storeURL = NSURL [containerURL URLByAppendingPathComponent:@ "DataModel.sqlite"]; / / initialize storage scheduler NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@ "DataModel" withExtension:@ "momd"]; NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]; NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model]; [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL Options:nil error:nil]; / / create a controlled object context NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType]; [context performBlockAndWait:^{[context setPersistentStoreCoordinator: coordinator]}];

To facilitate the demonstration, NSUserDefault is used here to store the URL addresses directly. The code reads as follows:

/ * * * * click on the submit button - (void) didSelectPost BOOL hasExistsUrl {__block = NO; [self.extensionContext.inputItems enumerateObjectsUsingBlock:^ (NSExtensionItem * _Nonnull extItem, NSUInteger IDX, BOOL * _Nonnull stop) {[item.attachments enumerateObjectsUsingBlock:^ (NSItemProvider * _Nonnull itemProvider, NSUInteger IDX, BOOL * _Nonnull stop if ([itemProvider) {hasItemConformingToTypeIdentifier:@ "public.url"]) {[itemProvider loadItemForTypeIdentifier:@ "public.url options:nil completionHandler:^" (id< NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) { If ([(NSObject) item isKindOfClass:[NSURL class]] (NSLog) {@ share URL =% @ ", item); NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@" group.cn.vimfung.ShareExtensionDemo "]; [userDefaults setValue: ((NSURL) item).AbsoluteString forKey:@" share-url "]; / / mark is the new [userDefaults setBool:YES forKey:@ share" has-new-share}]}]; HasExistsUrl = YES; *stop = YES;}}]; if (hasExistsUrl) {*stop = YES;} / / This is called}]; after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments. Inform the host that we're / done so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context. [self.extensionContext completeRequestReturningItems:@[] / / completionHandler:nil];}

2.3.3 do share the prompt operation of the plug-in

By default, if the user clicks the Post button, the sharing interface disappears, and the user can continue to manipulate the host program. These all depend on NSExtensionContextd’s completeRequestReturningItems:completionHandler: method. Now, because of the inclusion of shared content in the didSelectPost method, since the attachment is an asynchronous process, you need to do some prompt on the interface. Otherwise, after the sharing interface disappears, because there is no operation hint, it will make the user mistakenly believe that the interface is stuck, but in fact, the content has not been processed. The next step is to optimize the prompt operations on the UI, as follows:

/ * * * * click on the submit button - (void didSelectPost) {/ / initialize UIActivityIndicatorView *activityIndicatorView loading animation = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; CGRectMake (activityIndicatorView.frame = (self.view.frame.size.width - activityIndicatorView.frame.size.width) / 2 (self.view.frame.size.height - activityIndicatorView.frame.size.height) / 2, activityIndicatorView.frame.size.width, activityIndicatorView.frame.size.height); activityIndicatorView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin UIViewAutor | EsizingFlexibleLeftMargin UIViewAutoresizingFlexibleRightMargin | | UIViewAutoresizingFlexibleBottomMargin; [self.view addSubview:activityIndicatorView]; / / [activityIndicatorView startAnimating] activation loading animation ShareViewController * theController; __weak = self; __block = BOOL hasExistsUrl NO; [self.extensionContext.inputItems enumerateObjectsUsingBlock:^ (NSExtensionItem * _Nonnull extItem, NSUInteger IDX, BOOL * _Nonnull stop) {[extItem.attachments enumerateObjectsUsingBlock:^ (NSItemProvider * _Nonnull itemProvider, NSUInteger IDX, BOOL * _Nonnull stop) {if ("[itemProvider hasItemConformingToTypeIdentifier:@ public.url") {[itemProvider loadItemForTypeIdentifi Er:@ "public.url" options:nil (completionHandler:^ id< NSSecureCoding> _Nullable; item, NSError * _Null_unspecified (error) {if [(NSObject) item isKindOfClass:[NSURL class]] (NSLog) {@ share URL =% @ ", item); NSUserDefaults *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@" group.cn.vimfung.ShareExtensionDemo "]; [userDefaults setValue: ((NSURL *) item).AbsoluteString forKey:@" share-url "]; Is used to mark the new sharing / / [userDefaults setBool:YES forKey:@ "has-new-share"]; [activityIndicatorView stopAnimating]; [theController.extensionContext completeRequestReturningItems:@[extItem] completionHandler:nil];}}]; hasExistsUrl = YES; *stop = YES;}}]; if (hasExistsUrl) {*stop = YES;}}]; if (! HasExistsUrl) {/ / completeRequestReturningItems:@[] directly from [self.extensionContext CompletionHandler:nil];}

2.4 container program to obtain shared data

The work of the plug-in has been basically developed, and then the container program gets the data and operates. Here is the processing code for the container program:

- (void) applicationDidBecomeActive: (UIApplication * application) {/ / UserDefaults NSUserDefaults share *userDefaults = [[NSUserDefaults alloc] initWithSuiteName:@ "group.cn.vimfung.ShareExtensionDemo"]; if ([userDefaults @ boolForKey: "has-new-share" ("NSLog") {@ New Share: [userDefaults% @ ", valueForKey:@" share-url "]); / / [userDefaults setBool:NO forKey:@ reset share logo the" has-new-share ";}}

To facilitate the demonstration, the new method is directly detected in the applicationDidBecomeActive: method in AppDelegate, and if so, links are made by Log printing.

At this point, the entire Share Extension development process has been completed.

Note 2.5 for AppStore

  1. The processing in the extension cannot take too long to block the main thread (recommended to be processed in the thread), or it could cause apple to reject your application.
  2. Expansion alone cannot be questioned, must be submitted with the AppStore audit program container.
  3. For the expansion and application Build Version container to be consistent, otherwise when the package will upload audit warning, causes the program to normal.

3. advanced research

3.1 extend the default sharing interface

In some cases, additional information is displayed in the sharing interface, or other options are used by the user. Such as: to share content to friends, to share the visible permissions of the content and so on. Then, the default sharing interface (SLComposeServiceViewController) provides the means to extend it. These methods are defined as follows:

#if TARGET_OS_IPHONE Configuration Item Support (account / pickers, privacy, selection, location, etc.) Subclasses should implement * / / / this, and return an array of SLComposeSheetConfigurationItem instances, if if needs to display configuration items in the sheet. Defaults to nil. (NSArray * configurationItems); / / Forces a reload of the configuration items table. / / This is typically only necessary for subclasses that determine their configuration items in a deferred manner (for example, in -presentationAnimationDidFinish). Do not need to / / You call this after changing a configuration item property the base class detects and; reacts to that automatically. (void) reloadConfigurationItems a configuration view controll; / / Presents Er. Typically called from a configuration item's tapHandler. Only one configuration view controller is allowed at a time. pushed view controller should / / The set preferredContentSize appropriately. SLComposeServiceViewController observes changes to that property and animates sheet size changes as necessary. (void) pushConfigurationViewController: (UIViewController * viewController); / / Dismisses the current configuration view controller. (void) popConfigurationViewController; #endif

The following is a description of the method

Method Explain
– (NSArray *) configurationItems; An array of type SLComposeSheetConfigurationItem, by default, the method returns a nil. If you want to add an extension, you can add a SLComposeSheetConfigurationItem object by rewriting this method. Some information about SLComposeSheetConfigurationItem will be presented below.
– – (void) reloadConfigurationItems; Reload the configuration item list, which triggers the call of configurationItems and refreshes the change content of the configuration item.
– – (void) pushConfigurationViewController: (UIViewController *) viewController; Displays a configuration related view controller. The method is designed in conjunction with our custom configuration item, which requires more detailed selection when clicking on a configuration item. You can use this method to realistically view a controller and configure it. Note: only one configuration view controller is allowed at a time.
– – (void) popConfigurationViewController; Closes a configured view controller.

Take a look at the SLComposeSheetConfigurationItem statement:

Typedef void (^SLComposeSheetConfigurationItemTapHandler) (void); / / Represents a user-configurable option for the compose session. allowing the user to / / For choose which account to post from, what privacy settings to use etc. SOCIAL_CLASS_AVAILABLE (NA, 8_0) @interface SLComposeSheetConfigurationItem: NSObject / / Designated initializer - (instancetype) init NS_DESIGNATED_INITIALIZER @property (nonatomic, copy); NSString *title displayed name of the; / / The option. @property (nonatomic, copy) NSString *value The current value/setting of; / / the option. @property (nonatomic, assign) BOOL valuePending is NO. set; / / Default to YES to show a progress indicator. Can be used with a value too. Called on the main queue when / the configurat Ion item is tapped. block should not keep / / Your a strong reference to either the configuration item, or the SLComposeServiceViewController, otherwise you'll end up with a retain cycle. @property (nonatomic, copy) SLComposeSheetConfigurationItemTapHandler tapHandler; @end

Its properties are described below:

attribute Explain
Title Configuration item header
Value Current configuration value.
ValuePending YES shows the value of the configuration when the display value position displays the loaded animation, NO.
TapHandler Click event handler for configuration item

The following will extend the UI by using these methods to add two configuration items to the plug-in: one is whether to share the configuration item publicly, which identifies a switch value. The other is the public permission setting item that is displayed when the open share switch is on. You can choose to share with everyone or friends. The code reads as follows:

- (NSArray *) configurationItems add configuration options {/ / To via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here. / / define two configuration items, were recorded for users to choose whether or not to open and open access, and then according to the configuration of the value of static BOOL isPublic static NSInteger = NO; Act = 0; NSMutableArray *items = [NSMutableArray array]; / / create open configuration item SLComposeSheetConfigurationItem *item = [[SLComposeSheetConfigurationItem alloc] init]; item.title = @ "open"; item.value = isPublic? @ @: "yes" "no"; __weak ShareViewController *theController = self __weak SLComposeSheetConfigurationItem; *theItem = item; item.tapHandler = ^{ IsPublic = isPublic; theItem.value = isPublic!? @ @: "yes" "no"; [theController reloadConfigurationItems];}; [items addObject:item]; if (isPublic) {/ / if publicly identified as YES, create a public rights configuration SLComposeSheetConfigurationItem *actItem = [[SLComposeSheetConfigurationItem alloc] init]; actItem.title = @ "public authority"; switch (ACT) {case 0: actItem.value = @ "all the people"; break case; 1: actItem.value = @ "friends"; break; default: break;} / / actItem.tapHandler = ^{share permissions settings popup selection field ShareActViewController *actVC = [[ShareActViewController alloc] init]; [theController pushConfigurationViewController:actVC]; [actVC onSelected:^ (NSIndexPath *indexPath) {/ / when choosing the complete exit selection interface and refresh configuration. Act = indexPath.row; [theController popConfigurationViewController]; [theController reloadConfigurationItems];}]; [items addObject:actItem];}}; return items;}

ShareActViewController implementation:

@interface ShareActViewController (<); UITableViewDelegate, UITableViewDataSource> @property (nonatomic, strong) void (^selectedHandler) (@end); @implementation ShareActViewController (void) viewDidLoad [super viewDidLoad] UITableView {*tableView = [[UITableView; alloc] = initWithFrame:self.view.bounds]; tableView.backgroundColor [UIColor clearColor]; tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; tableView.dataSource = self; tableView.delegate = self; [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@ "Cell"]; [self.view addSubview:tableView];} - (void) onSelected: (void (^) (NSIndexPath *indexPath) {self.selecte handler) DHandler = handler;} - (NSInteger) tableView: (UITableView *) tableView numberOfRowsInSection: (NSInteger) section {return 2;} - (UITableViewCell *) tableView: (* UITableView) tableView cellForRowAtIndexPath: (NSIndexPath * indexPath) {UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@ cell.backgroundColor = "Cell"]; [UIColor clearColor]; switch (indexPath.row) {case 0: cell.textLabel.text = @ "all the people"; break case; 1: cell.textLabel.text = @ "friends"; break default:; break return cell;};} - (void) tableView: (UITableView * tableView) didSelectRowAtIndexPath: (NSIndexPath * indexPath {if (self.selectedHandle) R) {self.selectedHandler (indexPath)}}

In the shared plug-in interface, the configurationItems method is rewritten, and then two configuration item properties are defined, namely whether the isPublic is publicly identified and act is publicly granted. The SLComposeSheetConfigurationItem configuration item is then created and the isPublic value is used to determine whether the open permission configuration item is created. Whether or not the public configuration is clicked changes the value of the isPublic, thereby displaying or hiding the open permission configuration. The click of the open permission configuration pops up a select TableView for selecting the given value and then returning to the share interface.

3.2 replace the default sharing interface in Share Extension

If you can’t satisfy your requirements by extending the SLComposeServiceViewController, you need to design a shared view controller to replace the default SLComposeServiceViewController.

  1. First, create a custom view controller, such as: CustomShareViewController.
  2. Then open the extended Info.plist file, remove the NSExtensionMainStoryboard attribute and add a NSExtensionPrincipalClass attribute point to CustomShareViewController (Note: there is no use of Storyboard so you want to delete this attribute), such as:
    IOS extensions development (I) - Share Extension
    Info.plist map
  3. Next, design and display the sharing view according to the actual needs.
  4. Then calling the extensionContext CustomShareViewController to control the extension of the submission and cancellation of operation (Note: due to the expansion into the ExtensionContext on the UIViewController category, therefore, each ViewController with the extensionContext attribute).

For simplicity of presentation, the following code will get to the URL by extensionContext after the show to share custom view Label, while also providing a submission and cancel button for users to share content operation. The code reads as follows:

- (void) viewDidLoad {[super viewDidLoad]; Do any additional setup after loading / the view. / / define a container to store view share content and operation of the two UIView *container [[UIView alloc] initWithFrame:CGRectMake button = ((self.view.frame.size.width - 300) / 2 (self.view.frame.size.height - 175) / 2, 300, 175)] = container.layer.cornerRadius; 7 container.layer.borderColor; lightGrayColor].CGColor = [UIColor; container.layer.borderWidth = 1; container.layer.masksToBounds = YES; container.backgroundColor = [UIColor whiteColor]; container.autoresizingMask = UIViewAutoresizingFlexibleTopMargin UIViewAutoresizingFlexibleLeftMargin UIViewAutoresizingFlexibleRightMargin UIViewAutoresizingFlexibleB | | | OttomMargin; [self.view addSubview:container]; / / Post and Cancel UIButton *cancelBtn definition button = [UIButton buttonWithType:UIButtonTypeSystem]; [cancelBtn setTitle:@ "Cancel" forState:UIControlStateNormal]; cancelBtn.frame = CGRectMake (8, 8, 65, 40); [cancelBtn addTarget:self action:@selector (cancelBtnClickHandler:) forControlEvents:UIControlEventTouchUpInside]; [container addSubview:cancelBtn]; UIButton *postBtn = [UIButton buttonWithType:UIButtonTypeSystem]; [postBtn setTitle:@ "Post" forState:UIControlStateNormal]; postBtn.frame = CGRectMake (container.frame.size.width - 8 - 65, 8, 65, 40); [postBtn addTarget:self action:@selector (postBtnClickHandler:) forControlEvents:UIControlEventTouchUpInsid E]; [container addSubview:postBtn]; / / define a UILabel *label share link tag = [[UILabel alloc] initWithFrame:CGRectMake (cancelBtn.frame.origin.y + 8, cancelBtn.frame.size.height + 8, container.frame.size.width - 16, container.frame.size.height - 16 - cancelBtn.frame.origin.y - cancelBtn.frame.size.height)]; label.numberOfLines = 0; label.textAlignment = NSTextAlignmentCenter; [container addSubview:label]; __block BOOL hasGetUrl / / get to share links [self.extensionContext.inputItems = NO; enumerateObjectsUsing Block:^ (NSExtensionItem * _Nonnull obj, NSUInteger IDX, BOOL * _Nonnull stop) {[obj.attachments enumerateObjectsUsingBlock:^ (NSItemProvider * _Nonnull itemProvider, NSUInteger IDX, BOOL * _Nonnull stop if ([itemProvider) {hasItemConformingToTypeIdentifier:@ "public.url"]) {[itemProvider loadItemForTypeIdentifier:@ "public.url" options:nil (completionHandler:^ id< NSSecureCoding> _Nullable; item, NSError * _Null_unspecified error) {if ([(NSObject) item isKindOfClass:[NSURL class]] (dispatch_async) {(dispatch_get_main_queue), ^{(label.text = (NSURL * item).AbsoluteString); }]});}; hasGetUrl = YES; *stop = YES;} *stop = hasGetUrl;}];}];} - (void) cancelBtnClickHandler: (ID) sender [self.extensionContext cancelRequestWithError:[NSError errorWithDomain:@ {/ / unshare "CustomShareError" code: NSUserCancelledError userInfo:nil]];} - (void) postBtnClickHandler: (ID sender) {/ / executive share content [self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];}

The effect is shown below:

IOS extensions development (I) - Share Extension
renderings

Finally, attach the Demo address