Using NSFileProviderReplicatedExtension on iOS

Hello friends,

I saw that iOS 16 has support for NSFileProviderReplicatedExtension, and I was hoping I could use this as a backend for my SSH/SFTP client.

I have attempted to use it by adding a new "File Provider Extension" target to my application, but when I attempt to use it, the system throws an exception shortly after calling my extension constructor, reporting that my class does not respond to the beginRequestWithExtensionContext:

2022-07-11 17:38:08.775886-0400 SshFs[32841:7522410] -[SshFs.FileProviderExtension beginRequestWithExtensionContext:]: unrecognized selector sent to instance 0x600001f80220

2022-07-11 17:38:08.778225-0400 SshFs[32841:7522410] [xpc.exceptions] <NSXPCConnection: 0x600002c900a0> connection to service with pid 27736 created from an endpoint: Warning: Exception caught during invocation of selector beginRequestWithDomain:alternateContentsDictionary:domainServicer:providerDomain:domainVersion:completionHandler:, dropping incoming message and invalidating the connection.

Exception: -[SshFs.FileProviderExtension beginRequestWithExtensionContext:]: unrecognized selector sent to instance 0x600001f80220

-[SshFs.FileProviderExtension beginRequestWithExtensionContext:]: unrecognized selector sent to instance 0x600001f80220

(

	0   CoreFoundation                      0x000000018040d20c __exceptionPreprocess + 172

	1   libobjc.A.dylib                     0x000000018005008c objc_exception_throw + 56

	2   CoreFoundation                      0x000000018041bd28 +[NSObject(NSObject) instanceMethodSignatureForSelector:] + 0

	3   CoreFoundation                      0x0000000180411104 ___forwarding___ + 1308

	4   CoreFoundation                      0x00000001804133fc _CF_forwarding_prep_0 + 92

	5   FileProvider                        0x000000018feb7c5c -[FPXExtensionContext beginRequestWithDomain:alternateContentsDictionary:domainServicer:providerDomain:domainVersion:completionHandler:] + 1464

	6   CoreFoundation                      0x00000001804135b0 __invoking___ + 144

	7   CoreFoundation                      0x0000000180410924 -[NSInvocation invoke] + 276

	8   Foundation                          0x0000000180cbc798 __NSXPCCONNECTION_IS_CALLING_OUT_TO_EXPORTED_OBJECT__ + 12

	9   Foundation                          0x0000000180cbb37c -[NSXPCConnection _decodeAndInvokeMessageWithEvent:reply:flags:] + 1184

	10  Foundation                          0x0000000180cbd5e0 message_handler_message + 84

	11  Foundation                          0x0000000180cbcb6c message_handler + 148

	12  libxpc.dylib                        0x0000000180087420 _xpc_connection_call_event_handler + 68

	13  libxpc.dylib                        0x00000001800877e0 _xpc_connection_mach_event + 904

	14  libdispatch.dylib                   0x0000000180134864 _dispatch_client_callout4 + 16

	15  libdispatch.dylib                   0x0000000180152f08 _dispatch_mach_msg_invoke + 624

	16  libdispatch.dylib                   0x000000018013c690 _dispatch_lane_serial_drain + 348

	17  libdispatch.dylib                   0x0000000180153fd4 _dispatch_mach_invoke + 468

	18  libdispatch.dylib                   0x000000018013c690 _dispatch_lane_serial_drain + 348

	19  libdispatch.dylib                   0x000000018013d4bc _dispatch_lane_invoke + 448

	20  libdispatch.dylib                   0x000000018014960c _dispatch_workloop_worker_thread + 772

	21  libsystem_pthread.dylib             0x00000001add9bb40 _pthread_wqthread + 284

	22  libsystem_pthread.dylib             0x00000001add9a904 start_wqthread + 8

)

I tried with a fresh project, with no baggage, and I get the same error, I uploaded it to GitHub

I am wondering what I might be missing, or if there is some sample code that I could get my hands on, just like there was some for macOS a couple of years back (FruitBasket).

When using NSFileProviderReplicatedExtension, you should construct NSFileProviderDomain without the pathRelativeToDocumentStorage argument.

Thank you, this did indeed get me over this odd crash.

I have run into another issue, and I can not seem to be making any progress, despite my attempts at single-stepping into the FileProvider framework with little luck.

I explored a couple of options:

Default scaffolding

I took the default scaffolding produced by Xcode, and merely added the NSFileProviderManager.add on my main application. In this scenario, when I try to run the extension, my constructor required init(domain: NSFileProviderDomain) is never invoked. But I do get the following messages on the console:

2022-07-13 09:48:41.519607-0400 Gooble[11689:275870] [org.tirania.test.swiftui.Third.Gooble/N{35}r] [ERROR] Creating internal error for "itemForItemID", original error was: NSError: FP -1000 "You need to authenticate before accessing this item."

2022-07-13 09:48:41.524792-0400 Gooble[11689:275869] [org.tirania.test.swiftui.Third.Gooble/N{35}r] [ERROR] Creating internal error for "fetchAndStartEnumeratingWithSettings", original error was: NSError: FP -1000 "You need to authenticate before accessing this item."

I have tried to use breakpoints and abort() everywhere to try to catch this thing, I tried using dtrace after disabling system integrity protection, and looked the Console logs and nothing seems unusual about it:

