AVContentKeySession does not call delegate for repeated processContentKeyRequest with same identifier

I’m working with FairPlay offline licenses using AVContentKeySession and ran into behavior that I cannot find documented.

I am explicitly calling:

contentKeySession.processContentKeyRequest(
    withIdentifier: identifier as NSString,
    initializationData: nil,
    options: nil
)

Expected behavior

I expect that each call to processContentKeyRequest will eventually result in the delegate callback: contentKeySession(_:didProvide:)

Observed behavior

If I call processContentKeyRequest with a new identifier, everything works as expected:

  • didProvide is called
  • I complete the request successfully

However, if I call processContentKeyRequest again with the same identifier that was already processed earlier, then:

  • No delegate callbacks are triggered at all
  • The session does not appear to be blocked or stuck
  • If I issue another request with a different identifier, it is processed normally

So the behavior looks like the session is silently ignoring repeated requests for the same content key identifier.

Important context

  • This is not a concurrency issue — the session continues processing other requests
  • This is reproducible consistently
  • I am not using renewExpiringResponseData(for:) because I do not have a live AVContentKeyRequest at the time of retry

Use case

My use case is offline playback with periodically refreshed licenses. The app can stay alive for a long time (days/weeks), and I need to proactively refresh licenses before expiration.

In this scenario:

  • I only have the contentKeyIdentifier
  • I do not have a current AVContentKeyRequest
  • Calling processContentKeyRequest again for the same identifier does not trigger any delegate callbacks

Questions

  1. Is this behavior expected — that AVContentKeySession ignores repeated processContentKeyRequest calls for the same identifier?
  2. What is the recommended way to re-fetch or refresh a license when:
  • I only have the identifier
  • I do not have a current AVContentKeyRequest
  • I need to refresh proactively (not just in response to playback)
  1. What is the intended approach in this case? Maybe to create a new AVContentKeySession to force a new request cycle? Or something else?

  2. Is there any way to guarantee that a call to processContentKeyRequest will result in a delegate callback, or is it expected that it may be ignored in some cases?

Any clarification on the intended lifecycle of AVContentKeySession and how repeated requests should be handled would be greatly appreciated.

The behavior you're seeing — processContentKeyRequest(withIdentifier:initializationData:options:) silently skipping a repeated call for the same identifier — is not explicitly documented. The SDK header for this method describes it as a way to "generate an AVContentKeyRequest from request initialization data already in hand, without awaiting such data during the processing of media data of an associated recipient" — but says nothing about what happens on repeated calls for the same identifier.

For your use case — proactive offline license renewal when you only have the identifier and no live AVContentKeyRequest — there are two approaches to consider:

  1. Revoke the content key, then re-request (iOS 17.4+)

If you can target iOS 17.4 or later, AVContentKey.revoke() is likely the best fit. The SDK header states explicitly: "Once revoked, the AVContentKey is no longer eligible to be used with any media. If the key is required again, or if the key is requested to be loaded by the application, a new AVContentKeyRequest will be dispatched to the delegate."

This means calling revoke() on the content key and then calling processContentKeyRequest(withIdentifier:initializationData:options:) will trigger contentKeySession(_:didProvide:) again. You can obtain the AVContentKey from the original AVContentKeyRequest via its contentKey property (available since iOS 14.5), so you would need to retain either the request or the content key object, keyed by identifier.

  1. Use renewExpiringResponseData(for:) with a retained request

renewExpiringResponseData(for:) is the intended renewal mechanism. The SDK header confirms it "will invoke your delegate with a new content key request... via contentKeySession:didProvideRenewingContentKeyRequest:". This triggers contentKeySession(_:didProvideRenewingContentKeyRequest:), giving you a fresh AVContentKeyRequest to complete the renewal flow. This requires retaining a reference to the original AVContentKeyRequest, keyed by identifier.

Both options require an architectural change — retaining either the AVContentKey or the AVContentKeyRequest from the original request so you have it available at renewal time. Option 1 has the strongest documented guarantee that a new request will be dispatched.

I'd also suggest filing a feedback request asking Apple to document the behavior of processContentKeyRequest when called with an identifier that has already been processed — the silent behavior is surprising and not covered in the documentation or SDK headers. See Bug Reporting: How and Why? for tips on writing an effective report.

AVContentKeySession does not call delegate for repeated processContentKeyRequest with same identifier
 
 
Q