Developer Guide for Sharing wif Format on macOS

On macOS or IOS, the user sees the file extension that seems to look just like Windows, but behind the scenes, it is more complex using a Uniform Type Identifier, UTI, that marks each file to specific application. Extensions are not meant to be shared.

Since Apple doesn't provide a standard way to share a file extension for groups that don't have a specific owner of the standard, we need a group way of adapting to a standard. For wif, that will require a two phase approach. The goal is to have a single UTI that we all declare for out wif format. With a single UTI, then all apps can open and save in that format.

Since there are already multiple apps that have defined the UTI using the Apple specification, a two phased approach is needed to reach our goal for sharing. The diagram below shows an example of this approach.

UTI Hierarchy for WIF File Format

Chart shows transitional hierarchy. The private legacy UTI are needed to support customers until all apps conform to the shared canonical UTI. Once that happens, then the second layer of declartions is unnecessary. In the final phase, all apps declare the canonical UTI and don't need the others.

                        public.data
                               │
                               ▼
                    org.wif-format.wif
                 (shared canonical UTI)
                               │
        ┌──────────────────────┼──────────────────────┐...more Apps
        ▼                      ▼                      ▼
com.canyonart.weaveit.wif   com.fiberworks.wif   com.tempoweave.wif
 (private legacy UTI)       (private legacy UTI) (private legacy UTI)
        │                      │                      │
        └────────────── conforms to ──────────────────┘

Explanation:

Transitional Phase Requirements

Apps still have legacy private UTIs in the wild.

Goal: Avoid breaking existing installs while moving toward the shared UTI

During the interm period until others have updated to org.wif-format.wif it is necessary to list all the apps in the LSItemContentTypes Once they have imported your UTI, then this is not required.

During the transition period, private wif UTIs need to be listed as LSItemContentTypes of the shared UTI. This enables all to be listed in "Open With". You don't need to list your own if you export it. Once all these apps have added org.wif-format.wif to their declarations then it will not be necessary to list them. Of course, if more apps are involved, they will need to be added to the list. The current list of private UTIs that you should list are:

Do not import your own UTI. There should only be either an export or import for a UTI.

Each time a new app releases on Mac, the list will expand for all. It is not clear who will win the LaunchServices priority and that can change with updates and new additions.

The final requirement is: Do Not call "LSSetDefaultRoleHandlerForContentType" in your applicationDidFinishingLaunching. This function sometimes works and other times not, and violates a user's ability to set the default app to open the wif file.

Download Transitional Code Example for Info.plist

TIP: In Xcode, open the Info.plist as 'source code' to edit the xml.
You can add any other keys that you use. This is the minimal set for sharing.
Use the download link above for the code as the example below is full of html for your review.

<!-- ============================================== -->
<!-- DOCUMENT TYPES -->
<!-- Associates file types with your app in Finder -->
<!-- The list below only includes type for .wif -->
<!-- Add Bundle Types for your private file formats -->
<!-- You may want to add your NSDocumentClass for <!-- reading the wif file. -->
<!-- ============================================== -->
<key>CFBundleDocumentTypes</key>
<array> <!-- The shared WIF format -->
<dict>
 <key>CFBundleTypeName</key>
   <string>Weaving Info Format</string>
 <key>CFBundleTypeRole</key>
   <string>Editor</string>
  <key>CFBundleTypeRank</key>
   <string>Default</string>
 <key>LSItemContentTypes</key>
  <array>
   <string>org.wif-format.wif</string>
   <string>com.canyonart.weaveit.wif</string> <!--Temporary-->
   <string>com.fiberworks-pcw.wif</string> <!--Temporary-->
   <string>com.loftyfiber.wif</string> <!--Temporary-->
  </array>
</dict>
<!-- Your app's private format for wif --><!--Temporary-->
<dict>
 <key>CFBundleTypeName</key>
   <string>The App Weaving File</string>
 <key>CFBundleTypeRole</key>
   <string>Editor</string> <!-- if only reading, then use Viewer -->
  <key>CFBundleTypeRank</key>
   <string>Owner</string> <!-- You are owner of your format -->
 <key>LSItemContentTypes</key>
  <array>
   <string>com.myApp.wif</string> <!-- replace with your UTI -->
  </array>
