HomeiOS DevelopmentGrowing Stay Actions in SwiftUI Apps

Growing Stay Actions in SwiftUI Apps


Stay Actions, first launched inĀ iOS 16, are certainly one of Apple’s most fun updates for creating apps that really feel extra related to customers in actual time. As an alternative of requiring customers to continuously reopen an app, Stay Actions let info stay seen proper on the Lock Display screen and Dynamic Island. Whether or not it is monitoring a meals supply, checking sports activities scores, or monitoring progress towards a purpose, this characteristic retains necessary updates only a look away.

Later inĀ iOS 17, Apple expanded Stay Actions even additional by supporting push updates from the server facet, which makes them much more highly effective for apps that depend on real-time info. However even with out server-driven updates, Stay Actions are extremely helpful for client-side apps that need to enhance engagement and supply well timed suggestions.

On this tutorial, we’ll discover the right way to implement Stay Actions by constructing aĀ Water Tracker app. The app permits customers to log their day by day water consumption and immediately see their progress replace on the Lock Display screen or Dynamic Island. By the top of the tutorial, you will perceive the right way to combine Stay Actions into your SwiftUI apps.

A Fast Take a look at the Demo App

liveactivities-demo-app

Our demo app,Ā Water Tracker, is an easy and enjoyable method to preserve observe of your day by day water consumption. You’ve most likely heard the recommendation that consuming eight glasses of water a day is an efficient behavior, and this app helps you keep conscious of that purpose. The design is minimal on objective: there is a round progress bar displaying how far alongside you’re, and each time you faucet theĀ Add GlassĀ button, the counter goes up by one and the progress bar fills a bit of extra.

Behind the scenes, the app makes use of aĀ WaterTrackerĀ class to handle the logic. This class retains observe of what number of glasses you’ve already logged and what your day by day purpose is, so the UI at all times displays your present progress. Right here’s the code that makes it work:

import Remark

@Observable
class WaterTracker {
    var currentGlasses: Int = 0
    var dailyGoal: Int = 8
    
    func addGlass() {
        guard currentGlasses = dailyGoal
    }
    
}

What we’re going to do is so as to add Stay Actions help to the app. As soon as carried out, customers will be capable to see their progress instantly on the Lock Display screen and within the Dynamic Island. The Stay Exercise will present the present water consumption alongside the day by day purpose in a transparent, easy method.

liveactivities-lockscreen-island.png

Stay Actions are constructed as a part of an app’s widget extension, so step one is so as to add a widget extension to your Xcode undertaking.

On this demo, the undertaking is known as WaterReminder. To create the extension, choose the undertaking in Xcode, go to the menu bar, and select Editor > Goal > Add Goal. When the template dialog seems, choose Widget Extension, give it a reputation, and ensure to verify the Embrace Stay Exercise possibility.

liveactivities-add-widget.png

When Xcode asks, remember to activate the brand new scheme. It is going to then generate the widget extension for you, which seems as a brand new folder within the undertaking navigator together with the starter code for the Stay Exercise and the widget.

We’ll be rewriting your entireĀ WaterReminderWidgetLiveActivity.swiftĀ file from scratch, so it’s greatest to filter all of its present code earlier than continuing.

For the reason that Stay Exercise doesn’t depend on the widget, you may optionally take away the WaterReminderWidget.swift file and replace the WaterReminderWidgetBundle struct like this:

struct WaterReminderWidgetBundle: WidgetBundle {
    var physique: some Widget {
        WaterReminderWidgetLiveActivity()
    }
}

Defining the ActivityAttributes Construction

TheĀ ActivityAttributesĀ protocol describes the content material that seems in your Stay Exercise.Ā We’ve to undertake the protocol and outline the dynamic content material of the exercise.

Since this attributes construction is normally shared between each the principle app and widget extension, I recommend to create a shared folder to host this Swift file. Within the undertaking folder, create a brand new folder named Shared after which create a brand new Swift file named WaterReminderWidgetAttributes.swift.

Replace the content material like this:

import Basis
import ActivityKit

struct WaterReminderWidgetAttributes: ActivityAttributes {
    public struct ContentState: Codable, Hashable {
        var currentGlasses: Int
        var dailyGoal: Int
    }
    
    var activityName: String
}

extension WaterReminderWidgetAttributes {
    static var preview: WaterReminderWidgetAttributes {
        WaterReminderWidgetAttributes(activityName: "Water Reminder")
    }
}

extension WaterReminderWidgetAttributes.ContentState {
     static var pattern: WaterReminderWidgetAttributes.ContentState {
        WaterReminderWidgetAttributes.ContentState(currentGlasses: 3, dailyGoal: 8)
     }
     
    static var goalReached: WaterReminderWidgetAttributes.ContentState {
        WaterReminderWidgetAttributes.ContentState(currentGlasses: 8, dailyGoal: 8)
     }
}

