Last year, I reviewed iOS 13 from a developer’s perspective. Back then, I wrote that iOS 13 is not only a big update is for users, but for developers as well. Due to the significance of the iOS 13 API’s that Power Player uses, I decided to take a slightly different approach and reflect on Power Player’s development during 2020, along with the challenges and possibilities.

 

1.3: Building the Home tab

Home has been built from scratch and features a more modular codebase that not only enables asynchronous data retrieval but also more and easier customizability. It is achieved by using Diffable Data Sources, Compositional Layouts and an in-house developed API for retrieving data.

 

Diffable Data Sources

The UICollectionViewDiffableDataSource, UITableViewDiffableDataSource and NSDiffableDataSourceSnapshot offer a new and simplified way to handle data within your app. Unlike the standard Data Source protocols, one no longer has to implement the numberOfSections(:), numberOfItems(inSection:) and cellForRow(atIndexPath:) functions. Additionally, using the batch update-functions are no longer necessary.

Instead, a cellProvider is used to fill the cells with data; the rest is handled by calling the Data Source’s apply(:) function and passing a snapshot.

Creating snapshots is pretty straightforward, though its items have to conform to the Hashable protocol. Additionally, each item has to be unique and duplicate hash values will be removed. Because the Home tab is very likely to contain the same items across different sections, I have created adapter objects that additionally combine their section number into the hash.

 

 

Compositional Layouts

With UICollectionViewCompositionalLayout, creating complex layouts has become simpler than ever before. Thanks to layout groups, items can have different sizes, thus making it easy to create layouts like those in the Photos app. Additionally, it is extremely easy to create orthogonal scrolling by adding the following code:

 

let section = NSCollectionLayoutSection(group: …)

section.orthogonalScrollingBehavior = .continuousGroupLeadingBoundary

 

While developing the Home tab, I wanted to standardize the layout for a future app-wide implementation. I did this by sublassing the layout and creating trait class based configuration items and factory objects. The sublassing was necessary to update the section spacing per trait collection and I hope Apple adds adaptive configurations in the future.

Other things I would like to see is the ability to decide per section if they should be placed on the same row and inline View Controller support like Power Player’s expanding albums on iPad an large landscape iPhones.

 

Combine and Data Controllers

Combine makes it easy to handle synchronous and asynchronous values over time. Thanks to its declarative nature, one’s code is both easier to read and handle. It even features a Promise mechanism that is found in JavaScript, called a Future.

Inspired by the NSFetchedResultsController, I have created an object that fetches, filters and sorts data and automatically refreshes it if desired. The data handling happens asynchronously and requests can be cancelled if desired.

The true power of this object lies in its components as it uses those to fetch and group data exactly the way the developer wants it. Filtering, sorting and refreshing by listening to Notifications can be done on a per-section basis. Additionally, a so-called proxy can be attached to pass the fetched data from a section to another data controller, thus reducing another request from Core Data or a web server.

Combine is used to chain the data handling and map the data. Additionally, I use the framework to fetch and map the dummy data that I use in my app screenshots.

 

MetricKit

With MetricKit, Apple provides developers an in-house solution to get more insight into app usage. Metrics are generated for the developer when the user has enabled “Share With App Developers” in the iOS privacy settings and are displayed in the Xcode Organizer. Although MetricKit is very easy to setup, the metrics have never appeared in the Organizer for me. The developer documentation also does not hint on calling a submit function though metrics can be exported to wherever the developer wishes by using the provided delegate methods.

 

Improving Scrolling Performance

Lastly, I have worked on the scrolling performance when loading artwork. It has been improved by adopting the UICollectionViewDataSourcePrefetching in combination with an OperationQueue. I am positively surprised by the improved scrolling performance on my iPad Air 2 and wish I had known about the OperationQueue earlier. Although I already was using a caching mechanism, the artwork was loaded by using DispatchQueue.main.async {…}.

 

Conclusion

