Universal Link

Hello,

I'm developing a feature for my app, that allows users to challenge their friends. The friend request functionality is built using Universal Links, but I've run into a significant issue.

The Universal Links are correctly deep-linking into the app. However, once the app opens, nothing happens—the friend request acceptance or rejection flow does not occur. This prevents users from completing friend requests and building their friend list.

Here are examples of the Universal Links I'm generating:

https://www.strike-force.app/invite?type=invite&userID=...

https://www.strike-force.app/invite?type=invite&friendRequestID=...

https://www.strike-force.app/profile?userID=...

I've recently updated my cloudflare-worker.js to serve a paths array of ["*"] in the AASA file, so I believe the links themselves should be valid.

Technical Details & Error Logs

In the console, I am consistently seeing the following error message:

Cannot issue sandbox extension for URL:https://www.strike-force.app/invite?token=7EF1E439-090B-4DF2-BE64-9904F50A3F8B

Received port for identifier response: <(null)> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false} elapsedCPUTimeForFrontBoard couldn't generate a task port

This error appears to be related to entitlements and process state, but I am not sure if it's the root cause of the Universal Link issue or a separate problem. The 'Client not entitled' error on line 3 has had me chasing down entitlements issues. But, I've added the Associated Domains entitlement with the proper applink URLs and verified this in my Developer Portal. I've regenerated my provisioning profile, manually installed it, and selected/de-selected Automatically Manage Signing. As well I've verified my AASA file and it's correctly being served via HTTPS and returning a 200.

curl -i https://strike-force.app/.well-known/apple-app-site-association

curl -i https://www.strike-force.app/.well-known/apple-app-site-association

I am looking for guidance on why the friend request flow is not being triggered after a successful deep-link and how I can fix the related error. Any insights or suggestions would be greatly appreciated.

Answered by DTS Engineer in 856683022
I've just created a minimal test app and the Universal Links seem to work.

OK, cool.

Unsure then why I'm seeing these errors in the console logs.

IMO that’s just log noise.

It seems that someone is trying to extend a sandbox to access the https://www.strike-force.app/… URL. However, that’s an https URL, and sandbox extensions are irrelevant because they only apply to file system URLs [1]. So, the fact that this attempt fails tells you nothing about the state of your app’s universal links support.

For general advice about log noise, see On Log Noise.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] We generally don’t talk a lot about sandbox extensions on iOS. I discuss this topic for the App Sandbox on macOS in On File System Permissions. While macOS’s App Sandbox and the iOS sandbox are quite different, they share a lot of underlying infrastructure and sandbox extensions are part of that.

This is iOS, right?

once the app opens, nothing happens

So, lemme see if I understand this properly:

  1. The user taps on your link.
  2. It opens your app.
  3. Something goes wrong from there.

Is that right?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Sorry for omitting these details in my original post.

PLATFORM AND VERSION

iOS

Development environment: Xcode Version 16.4 (16F6), macOS 15.5 (24F74)

Run-time configuration: iOS 18.5

CURRENT BEHAVIOR

When I tap "Add Friends" from my iOS app it generates a unique token and to be shares via Universal Link and displays the share sheet for me to select how I want to share this.

https://www.strike-force.app/invite?token=781E1E89-BEAC-4C5C-B3F1-2EC916566D2C

This is in dev when I build from XCode. If I perform this action from TestFlight, the button is unresponsive. I assume it's because of the errors below.

Cannot issue sandbox extension for URL:https://www.strike-force.app/invite?token=781E1E89-BEAC-4C5C-B3F1-2EC916566D2C
App is being debugged, do not track this hang
Hang detected: 0.37s (debugger attached, not reporting)
Received port for identifier response: <(null)> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false}
elapsedCPUTimeForFrontBoard couldn't generate a task port
Received port for identifier response: <(null)> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false}
elapsedCPUTimeForFrontBoard couldn't generate a task port
Received port for identifier response: <(null)> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false}
elapsedCPUTimeForFrontBoard couldn't generate a task port