TheĀ WaterReminderWidgetAttributesĀ struct adopts theĀ ActivityAttributesĀ protocol and contains anĀ activityNameĀ property to determine the exercise. To adapt to the protocol, we outline a nestedĀ ContentStateĀ struct, which holds the info displayed within the Stay Exercise—particularly, the variety of glasses consumed and the day by day purpose.

The extensions are used for SwiftUI previews, offering pattern knowledge for visualization.

Please take notice that the goal membership of the file ought to be accessed by each the principle app and the widget extension. You’ll be able to confirm it within the file inspector.

liveactivities-shared-target-membership.png

Implementing the Stay Exercise View

Subsequent, let’s implement the reside exercise view, which handles the consumer interface in several settings. Open the WaterReminderWidgetLiveActivity.swift file and write the code like beneath:

import ActivityKit
import WidgetKit
import SwiftUI

struct WaterReminderLiveActivityView: View {
    
    let context: ActivityViewContext
    
    var physique: some View {
        VStack(alignment: .main, spacing: 10) {
            HStack {
                Textual content("šŸ’§")
                    .font(.title)
                Textual content("Water Reminder")
                    .font(.headline)
                    .fontWeight(.semibold)
                Spacer()
            }
            
            HStack {
                Textual content("Present: (context.state.currentGlasses)")
                    .font(.title2)
                    .fontWeight(.daring)
                Spacer()
                Textual content("Purpose: (context.state.dailyGoal)")
                    .font(.title2)
            }
            
            // Progress bar
            Gauge(worth: Double(context.state.currentGlasses), in: 0...Double(context.state.dailyGoal)) {
                EmptyView()
            }
            .gaugeStyle(.linearCapacity)
        }

    }
}

This view defines the principle interface of the Stay Exercise, which seems on each the Lock Display screen and the Dynamic Island. It shows a progress bar to visualise water consumption, together with the present variety of glasses consumed and the day by day purpose.

Subsequent, create the WaterReminderWidgetLiveActivity struct like this:

struct WaterReminderWidgetLiveActivity: Widget {
    var physique: some WidgetConfiguration {
        ActivityConfiguration(for: WaterReminderWidgetAttributes.self) { context in
            // Lock display/banner UI goes right here
            WaterReminderLiveActivityView(context: context)
                .padding()
        } dynamicIsland: { context in
            DynamicIsland {
                // Expanded UI goes right here.  Compose the expanded UI by means of
                DynamicIslandExpandedRegion(.middle) {
                    WaterReminderLiveActivityView(context: context)
                        .padding(.backside)
                }
            } compactLeading: {
                Textual content("šŸ’§")
                    .font(.title3)
            } compactTrailing: {
                
                if context.state.currentGlasses == context.state.dailyGoal {
                    Picture(systemName: "checkmark.circle")
                        .foregroundColor(.inexperienced)
                } else {
                    ZStack {
                        Circle()
                            .fill(Colour.blue.opacity(0.2))
                            .body(width: 24, top: 24)
                        
                        Textual content("(context.state.dailyGoal - context.state.currentGlasses)")
                            .font(.caption2)
                            .fontWeight(.daring)
                            .foregroundColor(.blue)
                    }
                }

            } minimal: {
                Textual content("šŸ’§")
                    .font(.title2)
            }
        }
    }
}

The code above defines the Stay Exercise widget configuration for the app. In different phrases, you configure how the reside exercise ought to seem beneath totally different configurations.

To maintain it easy, we show the identical reside exercise view on the Lock Display screen and Dynamic Island.

TheĀ dynamicIslandĀ closure specifies how the Stay Exercise ought to look contained in the Dynamic Island. Within the expanded view, the identicalĀ WaterReminderLiveActivityViewĀ is proven within the middle area. For the compact view, the main facet shows a water drop emoji, whereas the trailing facet modifications dynamically primarily based on the progress: if the day by day purpose is reached, a inexperienced checkmark seems; in any other case, a small round indicator reveals what number of glasses are left. Within the minimal view, solely the water drop emoji is displayed.

Lastly, let’s add some preview code to render the preview of the Stay Exercise:

#Preview("Notification", as: .content material, utilizing: WaterReminderWidgetAttributes.preview) {
   WaterReminderWidgetLiveActivity()
} contentStates: {
    WaterReminderWidgetAttributes.ContentState.pattern
    WaterReminderWidgetAttributes.ContentState.goalReached
}

#Preview("Dynamic Island", as: .dynamicIsland(.expanded), utilizing: WaterReminderWidgetAttributes.preview) {
    WaterReminderWidgetLiveActivity()
} contentStates: {
    WaterReminderWidgetAttributes.ContentState(currentGlasses: 3, dailyGoal: 8)
    
    WaterReminderWidgetAttributes.ContentState(currentGlasses: 8, dailyGoal: 8)
}


