iOS Development------Widget (today Extension) plug-in development

Source: Internet
Author: User

iOS10.0发布啦(貌似过去有点时间了吧 - -),在宏观带给我们使用体验的提升之外,更多的是带给iOS开发者一定的欣喜。因为我们又要学习新东西来适配10啦。


The widget (hereinafter referred to as the expansion of the application) is not IOS10 system new plug-in application (in fact, in the iOS8 already appeared, but the landlord is in the iOS10 released after the real concern it, it is ashamed of it). IOS10 before it was only in the notification column, as far as the hidden I will not say it. But it is not hard to see that Apple has increased its emphasis on iOS10 after being reborn and gaining a significant rise in status. Although the company app does not have an adaptation widget, it is not justified as a "post-knowledge" iOS developer, but not a study.

In order to avoid the actual situation and the blog post is not quite in line with the diagram, here is declared: The landlord with the IDE is the latest version of the Xcode8.0 (no way, or can't wait to upgrade 0.0), may be different version of the Xcode interface is not the same

All code for blog post: Https://github.com/RITL/WidgetDemo (if useful please star support, thanks)

Preview Map

Here is a preview of the widget demo: This will be slightly different, if you use Xcode7 and the previous version of the IDE compiled application (referred to as the host app), then find the Widget Method 1, if it is Xcode8 compiled host application, then you can directly through the 3D Touch evokes widgets, of course, through the first one is also possible. But the two are essentially the same.


Create Widget Extension

1. First create a new target:new->target,xcode8 will appear the following interface, select Today Extension, named Widgetextension:

2, created, the following folder will appear, the name of what is not the problem, generally created a good name for Todayviewcontroller, I just changed the name O (∩_∩) o

3, here wordy, although as an extension of the application, but these two applications are "independent" exist, you can also think that the expansion of applications and host applications is two completely separate applications, which means that in the development process there will be some shared issues, but the sharing problem is described in the following blog post. Before that, we had to go to developers to apply for app IDs and develop and publish certificates.

Because the landlord is just to learn, with the Xcode8 automatically manager signing, its role is to automatically generate ID and certificate.

The point is: if the development team does not have a corresponding app ID, Xcode will automatically generate the app ID; If the corresponding certificate is not created, it will automatically create the certificate (of course, during normal development, it is recommended to manually create the ID and configure the certificate)

4, the certificate is configured, run, add widgets, you can see our project has been equipped with the expansion of the widget function, the default is Maininterface.storyboard on the content: (I changed the word on the label, O (∩_∩) o)


Layout mode Interface Builder or coding

If you're involved in the way the UI is drawn, here's just a few things to tweak. In the demo, the landlord chooses to use the storyboard to complete the quick layout, of course, if the developer is accustomed to use the code to complete the layout, it is still possible. You need to do the following for info.plist files that extend your application:

Using Interface Builder

This is the default, if you modify the default storyboard, you only need to NSExtensionMainStoryboard change the value of the corresponding storyboard name can be

Using coding

First NSExtensionMainStoryboard Delete the field, add a NSExtensionPrincipalClass dictionary, and value is the class name of the master controller.

Use this method to not forget to set the Preferredcontentsize property sizing in Todayviewcontroller viewdidload.


Data sharing

A lot of times we need widgets to share some data with the host app, think of data sharing, if it's a single app, our approach is many, such as a single case, file, etc., but due to the expansion and hosting application is two completely separate apps, and the iOS app is based on the sandbox form, Therefore, the common method of sharing data is not to achieve data sharing, here you need to use the app Groups.

APP Groups

1, first need to register an app on the developer website Groups

2. Open the app groups in the host app and extension app and select the group that needs to share the data


Two ways to share data


Sharing data using Userdefaults

Nsuserdefaults Everyone should be very familiar with, the usual usage is

//获取UserDefaults的单例对象,完成对应用内相关数据的持久化储存[NSUserDefaults standardUserDefaults];

As previously mentioned, due to the sandbox mechanism, the expansion of the application is not allowed to access the host application sandbox path, so the above usage is not correct, need to be with the app group to complete the instantiation of Userdefaults, using the Userdefaults class for data sharing landlord package forRITL_ShareDataDefaultsManager

The code for instantiating the Userdefaults object through groups is as follows:

//组名privatestaticlet"group.com.yue.WidgetTest"/// 获得userDefualt对象privateclass func __userDefault() -> UserDefaults{    return UserDefaults(suiteName: RITL_ShareDataDefaultsManager.groupIdentifier)!}

The method of storing data is as follows, as to why there will be open keywords (the same as the public, but the development of the new API seems to be open in the document), because the landlord in the demo will separate the file, you need to implement the "different namespaces" code to share, So Swift's default internal scope appears to be under-privileged, as to how to detach the following will be mentioned:

//存放数据的键值privatestaticlet"com.yue.WidgetTest.value"/// 保存数据value : String){    //保存数据    __userDefault().set(value, forKey: RITL_ShareDataDefaultsManager.defaultKey)    __userDefault().synchronize()}

The way to get data is much like saving data:

/// 获取数据open class func getData() -> String!{    //如果值为nil,表示没有存过值,返回默认的值    letvalue = (__userDefault().value(forKey: RITL_ShareDataDefaultsManager.defaultKey))    __userDefault().synchronize()    valueelse {        returnvalueas! String    }    return""}

Because it is generated by a file, it is necessary to delete the stored data as follows:

/// 清除数据class func clearData(){    __userDefault().removeSuite(named: RITL_ShareDataDefaultsManager.groupIdentifier)    __userDefault().synchronize()}


Sharing data using FileManager

The second approach is essentially the same as the first, because they are all shared by creating a local file to complete the data, which is encapsulated in the demoRITL_ShareDataFileManager

The first difference is that it not only instantiates the object, but also gets the path to the saved data, as follows:

//Group namePrivate Static  LetGroupidentifier:string ="Group.com.yue.WidgetTest"//Storage pathPrivate Static  LetDatasavepathfile:string ="Library/caches/widgettest"///   get the stored pathPrivate classFunc __Filemanagersavepath()-url{//Get the path of the current group    varurl = filemanager.default. Containerurl (ForSecurityApplicationGroupIdentifier:RITL_ShareDataFileManager.groupIdentifier)//Return to the completed stitching pathUrl?. Appendpathcomponent (Ritl_sharedatafilemanager.datasavepathfile)returnurl!}

The method of saving the data, because some methods in Swift are throw exceptions, so the wording is slightly different, as follows:

/// 保存数据openvalue:String) -> Bool{    //进行存储    do {        tryvalue.write(totrue, encoding: String.Encoding.utf8)    catchas{//出错        returnfalse    }    returntrue}

The way to get the data is simply to read the stored data, of course, the demo is stored in a string, the method is implemented as follows:

/// 获取数据open class func getData() -> String{    //用于接收数据    varvalue : String    do {//读取数据        tryvalue = String(contentsOf: __fileManagerSavePath())    catchas NSError {        return""//有误输出空字符串    }    returnvalue}

Do not forget to clear the data when necessary:

/// 清除数据class func clearData() -> Bool{//其实不太规范,应该先判断是否存在该文件,再进行删除    do {//开始删除        try FileManager.default.removeItem(at: __fileManagerSavePath())    catchas NSError{        returnfalse    }    returntrue}


Code sharing

Why is there a code share here, if the above two stored classes are written in the host application directory, then the host application is not a problem, but, this time to expand the application is not to get these two classes, of course, if you write a set of each application is not possible, although this can solve the problem, But I am very difficult to use the perfect solution to describe him, because this will not only appear naming, not good maintenance and many other problems, serious time will bring a lot of problems, say not much, how to share code?

Using the framework

This problem can be solved with the framework perfectly after iOS8 (if someone asks iOS7 what to do?). Please wall 3 seconds, the widget is not iOS8 open to us 0.0)

