iOS开发之App Extension(应用扩展)之 -- Today Extension

最近在做一个记事类的App,其中有一个想实现的功能就是希望能够在通知中心查看自己的一些记事内容。正好也是之前写了一篇关于应用扩展的介绍之后,一直再忙,一拖到现在也好久了。所以这两天把这个功能完成之后,就把这篇文章补上了。

因为我自己对应用扩展一直也不是很熟悉,只是有一个了解,在这次完成这个通知中心扩展之后,有很多点我觉得对于学习应用扩展是很重要的,所以为了以后在做其他扩展的时候能够有个查阅的资料,也算是自己的一个记录了吧。

对于扩展的一些介绍,我之前写过一篇文章,如果对iOS中的App Extension还不是很了解的可以先看一下集中常见扩展的介绍:
iOS开发之App Extension (应用扩展)- 入门介绍篇

这篇文章我主要介绍几点,如果有只想了解某一个方面的,可以跳着看。

1. 如何创建Today Extension
2. 如何在扩展和宿主App之间共享数据
3. 如何在扩展和宿主App之间共享代码(framwork创建)
4. 如何从扩展中打开宿主App

最后完成的是一个非常简单的记事demo,效果图如下:

效果图.gif

1. 如何创建Today Extension

首先,我们先创建一个最基本的项目,项目创建完成之后,选中项目文件,选择 xcode ->Editor ->Add Target,如下图,选中Today Extension项,然后点击Next,命名(本文中为TodayWidget),在弹出框中选择Activate,激活这个scheme。

创建Target.png
创建Target.png

激活之后,项目中就会多出一个TodayWidget的扩展,还有如下图左侧的TodayWidget文件夹。


TodayWidget.png

文件夹中的MainInterface.storyboard和TodayViewController这个类就是我们要在通知中心显示的界面的控制器。你可以点开storyborad看一下,里面已经有一个很小的界面,其中包含了一个label,如果你选择这个viewcontroller,将它的高度调高,然后将其中label的文字颜色调一下

运行Extension.png

如上图,选中TodayWidget为Target后直接运行,你就会看到下面的界面(如果开始没有出现这个界面,不是项目有问题,可以来回滑动切换到首页和这个页面,多试几次,因为它需要刷新,真机上如果不显示,可能是因为扩展的Target中 iOS Deployment Target默认的都是最高,需要调到与真机一致或者更低)

模拟器直接运行extesnion.png

到这里,一个最简单的通知中心扩展已经完成了,是不是感觉很简单,很酷炫。

2. 如何在扩展和宿主App之间共享数据

现在让我们来完成这个简易记事app最基本的功能,由于这个app非常简单,一个简单的tableview展示数据,一个简单的新增记事页面来添加一条记事,所以我就不详细写了,最后的效果如下。

简单记事.gif

唯一要提出的一点是,因为这个是简易记事,所以我也没有用到CoreData,sqlite之类的数据库,而是直接使用的NSUserDefaults来存储,这个也是我后面要介绍的如何共享数据。

记事功能完成之后,我们现在要做的就是如何在App中新增一条记事之后,在通知中心扩展中可以查看到这条记事,甚至当我们程序退出之后,可以在通知中心扩展中查看到我们最近的记事。

扩展与宿主App之间共享数据有两种方式:
1.通过NSUserDefaults,这也是本文介绍的方式
2.通过一个扩展与App都可以访问的共享容器,来存放文件,数据(Core Data, Sqlite等都可以存放在这个共享的容器中)。

首先,我们需要创建一个app group,如下图,选中项目的Target -> Capabilities -> App Groups,打开,如果你以前创建过group,会自动列出来。选择+号,填入group的名称(复制这个名称,因为后面要用到)

创建Group.png
group名称.png

创建完成之后,选择扩展的Target,打开,这次直接选择我们刚才所创建的group,如下图。

extension选择group.png

在group创建完成之后,项目中会多出两个文件,如下图

创建group生成的文件.png

在我们新增一条数据之后,我们将这条数据保存到共享的NSUserDefaults中去

   if (_noteField.text.length > 0) {
    //先查找是否已存在数据 suiteName就是我们创建的group名称
    NSArray *myNote = [[[NSUserDefaults alloc] initWithSuiteName:@"group.todayextension.widget"] valueForKey:@"MyNote"];
    NSMutableArray *note = [NSMutableArray arrayWithArray:myNote];
    if (!note) {
        note = [NSMutableArray arrayWithCapacity:0];
    }
    [note insertObject:_noteField.text atIndex:0];
    //存入数据
    [[[NSUserDefaults alloc] initWithSuiteName:@"group.todayextension.widget"] setValue:note forKey:@"MyNote"];
    [self.navigationController popViewControllerAnimated:YES];
}

然后在TodayViewController中,我们需要从这里面取数据

[_noteList removeAllObjects];
NSArray *myNote = [[[NSUserDefaults alloc] initWithSuiteName:@"group.todayextension.widget"] valueForKey:@"MyNote"];
if (myNote) {
    [_noteList addObjectsFromArray:myNote];
}

这样就已经将数据存在一个可共享的地方,宿主App和扩展都是从一个地方取数据,现在我们在App中添加一条记事,然后就可以下拉打开通知中心查看了

显示更多.png

如果需要存储更多的数据,可以通过文件或者数据库(Core Data, Sqlite等)。这个时候共享数据的方法就是要创建一个共享的文件夹

NSURL *groupURL = [[NSFileManager defaultManager]  containerURLForSecurityApplicationGroupIdentifier: @"group.todayextension.widget"];

