My Widgets App

Introduction

Shortly after the release of iOS 14 with changes related to widgets, we were given the task to create an application for styling and adding widgets for analog and digital clocks, individual and group speed dial contacts, weather forecasts, etc. The user interface was quite simple, and that was a great opportunity to try SwiftUI, so we decided to dive in and create the whole application using SwiftUI.

The created widget styles are displayed on the main screen by sections, small, medium, and large. There is also the option to add a new style for each size. The screen for creating a new widget style contains panels for each type of widget, where you can select a specific theme and then customize it by changing colors, fonts, or changing locations for weather forecast widgets, etc. The widget with contacts can be small – with one contact, medium – with 8 contacts, and large – with 16 contacts that can all be styled individually. During setting up the contact, the user goes through several steps, styling, selecting a contact from the device, and then enabling speed calling and sending messages. Created widget styles can be edited or deleted later. The user has the ability to create multiple widgets of any size, and display them on the home screen of the device. After creating from the Edit menu of the widget itself, it can select the previously saved widget style by categories, for each type of widget.

UIViewControllerRepresentable

When we started implementing an image picker to select an image from the gallery or to add an image directly from the camera, we assumed that there was a SwitUI built-in component that would make the implementation process easier for us. Instead, we came across UIViewControllerRepresentable. It was something new for us.

UIViewControllerRepresentable is used to create and use UIViewController in the SwiftUI interface. The protocol contains three public methods:

  • Updates view from SwiftUI

  • Returns configured UIViewController which will be presented

  • Returns the instance used to communicate between the controller and SwiftUI. It is set as a controller delegate that will be created and presented later in SwiftUI

We used UIViewControllerRepresentable for the purpose of uploading a photo from the gallery or directly from the camera. In this case, we needed a coordinator to whom the image selection methods would be delegated from the UIImagePickerControollerDelegate protocol. We used the same coordinator in both cases.

ViewModifier

SwiftUI contains several modifiers for font, background, etc. which can be used to create specific modifiers. We took advantage of this benefit to create a modifier for a specific font that was used as the main font in the application. We set the font size, color, and weight using the modifier:

  • Here we have implemented view customization

We’ve also added an extension on View, as a small wrapper around the modifier:

Animations

Using SwiftUI greatly simplifies the use of animations. By combining and editing animations, we can quickly create both, simple and more complex animations. During the creation of our loading indicator, we encountered a problem where the animation, except for animating loading indicator behavior, also animates its displaying, which caused the loading indicator to constantly bounce after opening and/or closing the keyboard, for example. It took a while until we realized that the .animation() modifier animates all changes of the view to which it was added, while the withAnimation() call is used for specific animations. So in this call, we set the Bool flag which triggers the animation.

Core Data

As for CoreData, cool additions are the environment support and property wrappers that have been added.
There’s a managedObjectContext key in the environment with the purpose to store Core Data managed object context:

There’s a @FetchRequest property wrapper that is used for fetching entity items of specific entity type and sorting by specific criteria and order. So our sorted by creating time items wrapped property looks like:

Navigation

UINavigationController is replaced with NavigationView with NavigationLinks. For us, it was a change that we initially thought would be easy to overcome. As already mentioned, NavigationView contains NavigationLinks. Furthermore, NavigationLink is defined by a label that is actually a visual representation of the link, and the destination – view to which the link leads. NavigationView is a view for presenting a stack of views, a UIKit’s UINavigationController equivalent in SwiftUI. But we just missed the ability to manipulate that stack, and we realized that at the moment we needed to open screens deeper in the app and then return.

Specifically, the application goes through the screens for setting the contact data and at moment when the setting is completed, the user needs to be redirected to the main screen. After a little research, we realized that this whole concept is quite different from what we encountered using UIKit’s UINavigationViewController. The trick was that we had to propagate the data-binding from the root view, through all the views, to the final child view in order to be able to jump back to the root view. The same binding property is set for the isActive NavigationLink attribute of the root view. Also, the modifier isDetailLink is set to false, which means that the destination view will always be pushed onto the navigation stack, and thus can always be popped off. Finally, in the final view, the data binding property is set to false, which makes the user pop to root.

And that’s it. It’s not that complicated when you get used to it, but it can still be a little messy when you have more paths from the root view to some child views.

Conclusion

All in all, it was an interesting journey. In the end, the impression is divided. SwiftUI is insufficient for the realization of more complex applications, but a very useful framework for creating the UI part of the application, especially the view which is not much complicated. Can be expected to become the first option for creating a user interface very soon.

in by