1, as the creation of expansion, New->target, choose Cocoa Touch Framework to create a framework,demo in the name of a little bit, named Ritlkit

2. The code that needs to be shared is removed from the source project's compilation source and added to the Ritlkit

3, the framework will be created to link to the host project and expand the application of linked frameworks and libraries, do not forget to add, or you may not find the file problem

4, here is a hint, if the above steps completed, but in the extension or the hint can not find the file, then also need to do a step, is to add our framework to expand the application to Allow app extension API only Select, will be as follows:

5, after the completion of the steps, you should be able to expand and host the implementation of the code in the application can be shared, but also one point:

If it is a OBJC project, import the OBJC file, just use the #import"XX.h" import, but if the framework contains swift files, the use of #import "Project-Swift.h" import does not go into the project, you can use @import RILTKit; to create the framework compiled by the file to import, You can use the Swift file, which is also implemented in the demo.


Extension interacting with host applications

It is also an important means of interaction to open the Host app and implement the response operation by clicking the button on the widget.

1. First we need to add a URL in the host app's Target->info->url types schemes

2, through the widget to open the Host app, click the Widget button in the demo to jump to a different interface, the widget opens the host app operation as follows:

/// 打开我的App- (void)openMyApplication:(NSString *)title{    NSURL * url = [NSURL URLWithString:[NSString stringWithFormat:@"WidgetDemoOpenViewController://%@",title]];   [self.extensionContext openURL:url completionHandler:^(BOOL success) {}];}