通过上面的方法,扩展和App就都可以访问这个共享的文件夹了,将数据库,文件等存储在这个文件夹中,也同样的达到数据共享的目的。

3. 如何在扩展和宿主App之间共享代码(framwork创建)

在我们这个App中,记事列表的cell相对比较简单,但是如果我们需要使用更复杂的cell,而一般在通知中心扩展中,我们的UI有很大部分是跟App相同的,但是我们的扩展和App并不在一个Target当中,这也就代表着同样的代码,在扩展和App中我们需要分开两份,这样显然是很不合理的一种方式。所以,很多时候,我们需要在扩展和App之间共享代码,也就是将所有公用的文件添加到一个framework中,这样我们只要在扩展中引入这个framework,就可以使用其中的代码了。

在这个简单的记事App中,并没有十分复杂的自定义cell,为了解释如何共享代码,我将App中列表的datasource和扩展中列表的datasource抽出来成为一个NoteListDelegate类,因为扩展和App都是同一份数据,所以我只要在这个NoteListDelegate类中实现一次tableview的datasource代理方法,然后生成一个framework,在App和扩展中引入就可以同时使用了。

首先,我们创建一个framwork,取名TableKit,如下图


创建framework.png
framework1.png
framework2.png

选中.h文件,右侧Target Membership选中我们所创建的framwork,如下图


framework3.png

选中.m文件,右侧Target Membership先取消TodayExtension的Target,然后选中我们所创建的framwork,如下图


framework4.png

如果你有许多公用的类想要生成framwork,按照上述的步骤,将所有的文件Target都加入到所创建的framwork就行了。

完成上述步骤之后,就可以在扩展和App中引入TableKit,使用其中的类了。

多说一句:
如果需要共享资源文件,比如图片什么的,可以像上面一样选中文件,在右侧Target Membership中勾选中Extension的Target,就可以了。

4. 如何从扩展中打开宿主App

一般在扩展中,我们会直接与宿主App进行通信,比如本文中的记事App,我需要在扩展中直接能添加新的记事,或者点击扩展中的某条记事直接打开我们的App。
通过别的App打开我们自己的App,我们需要先设置URL Types,如下图,添加一个新的URL Type,然后设置URL Schemes,命名为todaywidget,通过这个Schemes,就可以在别的App中调用下面方法:

 [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"todaywidget://"]];

来打开这个App了。

URL Types

但是很遗憾的是,扩展不是一个完整的程序,所以它并没没有[UIApplication sharedApplication] 这个对象。
所以Apple给每个UIViewController加了一个extensionContext属性,在我们的宿主App中,这个属性是nil,而在扩展中,我们就可以通过下面的方法:

//打开首页
[self.extensionContext  openURL:[NSURL URLWithString:@"todaywidget://home" ]completionHandler:nil];
//打开添加新记事页面
[self.extensionContext openURL:[NSURL URLWithString:@"todaywidget://add"] completionHandler:nil];

来打开宿主App。

然后我们可以在AppDelegate的openURL代理方法中,通过URL后面的参数不同,来打开不同的页面,下面是直接打开 “添加新记事的页面”

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {

if ([url.absoluteString hasPrefix:@"todaywidget"]) {
    if ([url.absoluteString hasSuffix:@"add"]) {//判断是否是直接跳入到添加页面
        UIViewController *addVC = [[UIStoryboard storyboardWithName:@"Main" bundle:nil] instantiateViewControllerWithIdentifier:@"AddVC"];
        UINavigationController *rootNav = (UINavigationController*)self.window.rootViewController;
        [rootNav pushViewController:addVC animated:YES];
    }
}
return YES;

}

Demo代码下载

总结:

现在iOS中的扩展越来越多,以后肯定会接触越来越多的。通知中心的扩展相对来说是比较简单的,所以这篇文章中我没有介绍太多的原理,重点是在流程上面了。如果需要对原理了解的更多,可以去网上查一下资料。后面我应该还会补充更多类型的扩展介绍,但是最近比较忙,希望能够尽快吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 156,423评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,339评论 1 289
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,241评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,503评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,824评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,262评论 1 207
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,615评论 2 309
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,337评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,989评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,300评论 2 240
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,829评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,193评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,753评论 3 230
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,970评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,708评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,295评论 2 267
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,207评论 2 258

推荐阅读更多精彩内容

  • iOS8.0加入了扩展,iOS10苹果又增加了很多扩展。在今后,程序中会集成越来越多的扩展功能。 今天主要来模仿1...
    fou7阅读 12,373评论 7 47
  • iOS10.0发布啦(貌似过去有点时间了吧 - -),在宏观带给我们使用体验的提升之外,更多的是带给iOS开发者一...
    RITL阅读 2,532评论 16 12
  • 厚云顿起星天外, 虫失踪影鸟失歌。 阑风万里飒飒雨, 斜戏伞下匆匆客。 庭前苍松显苍翠, 阁中微子起微节。 凭轩遥...
    海曲三少阅读 356评论 20 3
  • [玫瑰]分享一首小诗给自己 你靠什么谋生, 我不感兴趣; 我只想知道,你渴望什么, 你是否有勇气追逐心中的渴望。 ...
    A颖岚阅读 546评论 0 2
  • 你失去过一个很爱的人吗?这种感觉无疑打碎了一颗门牙,浑身不自在,连饭也吃不下,往事还历历在目,你的爱人就突然从你的...
    会红的眼阅读 2,975评论 83 98