So, I've been unable to test if the Universal Link strategy works with my testers in Test Flight. If I click the link myself it just opens my app.

I've implemented Friend Requests two ways. One is a direct linking between friends using a unique token. The other is a generic sharing of the user's profile which then results in a friend request from the person who initiates the request to that user.

https://www.strike-force.app/profile?userID=000773.de39195514404b7b9c60b14a7f1f8048.0122

This produces a similar error.

Cannot issue sandbox extension for URL:https://www.strike-force.app/profile?userID=000773.de39195514404b7b9c60b14a7f1f8048.0122
Received port for identifier response: <(null)> with error:Error Domain=RBSServiceErrorDomain Code=1 "Client not entitled" UserInfo={RBSEntitlement=com.apple.runningboard.process-state, NSLocalizedFailureReason=Client not entitled, RBSPermanent=false}
elapsedCPUTimeForFrontBoard couldn't generate a task port

I've just created a minimal test app and the Universal Links seem to work. Unsure then why I'm seeing these errors in the console logs.

https://github.com/ChristopherJones72521/MinimalLinkTest

I've just created a minimal test app and the Universal Links seem to work.

OK, cool.

Unsure then why I'm seeing these errors in the console logs.

IMO that’s just log noise.

It seems that someone is trying to extend a sandbox to access the https://www.strike-force.app/… URL. However, that’s an https URL, and sandbox extensions are irrelevant because they only apply to file system URLs [1]. So, the fact that this attempt fails tells you nothing about the state of your app’s universal links support.

For general advice about log noise, see On Log Noise.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] We generally don’t talk a lot about sandbox extensions on iOS. I discuss this topic for the App Sandbox on macOS in On File System Permissions. While macOS’s App Sandbox and the iOS sandbox are quite different, they share a lot of underlying infrastructure and sandbox extensions are part of that.

Accepted Answer

Here's a summary of what was going on and how I fixed the issue (Logs are clean now!).

The issue was not with the Universal Link configuration itself (the AASA file and Associated Domains were correct), but with a race condition in the SwiftUI app's lifecycle.

The Problem: A Timing Issue

When the Universal Link is tapped, iOS launches the app and immediately passes the URL via the .onContinueUserActivity modifier.

In my original code, this modifier was on the root view in Strike_ForceApp.swift. It would parse the link and post a Notification for the UI to handle.

However, at this early stage of the app launch, the destination view (FriendsAndChallengesView) that was supposed to be listening for this notification had not been initialized yet, especially if the user needed to go through a loading or login screen first.

As a result, the notification was being posted before any part of the UI was ready to listen for it. The link would open the app, but the "message" to navigate or show an alert was lost.

The Solution: Centralizing the Logic in a View Model

The fix was to refactor the deep link handling to be independent of the view lifecycle by centralizing the logic in a persistent view model.

Centralized State: I moved all the logic for parsing and processing the deep link into the MainTabViewModel. This view model is created once and stays in memory for the entire time the main tab view is on screen. It now has @Published properties to hold the state of any pending deep link (e.g., a profile ID to show or a friend request to accept).

Delayed Handling: The .onContinueUserActivity modifier was moved from the root App struct to the MainTabView. This ensures that Universal Links are only caught and processed when the main, authenticated part of the app is actually on screen and ready.

State-Driven UI: The MainTabView now observes the @Published properties in the MainTabViewModel. When the view model processes a deep link and updates its state, the MainTabView automatically reacts by presenting the correct UI—either a .sheet for a user profile or an .alert for a friend request.

This new architecture completely resolves the race condition. The logic is no longer dependent on a specific view being on screen at the exact moment the link is opened. Instead, the state is managed centrally, and the UI reacts whenever that state changes, which has made the Universal Link handling robust and reliable.

Universal Link
 
 
Q