I'm trying to implement a Xamarin Forms Share Extension to allow my app to receive a zip file from another app.
I have successfully set up the App Group in the Apple Developer Account, am successfully able to see a shared url for the file, but am now having problems trying to sync the file back to the Container app via NSUserDefaults.
Here is my code with logging statements to cater for the fact that Extension apps can't be debugged in Xamarin Forms (as far as I know):
internal class CodeBasedViewController : SLComposeServiceViewController
{
public override async void DidSelectPost()
{
int count = 0;
try
{
NSExtensionItem item = ExtensionContext.InputItems[0];
foreach(NSItemProvider prov in item.Attachments)
{
Log(count++.ToString());
if (prov.HasItemConformingTo(UTType.URL) == true)
{
Log("url type ok");
//Load item with Load Completion action
prov.LoadItem(UTType.URL, null, async (dataRaw, error) =>
{
Log("load url item ok");
//dataRaw is "file:///Users/appname/Library/Developer/CoreSimulator/Devices/..GUID1../data/Containers/Shared/AppGroup/...GUID2.../
string uri = dataRaw.ToString();
Log2(uri);
NSUserDefaults shared = new NSUserDefaults(
"group.com.companyname.appname.ShareExtension",
NSUserDefaultsType.SuiteName);
shared.SetString(uri, "zipfile");
if (shared.Synchronize())
{
//sync completes successfully and code comes here
Log("sync success");
}
else
Log("sync failed");
//base.DidSelectPost();
});
}
}
}
catch (Exception e)
{
Log("ex: " + e.Message);
}
}
}
I have several questions or problems:
- The
LoadItemCompletion action takes about 7 secs to come through which seems like a long time for a 6kb file. - If I uncomment
base.DidSelectPost()the Extension is dismissed almost immediately upon clicking Post, making me suspect my Completion action code is being bypassed. - When I look at
NSUserDefaultsupon my Container apps activation, the uri value hasn't come through. (I am guessing if I can get this uri value in my Container, I will finally have access to the zip file.)
public override void OnActivated(UIApplication uiApplication)
{
base.OnActivated(uiApplication);
NSUserDefaults shared = new NSUserDefaults(
"group.com.companyname.appname.ShareExtension",
NSUserDefaultsType.SuiteName);
//ok = true
bool ok = shared.Synchronize();
//this is null!!
string uri = shared.StringForKey("zipfile");
}
Any ideas on what is going wrong here?
UPDATE
I've gone into my host VM and created two XCode apps which mirror the bundle names of my container and share extension apps on my Windows VS machine. The XCode apps also have AppGroups configured to them.
This still doesn't work. I'm not sure what else I can try. Any ideas?
UPDATE 02
I'm posting my Info.plist files here to provide more info.
Can anyone see if I'm doing something wrong here:
Container App
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>MinimumOSVersion</key>
<string>8.0</string>
<key>CFBundleDisplayName</key>
<string>MyApp</string>
<key>CFBundleIdentifier</key>
<string>com.companyname.MyApp</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleName</key>
<string>MyApp</string>
<key>XSAppIconAssets</key>
<string>Assets.xcassets/AppIcon.appiconset</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</plist>
SharedApp
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>MyApp ShareExtension</string>
<key>CFBundleExecutable</key>
<string>MyApp.iOS.ShareExtension</string>
<key>CFBundleIdentifier</key>
<string>com.companyname.MyApp.ShareExtension</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>com.your-company.MyApp.iOS.ShareExtension</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>MinimumOSVersion</key>
<string>8.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>0</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionPrincipalClass</key>
<string>CodeBasedViewController</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>
I figured out the answer.
In addition to the above settings I also need to add the Entitlements.plist to the respective container and extension projects. I thought that was done by default, simply by being a part of the project but I was wrong.
You also need to:
Do this both for container and extension apps.
NSUserDefaults should then be visible in both container and extension apps!