Power Player’s Home tab has been completely rebuilt from the ground up by using the before mentioned API’s. By combining these API’s and using the knowledge I have learned in school, I have created cleaner and more modular code that will make it so much easier for me to add new functionality. Note that I have not used SwiftUI; I have decided to use UIKit because of the power of the Diffable Data Sources and Compositional Layout.

In the next chapter I will write about my first experience with SwiftUI.

 

iOS 14

The two biggest features in iOS 14 are the new home screen widgets and the sidebar. Additionally, MetricKit adds metrics for the CPU, crashes, exceptions and animation responsiveness and Apple has made improvements to in-app purchases testing.

 

Widgets and SwiftUI

Unlike the today widgets from iOS 8 to 13, iOS 14 widgets can be placed on the home screen, come in various sizes, can be added multiple times and are more configurable. The configurability is realized by utilizing the Intents framework, that with a little extra work, adds Smart Stack auto rotation, Siri Suggestions and Shortcuts support. Awesome!

Data can be imported using the Timeline and can one can choose to let the system update the data periodically, let the host app decide update times or both. Power Player uses the latter. When using Intents, a relevance score can be added. This score helps to determine when to update data and rotate the Smart Stack. Because Power Player does not have full control of the Music Library’s update cycle, the relevance score is calculated by comparing the Timeline date to the first item’s date (e.g. the last played date when the widget is configured to display recently played items).
Unlike the old widgets, some interactivity is lost. When tapping on interactive elements, you are taken straight into the accompanying app. One can deep link into the app using the Linker API. This is very easy to do and automatically adds opacity when the linked element is in the highlighted state.

 

Link(destination: URL(…)) {

    …

}

 

When users tap outside of linked elements, the app will be opened with a NSUserActivity with an Intent attached and is the same behavior as running a Shortcut or tapping a Siri Suggestion.

The new Widgets are SwiftUI only and it is a great opportunity to get familiar with the UI framework. During my brief time with it and the information I found online, I came to the conclusion that I am of two minds about it.

On one hand, it is very easy to create UI’s that are very easy to make accessible and one no longer has to worry about layout constraints. I also like that the padding modifier has a default value that will make apps more consistent. Additionally, using the Circle object in combination with the overlay modifier means one no longer has to update a CALayer’s corner radius on size changes.

On the other hand, SwiftUI has several weird quirks:

 

  • Asynchronous behavior: Although Apple does not provide asynchronous image support in both UIKit and SwiftUI, UIKit offers several advanced behaviors like the Prefetching Data Source and UIImageViews that do not require one to pass an image. However, SwiftUI does not have this prefetching as far as I am aware of and requires Image objects to have an image attached to them. Passing nil is not possible and if one passes an empty string, the console will give you a warning that the image could not be found. Property Wrappers might be a possible future solution in case one does not want to create a custom Image object although they are not supported on local properties at the time of writing this article.
  • Grid layouts: With the introduction of SwiftUI 2, Apple introduced the LazyGrid object that allows developers to create grid layouts without using a HStack, resulting in cleaner code. When implementing it in the widget, I did notice that the spacing property was only applied to vertical items.
  • Accessing settings: Along with grid layouts, SwiftUI 2 also introduces the AppStorage property wrapper. This wrapper makes it easy to access the UserDefaults of the desired bundle and is used throughout the Home widget. During development, I noticed that changed settings were not applied right away though this is probably by design in order to save battery life.
  • SwiftUI Canvas: While developing the widgets, I played around with the canvas a bit. Overall, it works fine but the 5 second render timeout is very annoying.

 

 

Although SwiftUI is still very young, I believe the framework has potential: Apple’s demo showed it is possible to create an app with just a few lines of code that run natively on all devices, albeit one that is not as customizable as UIKit. Considering the course Apple took with the Swift programming language, it is a good idea to embrace SwiftUI with caution. Future Power Player updates will therefore partly embrace SwiftUI components.

 

Sidebars and Split View Controllers

