App Extension allows us to extend the functionality of our app while users are using other apps.

Today Extension is also called widget. It allows important messages to reach your users faster. For example, users can check the weather, or stock prices, check calendars, and so on. According to apple’s official documentation, a widget should have the following features.

  • Make sure content is up to date
  • User events to respond to
  • Good performance (takes up a lot of memory on iOS and the system may kill the widget)

Create Today the Extension

Xcode -> File -> New -> Target -> TodayExtension

Just like creating a new project, once the Settings are created, there will be a Target in the project. Modify Scheme to run the Extension you just created, and you will see the widget in the Today notification center. It says “Hello World.”

Plus Xcode creates default template files for you.

  • TodayViewController. Swift (if the OC will be.h å’Œ .mFile)
  • MainInterface.storyboard
  • Info.plist

Note: The default is to use the storyboard as the entry point to the widget. If you don’t want to use the storyboard, you can remove the storyboard and put it in the info.plist

  • NSExtensionMainStoryboardtoNSExtensionPrincipalClass
  • MainInterfacetoTodayViewController

Set the interface

After completing the steps above, either you choose to use stroyBoard as the entry point to your widget or you choose to do it in code. It’s all the same.

For some unknown reason, all the articles I read on the Internet use code to do this. So in this article and the sample code that follows, you will use Xcode’s default storyboard for the layout of the widget.

I will solve the problem

  • Open the main app in the widget and pass the parameters
  • Widgets share data with the main app
  • Widgets share resources with the main app
  • Widget opening and collapsing

I met the pit

There’s nothing wrong with Today Extension, after all.

  • When testing, since the Widget and the main app are two different targets, printing the corresponding value in the AppDelegate when passing parameters has no effect. At first I thought the changes in the main app were invalid because the scheme was a widget. But that’s not the case. Display the parameter as alert, and you can see that the main app is actually running.

Let’s start with the preparation I did

In order not to pull so many useless things. Let’s start with something THAT I did that’s not really relevant to today’s topic.

Write the main app

In the main app I wrote a UITableView and used Userdefault to save the data I wanted to persist. And then I add and delete to the Todo list.

widget

I have the same UITableView in the widget that only has the view function.

Things to do

Widgets share resources with the main app

Widgets share code and resources with the main app. As an engineer, we have to think of the unchanging truth of high clustering and low coupling in everything we do. As much as possible, widgets share code with the main app.

There are two main schemes:

  • framework
  • Direct sharing

In the framework case, cocoapods is a new target, so you only need to add the corresponding code to your podfile to use the widget.

The other is direct sharing, and that’s easy. In my example, I have the main app and widget share an image and a TodoCell class (including xiB files). The only thing I did was to select the file in Xcode and then check the corresponding target in TargetMenberShip to the right of Xcode.

Widgets share data with the main app

Widgets and apps are technically two different apps, and the only way to share data between them is to use App Groups.

First up in the main app

target -> capabilities -> app groups

Open the app Groups function and click + to set the ID. Change one if it’s repeated.

widget app

target -> capabilities -> app groups

Then the group list can see the corresponding group. Check it.

At this point you have completed the prerequisites for the widget to share data with the main app.

The next thing we need to do is to adjust the Userdefault code in our preparation.

Will UserDefaults. Standard change

UserDefaults(suiteName: "your group id")
Copy the code

This can then be used in widgets

let userdefault = UserDefaults(suiteName: "group.com.sunny.group")
Copy the code

Got the data persisted in the main app. Other uses of app Groups can be further explored.

Collapse and expand widgets

Apple’s official documentation clearly states that the widget’s interface cannot be swiped. After all, widgets and notifications center swipes don’t conflict.

So sometimes we need to fold up the widgets because they are really annoying when they are too long.

So let’s just talk a little bit about what we did on iOS10, because there are no devices below iOS10.

In the TodayViewController’s didLoad

        // Add collapse button in iOS10
        if #available(iOSApplicationExtension 10.0, *) { extensionContext? .widgetLargestAvailableDisplayMode = .expanded }else {
// You need to add your own collapse button on iOS8 and iOS9
        }
Copy the code

Then implement the methods in the NCWidgetProviding protocol

    func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) {
// There is no such proxy on iOS8 and iOS9. You need to set target-Action for the buttons you add and then modify them
        switch activeDisplayMode {
        case .compact:
            preferredContentSize = maxSize
        case .expanded:
            preferredContentSize = CGSize(width: 0.0, height: 60 * CGFloat(dataSource.count))}}Copy the code

In iOS8 and iOS9, you don’t have this feature. We’ll just have to write a button and then do it.

Widget opens the main app

The widget opens the main app in the same way that openURL does, and then adds the required parameters to the URL.

The preparatory work

Main app -> Target -> Info -> UrlTypes

Add a URlType and set the URL Scheme to your custom string. Such as “sunny”.

Write this code in the widget where you want to jump

self.extensionContext? .open(NSURL(string: "sunny://action=\(dataSource[indexPath.row])")
Copy the code

Parameter passing is the same as above, concatenated in the URL. As mentioned earlier, widgets and apps can share data. It could also be a way of passing parameters.

At this time open the main app is directly into the main interface. What if we need to do something else?

Think back to wechat or Alipay, you had to write some code in the AppDelegate.

    func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
        let prefix = "sunny://"// Check whether it is from a reliable place
        if url.absoluteString.hasPrefix(prefix) {
        // The parameter is coming! Do the opposite
            let a = UIAlertController(title: url.absoluteString, message: nil, preferredStyle: .alert)
            a.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
            self.window? .rootViewController? .present(a, animated:true, completion: nil)
            return true
        }
        return false
        
    }
Copy the code

others

highly

The default height of widgets is limited.

Compact:

  • max = 110
  • mim = 110

Expanded under:

  • min = 110
  • Max = Varies according to different models.

No matter how you set it, it doesn’t go beyond this range, okay

widgetPerformUpdate
    func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
        // Perform any setup necessary in order to update the view.
        
        // If an error is encountered, use NCUpdateResult.Failed
        // If there's no update required, use NCUpdateResult.NoData // If there's an update, use NCUpdateResult.NewData
        
        completionHandler(NCUpdateResult.newData)
    }
Copy the code

This method is used to select whether the widget will refresh when it appears again.

notice

inNSExtensionContextSeveral notifications seen in TodayExtension do not appear to be for TodayExtension.

NSExtensionContext you can see several notifications that they’re all listening for the state of the host app. So for widgets, the host app is Today.

The last

This article uses Today Extension to do a very simple function. Of course, we can do more with him than that. This is where we need to unleash our ingenuity.

Sample code download link Because it was written using Swift, you find that it doesn’t compile well for well-known reasons. You can contact me, I will do the adaptation.