SkillAgentSearch skills...

WidgetKit

Compose native apps without a code using JSON and load them as NSBundle into another app dynamicly from local or remote locations.

Install / Use

/learn @m8labs/WidgetKit
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

WidgetKit for iOS   Tweet

Platform Twitter Carthage compatible

WidgetKit framework allows you to compose native apps without a code and load them as NSBundle into another app dynamicly from local or remote locations. No executables will be downloaded and loaded into memory, because widgets contain none of them. All logic and data flow are based on NSPredicate and NSExpression.

WidgetKit uses technique of mediating controllers, which were introduced in Cocoa Bindings (NSController and its descendants in AppKit for macOS). Although, this is not direct port of it. Read more about this here.

WidgetKit view controllers consist of predefined and 100% reusable objects (NSObjects), or mediating controllers, which control presentation of views inside their view controller. You put these mediator objects onto the view controllers' scene in the Interface Builder, set their properties via User Defined Runtime Attributes and connect outlets between them and your UI elements. Or, alternatively, you can load all this setup through corresponding JSON files, and that's what will be described in this document.

Features:

  • Bind UIKit elements to model's fields with data formatting;
  • Handle received JSON data and parse it directly to the CoreData (in the background);
  • Send forms (with media content) to any http server using plain UIKit controls;
  • Populate UITableView and UICollectionView with various types of data (JSON, array of NSObject or NSManagedObject);
  • Handle UIControl's interactions, including infinite scroll and pull to refresh;
  • Filter content in UITableView and UICollectionView via text input fields using predicates;
  • Delete content with respecting its ownership. You set ownership rules in CoreData Model Designer;
  • Propagate content object between view controllers on segue;
  • Control presentation of particular UI elements in the view controller when specific data changes;
  • Calculate views geometry in the background for faster scrolling;

WidgetKit is not a set of ready-to-use views and view controllers. Your UI is completly under your control.

Examples

  • WidgetDemo - main example, open it to follow explanations below.
  • WidgetHostDemo - widgets loader. This example is on the picture below and uses loading code similar to what is used in the beginning of the Usage section.

To install, download this repo, in Terminal go to the "Samples/***Demo" directory and run pod install command.

  • TwitterDemo - complex "real life" example with custom code integration. You can download it here.
<p align="center"><i>WidgetHostDemo.GIF</i></p> <p align="center"><img width="272" src="https://github.com/faviomob/WidgetKit/raw/master/Samples/Resources/WidgetHostDemo.gif"></p>

Installation

CocoaPods

To install via CocoaPods add this line to your Podfile:

pod 'WidgetKit'

Then run pod install command.

Carthage

To install via Carthage add this to your Cartfile:

github "faviomob/WidgetKit"

Run carthage update --platform iOS to build the frameworks and drag built "*.framework" files into your Xcode project. Then open Build Phases section of your target and add new Copy Files Phase by pressing "+" button at the top left corner. Choose Frameworks as destination and add all the frameworks you've just dragged into the project.

Usage

First, let's see how you can integrate external NSBundle to yout host application. Drag new UIView to your view controller and set its custom class to WidgetView. Create @IBOutlet for this view in your view controller. Also drag UIButton and create @IBAction for it. The whole setup should look like this:


import WidgetKit

class MyHostViewController: UIViewController {
    
    @IBOutlet var remoteWidgetView: WidgetView!

    @IBAction func downloadAction(_ sender: UIButton) {
        sender.isEnabled = false
        remoteWidgetView.download(url: "https://<address>/YourWidget.zip") { widget, error in
            sender.isEnabled = true
        }
    }
}

You will also need to set widgetIdentifier for the widget view so, that it starts with bundleIdentifier of your widget's NSBundle. As alternative to the listing above, you can just set downloadUrl property of your widget view, but you will not be able to track failure or success manually in this case. All these properties you can set in the viewDidLoad method or in the User Defined Runtime Attributes section of the Interface Bulder. You can add as many widgets to the host view as you want, but all of them should have different widgetIdentifier.

Building a Widget

Now, let's see how you can create widget app itself. The easiest way to understand how things work is to open the WidgetDemo sample project and run it.

Concepts

WidgetDemo project contains Main.storyboard file, CoreData Model file and a couple of JSON files: one for each view controller, and one for setting up your networking stack.

Let's look at the "FeedViewController.json" and "NewPostViewController.json" files, which contain setup for our view controllers.

To load this type of files, the view contoller itself should be of ContentViewController custom class or its descendant. Also, the restorationIdentifier should be set to the name of the JSON file (without extension).

There are four base types of mediator objects you can create in this file, and all of them are inherited from CustomIBObject which in its turn is descendant of the NSObject:

  • BaseContentProvider
  • BaseDisplayController
  • ActionController
  • ActionStatusController

For each particular purpose you create one of the descendants of these four major types. For example, for displaying data inside UITableView you should choose TableDisplayController, and for setting view controllers' content object (which will update all binded UI controls), you create ContentDisplayController. For fetching data from CoreData store you connect these BaseDisplayController objects to the ManagedObjectsProvider content provider.

ActionController is an object, that can call some network action that you describe in your networking JSON, or it can call some selector if you set target outlet to it. In case when selector not found, it will try to call network action with the same name instead. The moment, when action happens depends on the concrete descendant of the ActionController. For example, for handling buttons pressing you create ButtonActionController and connect your button to the sender outlet, and OnLoadActionController will be triggered after the viewDidLoad.

More close look on the networking you will find in the Networking section of this document, but in brief, you have one JSON file (actually two: development and production) with a section for each named action, where you put path, httpMethod, parameters, resultType and other attributes of the network call. All parameters are automatically taken and substituted from ActionController. Also you have one common section, where you store your baseUrl and other defaults. After the network request completes, its response will be automatically parsed into CoreData objects in the backgound. The name of the class of objects created you set in the resultType.

Each action controller contains ActionStatusController in its status variable. So you can bind to status.inProgress key path and update your labels and activity indicators accordingly. You can also have independent ActionStatusController object for situations when action was initiated outside of the current view controller. For example, after loading current user, an action of loading feed content will be called automatically as a chain call (read about chained network calls below). Thus, to track the state of this call you need to create separate ActionStatusController object.

Fetch & Display

Ok, enough theory, let's move to our example. If you open "FeedViewController.json" you will find two sections in the root node: objects and elements. The first one should contain mediator objects, and the second one contains bindings for UI elements. You can also use this section to set initial values for properties (see attrs) the same way you do it in the User Defined Runtime Attributes, but additionally arrays are supported.

The widget starts its work from loading current user. In this sample we omit the authentication part of the networking layer, and assume that we already authenticated. To know how to setup real networking with complex authentication process check the TwitterDemo sample app.

To load the current user when widget starts we need OnLoadActionController object:

"currentUserAction": {
    "type": "OnLoadActionController",
    "attrs": {
        "actionName": "currentUser"
    }
}

This will initiate the call to the endpoint named "currentUser" and, upon receiving response JSON, Account managed object will be created, becau

Related Skills

View on GitHub
GitHub Stars206
CategoryDevelopment
Updated4mo ago
Forks17

Languages

Swift

Security Score

97/100

Audited on Dec 2, 2025

No findings