Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds
Arrow up icon
GO TO TOP
Mastering iOS 18 Development

You're reading from   Mastering iOS 18 Development Take your iOS development experience to the next level with iOS, Xcode, Swift, and SwiftUI

Arrow left icon
Product type Paperback
Published in Nov 2024
Publisher Packt
ISBN-13 9781835468104
Length 418 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Avi Tsadok Avi Tsadok
Author Profile Icon Avi Tsadok
Avi Tsadok
Arrow right icon
View More author details
Toc

Table of Contents (20) Chapters Close

Preface 1. Part 1: Getting Started with iOS 18 Development FREE CHAPTER
2. Chapter 1: What’s New in iOS 18 3. Chapter 2: Simplifying Our Entities with SwiftData 4. Chapter 3: Understanding SwiftUI Observation 5. Chapter 4: Advanced Navigation with SwiftUI 6. Chapter 5: Enhancing iOS Applications with WidgetKit 7. Chapter 6: SwiftUI Animations and SF Symbols 8. Chapter 7: Improving Feature Exploration with TipKit 9. Chapter 8: Connecting and Fetching Data from the Network 10. Chapter 9: Creating Dynamic Graphs with Swift Charts 11. Part 2: Refine your iOS Development with Advanced Techniques
12. Chapter 10: Swift Macros 13. Chapter 11: Creating Pipelines with Combine 14. Chapter 12: Being Smart with Apple Intelligence and ML 15. Chapter 13: Exposing Your App to Siri with App Intents 16. Chapter 14: Improving the App Quality with Swift Testing 17. Chapter 15: Exploring Architectures for iOS 18. Index 19. Other Books You May Enjoy

Keeping our widgets up to date

We have learned that we need to look ahead and create a timeline with different entries and dates to keep our widget up to date. But how does our widget work under the hood?

Widgets don’t get any running time – once we generate the timeline entries, WidgetCenter generates their different views, keeps them persistently, and just switches them according to the provided timeline.

So, there’s no way to update our widget without reloading the timeline, and when we created our timeline, we had to define its reload policy:

let timeline = Timeline(entries: entries, policy: .atEnd)

However, sometimes, we want to instruct WidgetCenter to reload the timeline immediately, due to data changes or any other alterations.

Let’s see how it happens.

Reload widgets using the WidgetCenter

Throughout the chapter, I have mentioned WidgetCenter frequently but I haven’t explained what it means.

WidgetCenter is an object that holds information about the different configured widgets currently used, and it also provides an option to reload them.

To use WidgetCenter, we need to call the shared property to access its singleton reference:

WidgetCenter.shared

The difference between WidgetCenter and the rest of the code we have handled up until now is the fact that we call WidgetCenter from the app and not the widget extension.

Let’s see how we can call the WidgetCenter to get a list of active widgets:

func getConfigurations() {
    WidgetCenter.shared.getCurrentConfigurations { result
      in
        if let widgets = try? result.get() {
            // handle our widgets
        }
    }
}

The getCurrentConfigurations function uses a closure to return an array of active widgets. Each one of them is the WidgetInfo type – a structure that contains information about a specific configured widget.

The WidgetInfo structure has three properties – kind, family, and configuration:

  • kind – This is the string we set when we created the widget configuration (look again at the Configuring our widget section).
  • family – The family size of the widget – small, medium, or large.
  • configuration – The intent that contains user configuration information. The configuration property is optional.

If needed, we can use that information to reload the timeline of a specific kind of widget. For example, if we want to reload widgets with the kind of MyWidget, we need to call the following:

WidgetCenter.shared.reloadTimelines(ofKind: "MyWidget")

Notice that the function says Timelines and not Timeline, as it is possible to have several widgets of the same kind.

If we want to reload all our app widgets, we can call the reloadAllTimelines() function:

WidgetCenter.shared.reloadAllTimelines()

There are several great use cases for reloading our widget timeline, such as when we get a push notification, or when the user data or settings have changed. If you remember, when we discussed the widget timeline in the Generating a timeline section, we talked about the fact that widgets have a certain budget for the amount of reloading they can do each day. But the good news is that calling the reloadTimelines or reloadAllTimelines functions doesn’t count in this budget if our app is in the foreground or uses some other technique, such as playing audio in the background.

In most cases, reloadTimelines works well when the updated data is already on the device or in our app. But what should we do when the local persistent store is not updated?

We perform a network request, of course!

Go to the network for updates

Performing a network request to update local data is a typical operation in mobile apps. But how does it work in widgets?

Let’s look at the getTimeline function again:

func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ())

We can see that the getTimeline function is an asynchronous function. It means that when we build our timeline, we can perform async operations such as open URL sessions and fetching data.

Let’s see an example of requesting the next calendar events:

func getTimeline(in context: Context, completion: @escaping
    (Timeline<SimpleEntry>) -> Void) {
        var entries: [SimpleEntry] = []
        calendarService.fetchNextEvents { result in
            switch result {
            case .success(let events):
                for event in events {
                    let entry = SimpleEntry(date:
                      event.alertTime, nextEvent:
                        event.title, nextEventTime:
                          event.date)
                    entries.append(entry)
                }
            case .failure(let error):
                print("Error fetching next events:
                  \(error.localizedDescription)")
            }
        let timeline = Timeline(entries: entries, policy:
          .atEnd)
        completion(timeline)
}
    }

The getTimeline function implementation is similar to the previous getTimeline implementation we saw in the Generating a timeline section, and this time, we are fetching the events using the calendarService instance. The calendarService goes to our server and returns an array of events. Afterward, we loop the events, generate timeline entries, and return a timeline using the completion block.

Up until now, we have seen how to create a widget, animate it, and ensure it is updated as much as we can. But if we want to make our widget shine, we need to add some user-interactive capabilities.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime