Overview

Extension是iOS8新开放的一种对几个固定系统区域的扩展机制,它可以在一定程度上弥补iOS的沙盒机制对应用间通信的限制。

App Extensions使用限制

一个App Extensions不能有以下情况:

1.访问sharedApplication对象

2.使用任何标记NS_EXTENSION_UNAVAILABLE宏的API,或者类似的宏,或者不可用framework里面的API,例如HealthKit framework不能用于App extensions

3.iOS设备访问相机或者麦克风(iMessage app可以访问这些资源,只要在Info.plist里面进行配置使用描述即可)

4.运行一个长时间的后台任务(根据不同平台而异)

5.使用AirDrop接收数据

创建项目

注:Share Extension不能单独存在,必须依赖于主体工程。因此在此之前需要先创建个项目,如何创建项目这里不再叙述。

创建Share Extension
File->New->Targets->Share Extension

运行项目

现在我们可以运行项目看看效果了。iOS给我们提供了一个默认的UI样式,我们可以根据实际需求自定义UI和功能。

这里以简书为例
简书->任意文章打开->分享->更多

信息那一栏滑动就可以看到我们建的share extension了

点击我们创建的share extension 这就是默认效果

配置 share extension info.plist

Bundle display name

扩展的显示名称,默认跟你的项目名称相同,可以通过修改此字段来控制扩展的显示名称。

NSExtension

扩展描述字段,用于描述扩展的属性、设置等。作为一个扩展项目必须要包含此字段。

NSExtensionAttributes

扩展属性集合字段。用于描述扩展的属性。

NSExtensionActivationRule

激活扩展的规则。默认为字符串“TRUEPREDICATE”,表示在分享菜单中一直显示该扩展。可以将类型改为Dictionary类型,然后添加以下字段:

NSExtensionActivationSupportsAttachmentsWithMaxCount

NSExtensionActivationSupportsAttachmentsWithMinCount

NSExtensionActivationSupportsImageWithMaxCount

NSExtensionActivationSupportsMovieWithMaxCount

NSExtensionActivationSupportsWebPageWithMaxCount

NSExtensionActivationSupportsWebURLWithMaxCount

NSExtensionMainStoryboard

设置主界面的Storyboard,如果不想使用storyboard,也可以使用NSExtensionPrincipalClass指定自定义UIViewController子类名

NSExtensionPointIdentifier

扩展标识,在分享扩展中为:com.Apple.share-services

NSExtensionPrincipalClass

自定义UI的类名

NSExtensionActivationSupportsAttachmentsWithMaxCount

附件最多限制,为数值类型。附件包括File、Image和Movie三大类,单一、混选总量不超过指定数量

NSExtensionActivationSupportsAttachmentsWithMinCount

附件最少限制,为数值类型。当设置NSExtensionActivationSupportsAttachmentsWithMaxCount时生效,默认至少选择1个附件,分享菜单中才显示扩展插件图标。

NSExtensionActivationSupportsFileWithMaxCount

文件最多限制,为数值类型。文件泛指除Image/Movie之外的附件,例如【邮件】附件、【语音备忘录】等。

单一、混选均不超过指定数量。

NSExtensionActivationSupportsImageWithMaxCount

图片最多限制,为数值类型。单一、混选均不超过指定数量。

NSExtensionActivationSupportsMovieWithMaxCount

视频最多限制,为数值类型。单一、混选均不超过指定数量。

NSExtensionActivationSupportsText

是否支持文本类型,布尔类型,默认不支持。如【备忘录】的分享

NSExtensionActivationSupportsWebURLWithMaxCount

Web链接最多限制,为数值类型。默认不支持分享超链接,需要自己设置一个数值。

NSExtensionActivationSupportsWebPageWithMaxCount

Web页面最多限制,为数值类型。默认不支持Web页面分享,需要自己设置一个数值。

注意事项
1、使用storyboard创建UI
找到Share Extension的info.plist->展开NSExtension->新增key NSExtensionMainStoryboard value MainInterface(即storyboard名称)
如果没有修改,键值默认就存在

2、使用代码创建界面
找到Share Extension的info.plist->展开NSExtension->将NSExtensionMainStoryboard这行删掉

在NSExtension里新增key:NSExtensionPrincipalClass value: 自定义ViewController类名

自定义UI、自定义功能