On iPad, navigation can be flattened by adapting the sidebar and can be made in both SwiftUI and UIKit.

For UIKit, the sidebar is based on the UISplitViewController and has a few tricks up its sleeve: it has the possibility to display a different View Controller in compact mode, a secondary column and a built-in keyboard shortcut to show / hide the navigation column. In order to create the sidebar’s main navigation, Apple recommends using the Collection View’s List Configuration and can be implemented entirely as a list or on a per-section basis and are built using the UICollectionViewCompositionalLayout, making it very flexible and powerful.

Additionally, items can be made collapsable by passing this information to the NSDiffableDataSourceSectionSnapshot. Although I am glad Apple created this class for more precise data management, the NSDiffableDataSourceSnapshot does not have the possibility to create collapsable items. Diffable Data Sources have gained reordering support and now support lazy loading of sections. Do note that this fetching happens on the main thread.

List items can be displayed using the new cell subclass UICollectionViewListCell. Additionally, UICollectionViewCell enables behaviors based on configuration:

 

  • UIListContentConfiguration: Contains information about content (e.g. text and images), appearance and layout.
  • UIBackgroundConfiguration: Contains information about backgrounds for cells, headers and footers.

 

Based on the state set in the Collection View Cell’s configurationState, the data from the before metioned objects is set accordingly. I always find it interesting when Apple implements new design patterns because it can help both experienced and less-experienced developers create more maintainable code.

Handling selected items is done by the UICollectionViewDelegate. Although this is logical, I would still like to see deeper integration with the UITabBarController, potentially saving developers from having to configure both the tabbar and sidebar.

In the session video, Apple talked about using using a protocol to update the state between collapsed and expanded Split View Controllers in the Shortcuts app. Although this is understandable considering the different hierarchies, it makes less sense for the Music and Photos app where the hierarchies are more similar.

I find this strategy a bit unlogical, considering more View Controllers equal more memory in use and possible additional loading times. I tried switching the View Controllers in the didExpand(:) and didCollapse(:) delegate methods but this only resulted in the following crash:

 

Mutating UISplitViewController is not allowed during a delegate callback.

 

Additionally, when no View Controllers are specified when collapsing and expanding, the following crash returns:

 

Unexpected view controller change in Secondary column for expanding UISplitViewController.

 

On the Apple Developer Forums I saw a Frameworks Engineer recommending using separate View Controllers for collapsed and expanding environments. At the time of writing, many tabs in Power Player are using the non-modular codebase. Due to that, the collapsing and expanding problems combined with the possible experience issues, I have decided to postpone implementing the sidebar.

 

In-app Purchases

Lastly, I would like to write about in-app purchases. In addition to the abilities to get insight into refunded purchases using the receipt and family shareable in-app purchases, the best new feature is the ability to test in-app purchases in Xcode.

This is a huge improvement because not only does this enable automated testing, purchases can also be removed, thus dramatically reducing the number of sandbox accounts necessary for testing.

It is set up by creating a StoreKit Configuration File and assigning that file to the desired scheme in the Scheme Editor. Do keep in mind that local testing does require a different certificate in order to access the receipt. 

 

Conclusion

iOS 14 refines several concepts introduced in iOS 13 (Diffable Data Sources, Data Source snapshots, Combine, SwiftUI, MetricKit). Additionally, Apple has made widgets more flexible and flattened navigation on iPad thanks to the sidebar. The new widgets are made using SwiftUI and it is a great chance for developers to get familiar with the framework. Although SwiftUI has some weird quirks, it definitely has potential, considering it is possible to create apps with a few lines of code that run on all Apple devices.

Overall, iOS 14 is a solid update for developers.

 

Wrapping Up

Power Player has received a new more modular codebase that both users as well as I as a developer will benefit from in the long run. Additionally, SwiftUI will slowly make its way into Power Player too, despite the weird quirks. Apple continues to provide new capabilities for developers to make more powerful apps and I am curious what Apple will come up with next.

 

Further reading: