iOS Setup
Customizing Live Activities
Requires SDK version 4.13.0 or later Requires iOS 18.0 or later
The Rover Live Activity widgets are open source. To change how a Live Activity looks, copy the widget's code into your Widget Extension and edit it there. Your app keeps using the Rover SDK's activity attributes, so Live Activities started and updated by Rover keep working as before.
This guide assumes you have already completed the Live Activities setup.
How It Works
Each league module contains two parts:
- Activity attributes (
Rover<League>ActivityAttributesand its content state). These describe the data Rover sends. Keep using them from the SDK. - The widget (
Rover<League>LiveActivity). This is the SwiftUI layout. You replace it with your own copy.
In this guide, <League> stands for NBA, NFL or NHL, matching the module you installed. For example, NFL apps copy RoverNFLLiveActivity.swift and keep using RoverNFLActivityAttributes.
Keep the SDK's attributes type
Do not copy or rename Rover<League>ActivityAttributes. Rover starts and updates Live Activities using this type and its activityName. A renamed copy will not receive any updates.
Copy the Widget Source
The widget source is in the Rover SDK package. In Xcode's Project navigator, expand the Rover package under Package Dependencies and open Sources/<League>LiveActivities. You can also find the same folder in the rover-ios repository at the tag for the SDK version you use.
Copy these two files into your Widget Extension target:
Rover<League>LiveActivity.swift, which contains the widget and all of its viewsTeamInfo+Assets.swift, which looks up team logos and colors
Rename the copied widget file (for example, to MyTeamLiveActivity.swift) and make these changes at the top:
import ActivityKit
import Rover<League>LiveActivities // Add this line
import SwiftUI
import WidgetKit
// Was: public struct Rover<League>LiveActivity: Widget
struct MyTeamLiveActivity: Widget {
// Remove: public init() {}
var body: some WidgetConfiguration {
ActivityConfiguration(for: Rover<League>ActivityAttributes.self) { context in
// ...
The #Preview near the bottom of the file still refers to the SDK's widget. Point it at your copy, otherwise the Xcode canvas keeps showing the original layout:
#Preview("My Team", as: .content, using: Rover<League>ActivityAttributes.preview) {
MyTeamLiveActivity() // Was: Rover<League>LiveActivity()
} contentStates: {
// ...
}
Copy the Team Assets
The copied views load team logos and colors from an asset catalog. Copy the TeamLogos and TeamColors folders from the module's Resources/Assets.xcassets into your Widget Extension's Assets.xcassets, keeping the folder names.
Then update the copied TeamInfo+Assets.swift to load from your extension's asset catalog:
import Rover<League>LiveActivities // Add this line
import SwiftUI
extension <League>TeamInfo {
var logo: Image {
Image("TeamLogos/\(abbreviation)") // Was: bundle: .module
}
var brandColor: Color {
Color("TeamColors/\(abbreviation)") // Was: bundle: .module
}
}
Update the Widget Bundle
Replace the SDK's widget with your copy in your Widget Bundle. Only one widget can handle a given attributes type, so do not include both.
@main
struct LiveActivityWidgetBundle: WidgetBundle {
var body: some Widget {
MyTeamLiveActivity() // Was: Rover<League>LiveActivity()
}
}
The registration code from the Live Activities guide does not change.
Customize the Widget
Your copy is ordinary SwiftUI code, so you can edit it as needed. The Lock Screen layout is LockScreenView and the views it uses, and the Dynamic Island layouts are in the dynamicIsland closure of ActivityConfiguration.
The views can show anything in the activity's attributes and content state:
- Attributes, set when the activity starts: both teams, the matchup title, the venue and whether your team is at home. Some leagues also include broadcast details.
- Content state, updated during the game: the game phase and clock, both teams' scores and stats, and the last play.
To add your own images, such as a sponsor logo, add them to your Widget Extension's asset catalog and show them with Image("YourAssetName").
Images must be bundled
A Live Activity cannot download images while it is on screen. Add any images it shows to your Widget Extension's asset catalog, and keep them small, because Live Activities have a tight memory limit.
Test Your Widget
Open your copied widget file and use the Xcode canvas. The preview shows the Lock Screen layout for each sample game state in the file. Check that your changes fit alongside the clock and scores in each one.
To preview the Dynamic Island, add a preview for each presentation you want to check:
#Preview("Expanded", as: .dynamicIsland(.expanded), using: Rover<League>ActivityAttributes.preview) {
MyTeamLiveActivity()
} contentStates: {
// Same content states as the Lock Screen preview
}
Use .dynamicIsland(.compact) and .dynamicIsland(.minimal) for the other presentations.
To see your widget with live games, test on a physical device as described in Testing Your Integration.
Updating the Rover SDK
Your copy of the widget does not change when you update the Rover SDK. After updating, compare the SDK's Rover<League>LiveActivity.swift with your copy and bring over any changes you want to keep.