#Preview("Dynamic Island Compact", as: .dynamicIsland(.compact), utilizing: WaterReminderWidgetAttributes.preview) {
    WaterReminderWidgetLiveActivity()
} contentStates: {
    WaterReminderWidgetAttributes.ContentState(currentGlasses: 5, dailyGoal: 8)
    
    WaterReminderWidgetAttributes.ContentState(currentGlasses: 8, dailyGoal: 8)
}

Xcode permits you to preview the Stay Exercise in several states while not having to run the app on a simulator or an actual machine. By establishing a number of preview snippets, you may shortly check how the Stay Exercise will look on each the Lock Display screen and the Dynamic Island.

Managing Stay Actions

Now that we’ve put together the view of the reside exercise, what’s left is to set off it when the consumer faucets the Add Glass button. To make our code extra organized, we are going to create a helper class referred to as LiveActivityManager to managing the reside exercise cycle.

import Basis
import ActivityKit
import SwiftUI

@Observable
class LiveActivityManager {
    personal var liveActivity: Exercise?
    
    var isLiveActivityActive: Bool {
        liveActivity != nil
    }
    
    // MARK: - Stay Exercise Administration
    
    func startLiveActivity(currentGlasses: Int, dailyGoal: Int) {
        guard ActivityAuthorizationInfo().areActivitiesEnabled else {
            print("Stay Actions will not be enabled")
            return
        }
        
        // Finish any present exercise first
        endLiveActivity()
        
        let attributes = WaterReminderWidgetAttributes(activityName: "Water Reminder")
        let contentState = WaterReminderWidgetAttributes.ContentState(
            currentGlasses: currentGlasses,
            dailyGoal: dailyGoal
        )
        
        do {
            liveActivity = attempt Exercise.request(
                attributes: attributes,
                content material: ActivityContent(state: contentState, staleDate: nil),
                pushType: nil
            )
            print("Stay Exercise began efficiently")
        } catch {
            print("Error beginning reside exercise: (error)")
        }
    }
    
    func updateLiveActivity(currentGlasses: Int, dailyGoal: Int) {
        guard let liveActivity = liveActivity else { return }
        
        Activity {
            let contentState = WaterReminderWidgetAttributes.ContentState(
                currentGlasses: currentGlasses,
                dailyGoal: dailyGoal
            )
            
            await liveActivity.replace(ActivityContent(state: contentState, staleDate: nil))
            print("Stay Exercise up to date: (currentGlasses)/(dailyGoal)")
        }
    }
    
    func endLiveActivity() {
        guard let liveActivity = liveActivity else { return }
        
        Activity {
            await liveActivity.finish(nil, dismissalPolicy: .rapid)
            self.liveActivity = nil
            print("Stay Exercise ended")
        }
    }

}

The code works with WaterReminderWidgetAttributes that we have now outlined earlier for managing the state of the reside exercise.

When a brand new Stay Exercise begins, the code first checks whether or not Stay Actions are enabled on the machine and clears out any duplicates. It then configures the attributes and makes use of theĀ requestĀ methodology to ask the system to create a brand new Stay Exercise.

Updating the Stay Exercise is simple: you merely replace the content material state of the attributes and name theĀ replacemethodology on the Stay Exercise object.

Lastly, the category features a helper methodology to finish the at the moment energetic Stay Exercise when wanted.

Utilizing the Stay Exercise Supervisor

With the reside exercise supervisor arrange, we are able to now replace the WaterTracker class to work with it. First, declare a property to carry the LiveActivityManager object within the class:

let liveActivityManager = LiveActivityManager()

Subsequent, replace the addGlass() methodology like this:

func addGlass() {
    guard currentGlasses 

When the button is tapped for the primary time, we name the startLiveActivity methodology to begin a reside exercise. For subsequent faucets, we merely replace the content material states of the reside exercise.

The reside exercise ought to be ended when the consumer faucets the reset button. Subsequently, replace the resetDaily methodology like beneath:

func resetDaily() {
    currentGlasses = 0
    
    liveActivityManager.endLiveActivity()
}

That’s it! We’ve accomplished all of the code modifications.

Updating Data.plist to Allow Stay Actions

Earlier than your app can execute Stay Actions, we have now so as to add an entry referred to as Helps Stay Actions within the Data.plist file of the principle app. Set the worth to YES to allow Stay Actions.

liveactivities-infoplist.png

Nice! At this level, you may check out Stay Actions both within the simulator or instantly on an actual machine.

liveactivities-dynamic-island.png

Abstract

On this tutorial, we explored the right way to add Stay Actions to SwiftUI apps. You have realized how these options enhance consumer engagement by delivering real-time info on to the Lock Display screen and the Dynamic Island, decreasing the necessity for customers to reopen your app. We lined your entire course of, together with creating the info mannequin, designing the consumer interface, and managing the Stay Exercise lifecycle. We encourage you to combine Stay Actions into your present or future purposes to offer a richer, extra handy consumer expertise.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments