summaryrefslogtreecommitdiff
path: root/Xamarin.Forms.Platform.iOS/iOSAppLinks.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Xamarin.Forms.Platform.iOS/iOSAppLinks.cs')
-rw-r--r--Xamarin.Forms.Platform.iOS/iOSAppLinks.cs190
1 files changed, 190 insertions, 0 deletions
diff --git a/Xamarin.Forms.Platform.iOS/iOSAppLinks.cs b/Xamarin.Forms.Platform.iOS/iOSAppLinks.cs
new file mode 100644
index 00000000..de5876b4
--- /dev/null
+++ b/Xamarin.Forms.Platform.iOS/iOSAppLinks.cs
@@ -0,0 +1,190 @@
+using System;
+using System.Threading.Tasks;
+#if __UNIFIED__
+using Foundation;
+using CoreSpotlight;
+using UIKit;
+
+#else
+using MonoTouch.Foundation;
+using MonoTouch.UIKit;
+using MonoTouch.CoreSpotlight;
+#endif
+
+namespace Xamarin.Forms.Platform.iOS
+{
+ internal class IOSAppLinks : IAppLinks
+ {
+ public async void DeregisterLink(IAppLinkEntry appLink)
+ {
+ if (string.IsNullOrWhiteSpace(appLink.AppLinkUri?.ToString()))
+ throw new ArgumentNullException("AppLinkUri");
+ await RemoveLinkAsync(appLink.AppLinkUri?.ToString());
+ }
+
+ public async void DeregisterLink(Uri uri)
+ {
+ if (string.IsNullOrWhiteSpace(uri?.ToString()))
+ throw new ArgumentNullException(nameof(uri));
+ await RemoveLinkAsync(uri.ToString());
+ }
+
+ public async void RegisterLink(IAppLinkEntry appLink)
+ {
+ if (string.IsNullOrWhiteSpace(appLink.AppLinkUri?.ToString()))
+ throw new ArgumentNullException("AppLinkUri");
+ await AddLinkAsync(appLink);
+ }
+
+ public async void DeregisterAll()
+ {
+ await ClearIndexedDataAsync();
+ }
+
+ static async Task AddLinkAsync(IAppLinkEntry deepLinkUri)
+ {
+ var appDomain = NSBundle.MainBundle.BundleIdentifier;
+ string contentType, associatedWebPage;
+ bool shouldAddToPublicIndex;
+
+ //user can provide associatedWebPage, contentType, and shouldAddToPublicIndex
+ TryGetValues(deepLinkUri, out contentType, out associatedWebPage, out shouldAddToPublicIndex);
+
+ //our unique identifier will be the only content that is common to spotlight search result and a activity
+ //this id allows us to avoid duplicate search results from CoreSpotlight api and NSUserActivity
+ //https://developer.apple.com/library/ios/technotes/tn2416/_index.html
+ var id = deepLinkUri.AppLinkUri.ToString();
+
+ var searchableAttributeSet = await GetAttributeSet(deepLinkUri, contentType, id);
+ var searchItem = new CSSearchableItem(id, appDomain, searchableAttributeSet);
+ //we need to make sure we index the item in spotlight first or the RelatedUniqueIdentifier will not work
+ await IndexItemAsync(searchItem);
+
+#if __UNIFIED__
+ var activity = new NSUserActivity($"{appDomain}.{contentType}");
+#else
+ var activity = new NSUserActivity (new NSString($"{appDomain}.{contentType}"));
+#endif
+ activity.Title = deepLinkUri.Title;
+ activity.EligibleForSearch = true;
+
+ //help increase your website url index rating
+ if (!string.IsNullOrEmpty(associatedWebPage))
+ activity.WebPageUrl = new NSUrl(associatedWebPage);
+
+ //make this search result available to Apple and to other users thatdon't have your app
+ activity.EligibleForPublicIndexing = shouldAddToPublicIndex;
+
+ activity.UserInfo = GetUserInfoForActivity(deepLinkUri);
+ activity.ContentAttributeSet = searchableAttributeSet;
+
+ //we don't need to track if the link is active iOS will call ResignCurrent
+ if (deepLinkUri.IsLinkActive)
+ activity.BecomeCurrent();
+
+ var aL = deepLinkUri as AppLinkEntry;
+ if (aL != null)
+ {
+ aL.PropertyChanged += (sender, e) =>
+ {
+ if (e.PropertyName == AppLinkEntry.IsLinkActiveProperty.PropertyName)
+ {
+ if (aL.IsLinkActive)
+ activity.BecomeCurrent();
+ else
+ activity.ResignCurrent();
+ }
+ };
+ }
+ }
+
+ static Task<bool> ClearIndexedDataAsync()
+ {
+ var tcs = new TaskCompletionSource<bool>();
+ if (CSSearchableIndex.IsIndexingAvailable)
+ CSSearchableIndex.DefaultSearchableIndex.DeleteAll(error => tcs.TrySetResult(error == null));
+ else
+ tcs.TrySetResult(false);
+ return tcs.Task;
+ }
+
+ static async Task<CSSearchableItemAttributeSet> GetAttributeSet(IAppLinkEntry deepLinkUri, string contentType, string id)
+ {
+ var searchableAttributeSet = new CSSearchableItemAttributeSet(contentType)
+ {
+ RelatedUniqueIdentifier = id,
+ Title = deepLinkUri.Title,
+ ContentDescription = deepLinkUri.Description,
+ Url = new NSUrl(deepLinkUri.AppLinkUri.ToString())
+ };
+
+ var source = deepLinkUri.Thumbnail;
+ IImageSourceHandler handler;
+ if (source != null && (handler = Registrar.Registered.GetHandler<IImageSourceHandler>(source.GetType())) != null)
+ {
+ UIImage uiimage;
+ try
+ {
+ uiimage = await handler.LoadImageAsync(source);
+ }
+ catch (OperationCanceledException)
+ {
+ uiimage = null;
+ }
+ searchableAttributeSet.ThumbnailData = uiimage.AsPNG();
+ uiimage.Dispose();
+ }
+
+ return searchableAttributeSet;
+ }
+
+ static NSMutableDictionary GetUserInfoForActivity(IAppLinkEntry deepLinkUri)
+ {
+ //this info will only appear if not from a spotlight search
+ var info = new NSMutableDictionary();
+ info.Add(new NSString("link"), new NSString(deepLinkUri.AppLinkUri.ToString()));
+ foreach (var item in deepLinkUri.KeyValues)
+ info.Add(new NSString(item.Key), new NSString(item.Value));
+ return info;
+ }
+
+ static Task<bool> IndexItemAsync(CSSearchableItem searchItem)
+ {
+ var tcs = new TaskCompletionSource<bool>();
+ if (CSSearchableIndex.IsIndexingAvailable)
+ {
+ CSSearchableIndex.DefaultSearchableIndex.Index(new[] { searchItem }, error => tcs.TrySetResult(error == null));
+ }
+ else
+ tcs.SetResult(false);
+ return tcs.Task;
+ }
+
+ static Task<bool> RemoveLinkAsync(string identifier)
+ {
+ var tcs = new TaskCompletionSource<bool>();
+ if (CSSearchableIndex.IsIndexingAvailable)
+ CSSearchableIndex.DefaultSearchableIndex.Delete(new[] { identifier }, error => tcs.TrySetResult(error == null));
+ else
+ tcs.SetResult(false);
+ return tcs.Task;
+ }
+
+ //Parse the KeyValues because user can provide associatedWebPage, contentType, and shouldAddToPublicIndex options
+ static void TryGetValues(IAppLinkEntry deepLinkUri, out string contentType, out string associatedWebPage, out bool shouldAddToPublicIndex)
+ {
+ contentType = string.Empty;
+ associatedWebPage = string.Empty;
+ shouldAddToPublicIndex = false;
+ var publicIndex = string.Empty;
+
+ if (!deepLinkUri.KeyValues.TryGetValue(nameof(contentType), out contentType))
+ contentType = "View";
+
+ if (deepLinkUri.KeyValues.TryGetValue(nameof(publicIndex), out publicIndex))
+ bool.TryParse(publicIndex, out shouldAddToPublicIndex);
+
+ deepLinkUri.KeyValues.TryGetValue(nameof(associatedWebPage), out associatedWebPage);
+ }
+ }
+} \ No newline at end of file