diff options
Diffstat (limited to 'Xamarin.Forms.Platform.iOS/iOSAppLinks.cs')
-rw-r--r-- | Xamarin.Forms.Platform.iOS/iOSAppLinks.cs | 190 |
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 |