default Gooble  Hello, I'm launching as euid = 501, uid = 501, (persona not available)
default Gooble  Initializing connection
default Gooble  Removing all cached process handles
default Gooble  Sending handshake request attempt #1 to server
default Gooble  Creating connection to com.apple.runningboard
default Gooble  Handshake succeeded
default Gooble  Identity resolved as xpcservice<org.tirania.test.swiftui.Third.Gooble([osservice<com.apple.FileProvider>:11146])>
default Gooble  Bootstrapping; Bootstrap complete. Ready for handshake from host.
default Gooble  [u 74075900-B5F2-4A1A-8E9F-0FE0A8E0380C] [(null)((null))] Prepare received as euid = 501, uid = 501, (persona not available)
default Gooble  [u 9207AE37-8A25-49EB-A34A-E08DFB9A197A] [org.tirania.test.swiftui.Third.Gooble(1.0)] Set sole personality.
default Gooble  [u 9207AE37-8A25-49EB-A34A-E08DFB9A197A] [org.tirania.test.swiftui.Third.Gooble(1.0)] Begin using received as euid = 501, uid = 501, (persona not available)
default Gooble  +[NSExtensionContext _allowedItemPayloadClasses] not implemented. Setting the allowed payload classes to {(
    NSO
)}
default Gooble  Start service name com.apple.spotlight.IndexDelegateAgent
error   Gooble  [ERROR] Creating internal error for "itemForItemID", original error was: NSError: FP -1000 "You need to authenticate before accessing this item."
error   Gooble  [ERROR] Creating internal error for "fetchAndStartEnumeratingWithSettings", original error was: NSError: FP -1000 "You need to authenticate before accessing this item."
default Gooble  [u 9207AE37-8A25-49EB-A34A-E08DFB9A197A] [org.tirania.test.swiftui.Third.Gooble(1.0)] Removed sole personality.
default Gooble  [u 9207AE37-8A25-49EB-A34A-E08DFB9A197A] [org.tirania.test.swiftui.Third.Gooble(1.0)] host connection from pid 11146 invalidated

Changing the base class

I modified the sample to subclass NSFileProviderExtension instead of NSObject, and with this, my breakpoint on the constructor is hit, but still, none of my methods are called. The Xcode console shows a similar result to the previous version - makes me suspect that there is something missing that prevents my code from being invoked that is not hooked up.

This was the reason I started to use dtrace in the first place, as the "The file couldn't be opened" sounded like a good candidate to find what was missing, but I can not find any suspicious attempts to access the file system in that case:

2022-07-13 09:58:25.646358-0400 Gooble[12084:288057] [org.tirania.test.swiftui.Third.Gooble/N{35}r] [ERROR] Creating internal error for "itemForItemID", original error was: NSError: Cocoa 256 "The file couldn’t be opened."

2022-07-13 09:58:25.652024-0400 Gooble[12084:288059] [org.tirania.test.swiftui.Third.Gooble/N{35}r] [ERROR] Creating internal error for "fetchAndStartEnumeratingWithSettings", original error was: NSError: Cocoa 3328 "The requested operation couldn’t be completed because the feature is not supported."

The output from the Console app:

default Gooble  Hello, I'm launching as euid = 501, uid = 501, (persona not available)
default Gooble  Initializing connection
default Gooble  Removing all cached process handles
default Gooble  Sending handshake request attempt #1 to server
default Gooble  Creating connection to com.apple.runningboard
default Gooble  Handshake succeeded
default Gooble  Identity resolved as xpcservice<org.tirania.test.swiftui.Third.Gooble([osservice<com.apple.FileProvider>:11146])>
default Gooble  Bootstrapping; Bootstrap complete. Ready for handshake from host.
default Gooble  [u C81493C9-173C-43F5-8C30-BF24B6AB36F7] [(null)((null))] Prepare received as euid = 501, uid = 501, (persona not available)
default Gooble  [u 118918F0-BAB7-4945-9770-C0AD353BF587] [org.tirania.test.swiftui.Third.Gooble(1.0)] Set sole personality.
default Gooble  [u 118918F0-BAB7-4945-9770-C0AD353BF587] [org.tirania.test.swiftui.Third.Gooble(1.0)] Begin using received as euid = 501, uid = 501, (persona not available)
default Gooble  +[NSExtensionContext _allowedItemPayloadClasses] not implemented. Setting the allowed payload classes to {(
    NSObject
)}
default Gooble  NotifyToken::RegisterDispatch(com.apple.LaunchServices.database) fired for session key LSSessionKey(system: 0 uid: 501)
default Gooble  Start service name com.apple.spotlight.IndexDelegateAgent
error   Gooble  [ERROR] Creating internal error for "itemForItemID", original error was: NSError: Cocoa 256 "The file couldn’t be opened."
error   Gooble  [ERROR] Creating internal error for "fetchAndStartEnumeratingWithSettings", original error was: NSError: Cocoa 3328 "The requested operation couldn’t be completed because the feature is not supported."
default Gooble  [u 118918F0-BAB7-4945-9770-C0AD353BF587] [org.tirania.test.swiftui.Third.Gooble(1.0)] Removed sole personality.
default Gooble  [u 118918F0-BAB7-4945-9770-C0AD353BF587] [org.tirania.test.swiftui.Third.Gooble(1.0)] host connection from pid 11146 invalidated

Mhm, I just noticed something that I missed previously, which might be the whole reason for the above not working.

The call to NSFileProviderManager.add (domain) is returning an error NSPOSIXErrorDomain Code=1 "Operation not permitted", but still manages to add my file provider to Files. And shortly after the console shows:

[unspecified] container_create_or_lookup_app_group_path_by_app_group_identifier: client is not entitled

I have made sure they both are on the same app group (had to add the app group manually to the containing application, and it matches the extension provider).

This seems to be only a problem on the simulator, and not on the device. I got the baseline to work on device, the same code on simulator produces the Operation Not Permitted issue.

Using NSFileProviderReplicatedExtension on iOS
 
 
Q