3, the host app through the Appdelegate response OpenURL proxy method, receive information and send a notification to respond to the global:

/// -(BOOL) Application: (uiapplication*) App OpenURL: (Nsurl*) URL options: (nsdictionary<uiapplicationopenurloptionskey,ID> *) options{if([url. Schemeisequaltostring:@"Widgetdemoopenviewcontroller"])    {NSLog(@"host =%@"Url. Host);//Send notifications[[NsnotificationcenterDefaultcenter] postnotificationname:@"Extenicationnotification"Object:url. Host]; }return false;}

4, the host application in response to notify the controller to receive notifications, such as the demo is the main page to jump:

//添加获得拓展打开基础应用的通知[[NSNotificationCenter defaultCenter] addObserverForName:@"ExtenicationNotification" object:nil queue:nil usingBlock:^(NSNotification * _Nonnull note) {    //获得类型    NSString * type = note.object;    [weakSelf presentTextController:type];}];


Ncwidgetproviding protocol

How to look carefully, in fact, the widget controller and other controllers are no different, but it fulfilled a call "ncwidgetproviding" protocol. The Protocol method is not many, in the iOS10 added one, abandoned one, as follows:

// 这个就不用多说了吧,没有很难得单词哦0.0typedef NS_ENUM(NSUInteger, NCUpdateResult) {    NCUpdateResultNewData,    NCUpdateResultNoData,    NCUpdateResultFailed} NS_ENUM_AVAILABLE_IOS(8_0);/* 该方法是用来告知Widget控制器是否需要更新的一个协议方法 */- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult result))completionHandler;

For example, in order to avoid repeated refreshes in the demo, do the following:

- (void) Widgetperformupdatewithcompletionhandler: (void(^) (Ncupdateresult)) Completionhandler {//Perform any setup necessary in order to update the view.    //If An error was encountered, use ncupdateresultfailed    //If there ' s no update required, use Ncupdateresultnodata    //If There ' s an update, use Ncupdateresultnewdata    //Data acquisition    NSString* NewValue = [Ritl_sharedatadefaultsmanager getData];if([NewValue isequaltostring: Self. Textlabel. Text])//indicates no update{Completionhandler (ncupdateresultnodata); }Else//need to refresh{Completionhandler (ncupdateresultnewdata); }}
// iOS10 版本之后将不会再被唤起// 用来设置Widget控制器边框间距的方法,如果出现偏差,可以调整此方法的返回值进行操作- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets NS_DEPRECATED_IOS(810"This method will not be called on widgets linked against iOS versions 10.0 and later.");// iOS10 新增的方法// 用来设置Widget是展开还是折叠状态的方法,可以设置相关的preferredContentSizes属性修改大小- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize NS_AVAILABLE_IOS(10_0);


Time to save data

This depends on the specific needs, such as the demo is to choose when the host application will lose the active state when the data are saved, the implementation of the following:

    //Get a missed front-desk monitor[[NsnotificationcenterDefaultcenter] Addobserverforname:uiapplicationwillresignactivenotification object:NilQueueNilusingblock:^ (nsnotification* _nonnull Note) {//To save the data        //Save the current data#ifdef ritl_sharedatatype_userdefaults        //The first kind of saving data[Ritl_sharedatadefaultsmanager savedata:weakself. Maintextfield. Text];#else        ///second saving of data[Ritl_sharedatafilemanager savedata:weakself. Maintextfield. Text];#endif}];


Widget cannot expand collapse problem

2016-09-24 Supplement

Lost a bit before, there are also small partners asked, that is, according to the above way to develop plug-ins, can not fold the problem.

Solution:

//在TodayViewController的ViewDidLoad里面需要设置最大展示的类型#ifdef __IPHONE_10_0 //因为是iOS10才有的,还请记得适配    //如果需要折叠    self.extensionContext.widgetLargestAvailableDisplayMode = NCWidgetDisplayModeExpanded;#endif

More Welcome to download GitHub code together to delve ~3q

Thank you for your help with my blog.
The Widget for iOS development
WWDC session notes-Getting started with IOS notification hubs extension production

iOS Development------Widget (today Extension) plug-in development

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.