</dict>

<!--Add Your app's private formats -->
</array>


<!-- ============================================ -->
<!-- EXPORTED TYPE DECLARATIONS -->
<!-- Declares UTIs that your app owns -->
<!-- Both must be exported so they are recognized -->
<!-- on any device where your app is installed -->
<!-- This only shows the export for wif extension -->
<!-- ============================================ -->
<key>UTExportedTypeDeclarations</key>
<array>

<!-- Export the shared WIF format UTI -->
<!-- This ensures it is known on any -->
<!-- device with your app installed -->
<dict>
 <key>UTTypeIdentifier</key>
   <string>org.wif-format.wif</string>
 <key>UTTypeDescription</key>
   <string>Weaving Information File</string>
 <key>UTTypeConformsTo</key>
  <array>
   <string>public.data<string>
  </array>
 <key>UTTypeTagSpecification</key>
  <dict>
   <key>public.filename-extension</key>
   <array>
    <string>wif</string>
   </array>
   </dict>
</dict>

<!-- Export your app's private wif UTI -->
<!-- which conforms to the shared -->
<!-- org.wif-format.wif UTI -->
<dict>
 <key>UTTypeIdentifier</key>
  <string>com.yourapp.wif</string>
 <key>UTTypeDescription</key>
  <string>Your App Wif File</string>
 <key>UTTypeConformsTo</key>
  <array>
 <string>org.wif-format.wif</string>
  </array>
 <key>UTTypeTagSpecification</key>
  <dict>
   <key>public.filename-extension</key>
   <array>
    <string>wif</string>
   </array>
  </dict>
</dict>
</array>

Final Phase Requirements

Once all the apps have exported the org.wif-format.wif UTI, life becomes much simpler. The only file declaration need is the shared format, org.wif-format.wif. The only export need is also org.wif-format.wif. We will all be using the shared file type. The only reason for imports, might be for legacy files.

The example code below if for the final phase. It is full of html, so don't download this.

Download the above example and make changes to that code by removing temporary items.

<key>CFBundleDocumentTypes</key>
<array> <!-- The shared WIF format -->
<dict>
 <key>CFBundleTypeName</key>
   <string>Weaving Info Format</string>
 <key>CFBundleTypeRole</key>
   <string>Editor</string>
  <key>CFBundleTypeRank</key>
   <string>Default</string>
 <key>LSItemContentTypes</key>
  <array>
   <string>org.wif-format.wif</string>
  </array>
</dict>
</array> <key>UTExportedTypeDeclarations</key>
<array>

<!-- Export the shared WIF format UTI -->
<!-- This ensures it is known on any -->
<!-- device with your app installed -->
<dict>
 <key>UTTypeIdentifier</key>
   <string>org.wif-format.wif</string>
 <key>UTTypeDescription</key>
   <string>Weaving Information File</string>
 <key>UTTypeConformsTo</key>
  <array>
   <string>public.data<string>
  </array>
 <key>UTTypeTagSpecification</key>
  <dict>
   <key>public.filename-extension</key>
   <array>
    <string>wif</string>
   </array>
   </dict>
</dict>

Final Thoughts

This code was designed using multiple AI apps. Those apps are also very human like, because in many places they disagreed slightly. It took many detailed questions and many iterations for them to agree. Hopefully we all will have a better understanding of what is needed.

I, Sally Breckenridge, did submit the transitional format and it does work. There is still much disagreement with the AI apps on how Apple's LaunchServices prioritizes app ownership. In the current situation only one app wins. But that is not always a win, because customers are then forced to decide which version they want and delete the others. The newer apps tend to lose that choice as people don't like change. Even if your app is the winner, tomorrow a new app may be launched to change that. Take the time to unite together as they did in 1997.