The app registers a custom URL scheme (mastgtest://).
The SceneDelegate handles incoming URLs via scene(_:openURLContexts:). For each URL, the handler reads sourceApplication from UIOpenURLContext.options and checks it against a hardcoded allowedSources set before processing. In this demo we verify that the sourceApplication property is accessed in the compiled binary.
Apple only populates sourceApplication when the calling app belongs to the same Apple Developer Team. Apps from other teams or system apps (e.g. Notes, Safari) will have sourceApplication set to nil. This is an Apple platform limitation, but it still allows verifying that the URL was opened by one of your own apps, which is useful when a URL scheme triggers privileged actions that should only be accessible from within your app suite.
importSwiftUIimportUIKit// SUMMARY: This sample demonstrates a custom URL scheme handler registered in Info.plist// that validates the source application using UIOpenURLContext.options.sourceApplication.// The SceneDelegate reads the source bundle ID from each incoming URL context and checks// it against an allowlist before processing. Note that sourceApplication is only populated// for apps that belong to the same Apple Developer Team as the receiving app.structURLSchemeEvent{leturl:Stringletsource:Stringletresult:String}enumAppDelegateState{staticvarlastEvent:URLSchemeEvent?staticvaronEvent:((URLSchemeEvent)->Void)?}structMastgTest{@inline(never)@_optimize(none)publicstaticfuncmastgTest(completion:@escaping(String)->Void){funcdisplay(_event:URLSchemeEvent){completion(""" Incoming URL: \(event.url) Source app: \(event.source) Handler returned: \(event.result) """)}ifletexisting=AppDelegateState.lastEvent{display(existing)}else{completion(""" Waiting for incoming URL scheme... Open one of the registered schemes from another app to see the result: mastgtest://transfer?amount=500 """)}AppDelegateState.onEvent={eventindisplay(event)}}}@objcclassAppDelegate:UIResponder,UIApplicationDelegate{funcapplication(_application:UIApplication,configurationForConnectingconnectingSceneSession:UISceneSession,options:UIScene.ConnectionOptions)->UISceneConfiguration{letconfig=UISceneConfiguration(name:"Default Configuration",sessionRole:connectingSceneSession.role)config.delegateClass=SceneDelegate.selfreturnconfig}}// PASS: [MASTG-TEST-0371] The SceneDelegate reads UIOpenURLContext.options.sourceApplication// and checks it against an allowlist before processing the URL.classSceneDelegate:UIResponder,UIWindowSceneDelegate{privateletallowedSources:Set<String>=["com.mastg.testing-app",]funcscene(_scene:UIScene,willConnectTosession:UISceneSession,optionsconnectionOptions:UIScene.ConnectionOptions){connectionOptions.urlContexts.forEach{handleURL($0)}}funcscene(_scene:UIScene,openURLContextsURLContexts:Set<UIOpenURLContext>){URLContexts.forEach{handleURL($0)}}privatefunchandleURL(_context:UIOpenURLContext){leturl=context.urlletsource=context.options.sourceApplication??"(none)"letresult=allowedSources.contains(source)postEvent(url:url,source:source,result:result)}privatefuncpostEvent(url:URL,source:String,result:Bool){letevent=URLSchemeEvent(url:url.absoluteString,source:source,result:result?"true":"false")AppDelegateState.lastEvent=eventAppDelegateState.onEvent?(event)}}
The output shows the SceneDelegate's scene:openURLContexts: handler, the cross-references to sourceApplication, and the disassembly around each access site.
The test case passes because sourceApplication is accessed from both URL handler paths: willConnectTo, the cold launch path where iOS starts the app to handle the URL, at 0x100004c8c, and openURLContexts, the warm open path where the app is already running or suspended, at 0x1000051a8.
In each disassembly block:
ldr x1, ... reloc.fixup.sourceApplication loads the sourceApplication selector.
bl sym.imp.objc_msgSend sends it to the URL options object, retrieving the source application value.
The returned Objective C object is retained and bridged into a Swift String when non nil.
cbz x20, ... branches when the result is nil, which means the source application value was unavailable or not provided. For cross-app opens, Apple documents this as the expected result when the originating app has a different team identifier.
This confirms that the handler reads the caller application identifier on both URL entry paths. Together with the surrounding source validation logic in the sample, this demonstrates that the custom URL scheme handler checks the caller source before processing the URL.
You can use xcrun to launch the app on a connected iOS device with an arbitrary custom URL scheme payload and confirm that the handler rejects the request when sourceApplication is not populated with an allowlisted bundle ID.
First, list the connected devices and copy the device identifier:
xcrundevicectllistdevices
Then launch the app with a crafted mastgtest:// URL:
After the app opens, tap Start in the demo app to process the stored event. Because the URL was not opened by an allowlisted same team app, the result is observable in the app output:
This confirms at runtime that the URL handler reads sourceApplication and only returns true when the source application bundle ID matches the allowlist.