I’m working on a SwiftUI app that uses the DynamicIsland’s expanded region to display a moving rectangular object. The movement is triggered by an AppIntent, but I’m encountering an issue where the UI doesn’t update to reflect the object’s new position when the intent is triggered:
import SwiftUI
import WidgetKit
import AppIntents
import ActivityKit
struct RectangularObjectMovingView: View, ActivityAttributes {
public typealias ContentState = MovementState
public var id: String
let activityID: String
public struct MovementState: Codable, Hashable {
var position: CGFloat
var isMoving: Bool
var gameState: GameState
}
@State private var objectY: CGFloat
@State private var gameState: GameState
var body: some View {
GeometryReader { geometry in
ZStack {
// Moving object
Rectangle()
.fill(Color.white)
.frame(width: 40, height: 40)
.position(x: 40, y: geometry.size.height - 20 - objectY)
.animation(.easeInOut(duration: 0.5), value: objectY)
}
.overlay(
Button(intent: MoveRectangularObjectIntent(activityID: activityID)) {
Color.blue
} // Passing activityID to Intent will not let user return back to main app immediately when on press of this button
.buttonStyle(PlainButtonStyle())
)
}
}
static func handleInput(for activityID: String) {
guard let activity = Activity<RectangularObjectMovingView>.activities.first else {
return
}
Task {
var newState = activity.content.state
newState.isMoving = true
newState.position = 100 // Move to 100
await activity.update(ActivityContent(state: newState, staleDate: nil))
// Simulate movement timing
try? await Task.sleep(nanoseconds: UInt64(0.5 * Double(NSEC_PER_SEC)))
newState.isMoving = false
newState.position = 0 // Move back to 0
await activity.update(ActivityContent(state: newState, staleDate: nil))
}
}
}
struct MoveRectangularObjectIntent: AppIntent {
static var title: LocalizedStringResource = "Move Object Signal"
static var description = IntentDescription("Sends a movement signal to the object")
@Parameter(title: "Activity ID")
var activityID: String
func perform() async throws -> some IntentResult {
print("MoveRectangularObjectIntent performed for activity ID: (activityID)")
RectangularObjectMovingView.handleInput(for: activityID)
return .result()
}
}
enum ObjectState: String, Codable {
case idle
case moving
case stopped
}
class ActivityManager {
static func startActivity(position: CGFloat = 0, isMoving: Bool = false, state: ObjectState = .idle) async throws -> String {
let id = UUID().uuidString
let activityID = UUID().uuidString
let attributes = RectangularObjectMovingView(id: id, activityID: activityID)
let initialContent = ActivityContent(state: RectangularObjectMovingView.ContentState(position: position, isMoving: isMoving, state: state), staleDate: nil)
let activity = try await Activity.request(
attributes: attributes,
content: initialContent,
pushType: nil
)
return activity.id
}
static func endAllActivities() async {
for activity in Activity<RectangularObjectMovingView>.activities {
await activity.end(nil, dismissalPolicy: .immediate)
}
}
static func signalInput(id: String) async {
if let activity = Activity<RectangularObjectMovingView>.activities.first(where: { $0.id == id }) {
let currentState = activity.content.state
let updatedContent = ActivityContent(state: RectangularObjectMovingView.ContentState(
position: currentState.position,
isMoving: true,
state: .moving
), staleDate: nil)
await activity.update(updatedContent)
}
}
}
When the MoveRectangularObjectIntent is triggered (by tapping the overlay button in the DynamicIsland), the handleInput method is called, and it updates the activity’s state. However, this state change doesn’t seem to be reflected in the UI – the rectangular object doesn’t move.
I’ve confirmed that the intent is being triggered and the handleInput method is being called, but the visual update isn’t happening. One side note thing worth mentioning is that the activityID passed to the intent doesn’t match the ID of the activity being updated, the handleInput method is still able to change the state (e.g., from idle to moving):
MoveRectangularObjectIntent performed for activity ID: 47BCB9B5-D773-44E8-B46A-82BD130EE95E
Updating content for activity 4088E571-4F10-4BAD-B45A-76BCE530A95A
This suggests that the activityID matching might not be crucial for updating the view state. However, this begs the question: Why isn’t the UI updating to reflect the state changes (such that the rectangular object moves in the DynamicIsland view?), even though the handleInput method is successfully changing the state? I’ve tried using @State variables to trigger redraws, but it doesn’t seem to help either. This doesn’t seem to be a DynamicIsland specific problem, but that some part of my SwiftUI was not working. What is the best approach to this problem?
Any insights or suggestions would be greatly appreciated!