MobileApplication.HandleURL not obvious

I need to be able to open a specific file type in my app.
I’d like to be able to send it by email, messages, or just have it on the phone, and be able to open with… like I can do with other apps.

I though that MobileApplication.HandleURL was the way to deal with this, but I can’t figure out how to register the file such that the os associates it with my app.

I did look at the example project, but it fails.

Help!

There’s a bug in the iOS framework which prevents file types from rendering to the plist. You’ll need to add the plist entries manually for now.

I suggest creating a desktop macOS project, adding the file types you need and then copying the relevant plist entries to a file for inclusion in your iOS app.

Hi Greg,
I did try that. Not sure which all entries are required.
Do I also have to register a custom URL Scheme?

I’ll have to look, but I believe that files come in through LaunchOptions

Do I have to do anything more to set it up?
I think I added the document types node from the plist to my iOS app.
When I run, I don’t see any way to send the docent to my app.

it depends on what you mean by “send the document” I suppose. What I was thinking of was when a user selects a file in the Files browser or touches one in an email.

that’s what I mean exactly (sorry for the lazy vocabulary).
When I see my document, and tap on it, I don’t get any option to “send/open/whatever” it in my app.

Here’s an example of what an imported and exported types look like in a plist file. For this example, my app can read (imports) JPEG files from anywhere, but it also creates (exports) its own file type with an extension of .myType

First there are sections that describes the imported and exported types:

Imported

   <key>UTImportedTypeDeclarations</key>
   <array>
      <dict>
         <key>UTTypeIdentifier</key>
         <string>public.jpeg</string>
         <key>UTTypeConformsTo</key>
         <array>
            <string>public.image</string>
            <string>public.data</string>
            <string>public.item</string>
            <string>public.content</string>
         </array>
         <key>UTTypeDescription</key>
         <string>JPEG Image File</string>
         <key>UTTypeTagSpecification</key>
         <dict>
            <key>public.filename-extension</key>
            <array>
            <string>jpg</string>
            </array>
            <key>public.mime-type</key>
            <array>
            <string>image/jpeg</string>
            </array>
         </dict>
      </dict>
   </array>

Exported

   <key>UTExportedTypeDeclarations</key>
   <array>
      <dict>
         <key>UTTypeIdentifier</key>
         <string>com.example.myapp-My-File-Type</string>
         <key>UTTypeConformsTo</key>
         <array>
            <string>public.data</string>
         </array>
         <key>UTTypeDescription</key>
         <string>A file in My File Type format</string>
         <key>UTTypeTagSpecification</key>
         <dict>
            <key>public.filename-extension</key>
            <array>
            <string>myType</string>
            </array>
            <key>public.mime-type</key>
            <array>
            <string>application/octet-stream</string>
            </array>
         </dict>
      </dict>
   </array>

Then a section that shows how your app is allowed to interact with those types:

   <key>CFBundleDocumentTypes</key>
   <array>
      <dict>
         <key>CFBundleTypeName</key>
         <string>JPEGimage</string>
         <key>CFBundleTypeExtensions</key>
         <array>
            <string>jpg</string>
         </array>
         <key>LSItemContentTypes</key>
         <array>
            <string>public.jpeg</string>
         </array>
         <key>CFBundleTypeRole</key>
         <string>Editor</string>
         <key>LSHandlerRank</key>
         <string>Default</string>
      </dict>
      <dict>
         <key>CFBundleTypeName</key>
         <string>My File Type</string>
         <key>CFBundleTypeExtensions</key>
         <array>
            <string>myType</string>
         </array>
         <key>LSItemContentTypes</key>
         <array>
            <string>com.example.myapp-My-File-Type</string>
         </array>
         <key>CFBundleTypeRole</key>
         <string>Editor</string>
         <key>LSHandlerRank</key>
         <string>Owner</string>
      </dict>
   </array>

When setting them up in a desktop project, make sure you set the Role and Rank properties to indicate to the system what your app does with the type (view, edit or execute) and how your app ranks in terms of the order of which apps get an opportunity to open the file (for imported types, you typically use Default, while Exported types typically use Owner).

1 Like

Yeah, so that works just fine… and you were right, they do come in through HandleURL. The URL that’s passed in is a file:// url.

Trying to edit now. Thanks for the help. I’ll report back!

Just to be clear, what I tested was going to a JPEG file in the Files app, touching Share and then selecting my app from the list of apps that could accept it:

This is exactly the workflow I’m trying to get to work.

One last thing, pay attention to the openInPlace parameter in HandleURL. If it’s set to True, the file has already been copied and you can open it directly. If it’s set to False however, you need to make a copy of the file yourself before the HandleURL event finishes or you’ll lose access to the file.

Oh, and you should return True to tell the system that you handled it.

Ok, so this works extremely well!

I tried to manually copy in my custom file type’s resource file (I copied it from the Mac build), but the iPhone doesn’t see it… or if it doesn’t it’s not using it.
Any idea how to make that work?

I did take note of that. Thanks!

Yes, you’ll need to copy the items I indicated above into their own plist file using a text editor. For instance, for my app it would look like this:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
   <key>CFBundleDocumentTypes</key>
   <array>
      <dict>
         <key>CFBundleTypeName</key>
         <string>JPEGimage</string>
         <key>CFBundleTypeExtensions</key>
         <array>
            <string>jpg</string>
         </array>
         <key>LSItemContentTypes</key>
         <array>
            <string>public.jpeg</string>
         </array>
         <key>CFBundleTypeRole</key>
         <string>Editor</string>
         <key>LSHandlerRank</key>
         <string>Default</string>
      </dict>
   </array>

   <key>UTImportedTypeDeclarations</key>
   <array>
      <dict>
         <key>UTTypeIdentifier</key>
         <string>public.jpeg</string>
         <key>UTTypeConformsTo</key>
         <array>
            <string>public.image</string>
            <string>public.data</string>
            <string>public.item</string>
            <string>public.content</string>
         </array>
         <key>UTTypeDescription</key>
         <string>JPEG Image File</string>
         <key>UTTypeTagSpecification</key>
         <dict>
            <key>public.filename-extension</key>
            <array>
            <string>jpg</string>
            </array>
            <key>public.mime-type</key>
            <array>
            <string>image/jpeg</string>
            </array>
         </dict>
      </dict>
   </array>
</dict>
</plist>

Basically, a plist file that only has the parts you need to insert, but not anything else. I suggest using a name like “plistadditions.plist” instead of “info.plist” to avoid issues.

Take this file and drag it directly into your project. When you build, the contents of the file will be merged with the plist information that Xojo generates when you build.

I don’t know if you know this, but on macOS you can drag and drop a file type onto the File Type Group editor and it will create a pre-populated file type for you directly from the Finder info. Very useful if you want to get it exactly right.

4 Likes

here’s the part I’m trying to figure out.
I copied the “myfiletype.icns” file to the app’s resource folder during compile.
The iOS device doesn’t use it.

UTTypeDescription
My File Type Description
UTTypeIconFile
myfiletype.icns
UTTypeTagSpecification

I suspect that the icns file needs to be in the same directory as the app on iOS.

yep… and if that doesn’t work, wiggle the handle and flush twice.
Reset the simulator and it’s all working.

Greg, you’ve been a huge help! Thank you.

2 Likes