创建完Share Extension时会默认创建一个ShareViewController的类,它继承于SLComposeServiceViewController

#import 
#import 

@interface ShareViewController : SLComposeServiceViewController

@end

我们在做自定义时可以把他删掉自己重新建个类或者直接拿默认的修改,这里仅拿默认的修改。

将ShareViewController的继承改成UIViewController,因为我们需要自定义UI,使用SLComposeServiceViewController无法显示自定义UI。

#import 
#import 

@interface ShareViewController : UIViewController

@end

获取别的app传过来的数据 NSExtensionContext是UIViewController里的属性,可以直接使用

// Returns the extension context. Also 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);

通过遍历的形式来获取我们需要的数据,这里获取URL

    __block BOOL hasGetUrl = NO;
    [self.extensionContext.inputItems enumerateObjectsUsingBlock:^(NSExtensionItem *  _Nonnull extensionItem, NSUInteger idx, BOOL * _Nonnull stop) {

        [extensionItem.attachments enumerateObjectsUsingBlock:^(id  _Nonnull itemProvider, NSUInteger idx, BOOL * _Nonnull stop) {

            if ([itemProvider hasItemConformingToTypeIdentifier:@"public.url"])
            {
                [itemProvider loadItemForTypeIdentifier:@"public.url" options:nil completionHandler:^(id  _Nullable item, NSError * _Null_unspecified error) {

                    if ([(NSObject *)item isKindOfClass:[NSURL class]])
                    {
                        NSURL * url = (NSURL *)item;
                        dispatch_async(dispatch_get_main_queue(), ^{

                            NSURLRequest * request = [NSURLRequest requestWithURL:url];
                            [self.webView loadRequest:request];
                        });
                    }

                }];

                hasGetUrl = YES;
                *stop = YES;
            }

            *stop = hasGetUrl;

        }];

    }];

退出Share Extension

NSError * error = [NSError errorWithDomain:@"GankIOExtension" code:NSUserCancelledError userInfo:nil];
[self.extensionContext cancelRequestWithError:error];

如果存在网络请求,且非https,请在info.plist里添加

NSAppTransportSecurity

    NSAllowsArbitraryLoads
    

剩下的该怎么玩就怎么玩,和正常的创建页面,处理逻辑一样

使用cocoapods

正常情况下我们是无法在Share extension里使用cocoapods的库的,所以我们需要配置一下。这样就可以在extension里使用cocoapods的库啦

代码共享

有时候我们想使用主体项目里的一些封装好的方法,例如category之类的,但是不能使用。

解决办法
1、拷贝一份代码到share extension(太sb)
2、使用framework来共享代码(推荐)

这里使用方法2来实现代码共享,具体如下:

1、创建framework
File->New->Targets-Cocoa Touch Framework

2、添加类到framework
我们选择需要共享的类,将其添加到framework里

选中.h文件

选中.m文件

找到framework文件夹里的.h文件,将共享的类的头文件添加进去

公开头文件

最最最重要一步
将framework添加到share extension里

请遵守app extensions使用限制

发布(此处有坑)

1、ERROR ITMS-90362: “Invalid Info.plist value. The value for the key ‘NSExtensionActivationRule’ in bundle xxxxx is invalid. Please refer to the App Extension Programming Guide on https://developer.apple.com”

NSExtensionAttributes的默认值是“TRUEPREDICATE”这是供给我们开发时使用的。我们发布时需要对该字段进行修改。

例如,要声明您的共享扩展可以支持多达十个图像,一个电影和一个网页URL,您可以使用以下字典来获取该NSExtensionAttributes键的值:

NSExtensionAttributes

    NSExtensionActivationRule
    
        NSExtensionActivationSupportsImageWithMaxCount
        10
        NSExtensionActivationSupportsMovieWithMaxCount
        1
        NSExtensionActivationSupportsWebURLWithMaxCount
        1
    

如果您不支持特定的数据类型,请使用0相应密钥的值或从NSExtensionActivationRule字典中删除密钥。

2、WARNING ITMS-90473: “CFBundleShortVersionString Mismatch. The CFBundleShortVersionString value ‘1.0’ of extension xxxxx does not match the CFBundleShortVersionString value ‘1.1.3’ of its containing iOS application ‘Gank.io.app’.”

这里要求extension的Version和Build版本号要与主体工程的版本号一致

参考资料

https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW8