Including a staleDate in Live Activity request causes activity to be disabled with loading spinner

When I try to initiate a Live Activity using the iOS 16.2 API for it, which includes the staleDate, when the staleDate has passed, rather than transitioning my Live Activity to its special ended state, the Live Activity presumably crashes because it looks disabled with a loading spinner on top

My ActivityAttributes struct is fairly simple, and the code that displays the "ended" state is itself not crashing, so unsure if I am doing something wrong

Here is the code for my ActivityAttributes:

struct TimerActivityAttributes: ActivityAttributes {
    enum TimerType: Codable {
        case rest, work
    }
    
    public struct ContentState: Codable & Hashable {
        var isEnded: Bool
    }
    
    let endDate: Date
    let exerciseName: String?
    let timerType: TimerType
    
    init(endDate: Date, exerciseName: String? = nil, timerType: TimerType = .rest) {
        self.endDate = endDate
        self.exerciseName = exerciseName
        self.timerType = timerType
    }
}

Here is the code that activates the Live Activity:

private func startLiveActivityNew(
        attributes: TimerActivityAttributes,
        contentState: TimerActivityAttributes.ContentState
    ) {
        guard let endDate else { return }
        let activityContent = ActivityContent(state: contentState, staleDate: endDate)
        
        do {
            ActivityManager.shared.activity = try Activity.request(
                attributes: attributes,
                content: activityContent
            )
        } catch (let error) {
            print("Caught an error: \(error.localizedDescription)")
        }
    }

Here is the code I use to determine whether to display the ended state (to maintain compatibility with iOS 16.1):

private func isEnded(context: ActivityViewContext<TimerActivityAttributes>) -> Bool {
        if #available(iOS 16.2, *) {
            if context.state.isEnded || context.isStale {
                return true
            }
        } else if context.state.isEnded {
            return true
        }
        
        return false
    }

And this is what happens after staleDate has passed.

I'm just not sure if I'm missing anything in my implementation

Replies

Hi Singy,

I had the same issue with my Live Activity and came across your thread here. For me the solution was to provide an explizit view for when the Activity is Stale. This can be done e.g. by incorporating a query.

struct LiveActivityView: View {
    let context: ActivityViewContext<TimerActivityAttributes>
    
    var body: some View {
        Stack {
            if context.isStale {
                // Stale View
            }
            else {
                // Normal View
            }
        }
    }
}

I hope this helps you in solving your issue.