Universal Linking for your iOS App

Despite of Deep Linking, Universal links can link a website domain with your mobile app without using any redirection on website. Your users can go through your app by clicking a https url, directly. (Directly, means your users don’t go Safari before opening of your application.) But how? iOS operation system can link mobile applications with domains. A mobile application can be link up to 30 domains.

To do so:

  • You need to create a contract file (in json format) that stores mobile app information on server side. (You need to specify )
  • Put this apple-app-site-association file under .well-known directory. You can this on Safari, by typing url https://DOMAIN_URL/apple-app-site-association . If you can reach this file, everything is OK and you’re ready to continue configurations on mobile project side.
  • Turn of Associate domains for your application. This will create an entitlement file and you need to update your provisioning profile. If you’re using Continuous Integration.

Read More »

How to add Core Spotlight to index your app content on iOS9? (ADVANCED)- iOS9 Feature (Part 4)

I’ve mentioned about “How to add Core Spotlight to index your app content on iOS9?” in this article. If you haven’t read it, I really recommend you do.

This article will be about how to add multiple search item in very little and efficient code writing. Search items have some features such as title, contentDescription, rating, unique identifier , thumbData which will show the icon of your special content or basically your application, and so on.

Basically there are two advanced concepts you should know about CSSearchableItems / CoreSpotlight.

First one is your user can navigate anywhere in your app, by understanding which search item is tapped by your user in Spotlight. You can be sure of your users will find anything they search, by navigating them true navigation point of application.

Apple serves very basic app delegate method :

– (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler;

Second one is that you can list your search items and their attributes in a very simple but handy property list (plist) file and creating a very simple singleton class you can call it SearchManager, will help you to separate your search methods from other app delegate methods.

Screen Shot 2016-01-18 at 21.36.06
SearchableItems.plist

Soo, let’s start!

In delegate of your application, you should call your SearchManager for initializing to index searchable items at spotlight.

APPDELEGATE.

- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
   [[SearchManager sharedInstance] initializeSpotlightSearch];
}
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler{

   if([userActivity.activityType compare: @"com.apple.corespotlightitem"]== NSOrderedSame){
      [[SearchManager sharedInstance] navigateFromSpotlightSearchItemContinueUserActivity:userActivity];
   }
return YES;
}

SearchManager.

You can find a brief explanation for following SearchManager code for Spotlight search.

#import "SearchManager.h"

@implementation SearchManager

static SearchManager *sharedInstance = nil;

NSString *const UNIQUE_IDENTIFIER_KEY       = @"uniqueIdentifier";
NSString *const DOMAIN_IDENTIFIER_KEY       = @"domainIdentifier";
NSString *const TITLE_KEY                   = @"title";
NSString *const CONTENT_DESCRIPTION_KEY     = @"contentDescription";
NSString *const THUMBNAIL_DATA_KEY          = @"thumbnailData";
NSString *const NAVIGATION_URL_KEY          = @"navigationURL";
NSString *const KEYWORDS_KEY                = @"keywords";
NSString *const KEYWORD_KEY                 = @"keyword";

+ (SearchManager *)sharedInstance {
    if (sharedInstance == nil) {
        sharedInstance = [[super allocWithZone:NULL] init];
    }
    
    return sharedInstance;
}

-(void)initializeSpotlightSearch{
//You should check if application is running iOS9 + devices,
//Otherwise your application will crash on iOS7 or iOS8 devices. 
    if(IS_IOS9_OR_GREATER){

        NSMutableArray *searchableItemList = [self getCSSearchableItemList];
        
        CSSearchableIndex *defaultSearchableIndex = [CSSearchableIndex defaultSearchableIndex];
        [defaultSearchableIndex indexSearchableItems:searchableItemList completionHandler:^(NSError * _Nullable error) {
            if (error)
                DLog(@"print error: %@", error.description);
            else
                DLog(@"indexed successfully");
        }];
    }
    
}

+ (NSDictionary *)getPropertyFileBySource:(NSString *)source andRoot:(NSString *)root {
    
    NSBundle *bundle = [NSBundle mainBundle];
    NSString *plistPath = [bundle pathForResource:source ofType:@"plist"];
    NSDictionary *serachableItemsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistPath];
    return serachableItemsDictionary[root];
    
}

-(NSDictionary*) getSearchableItemsPlistDictionary{
    return [SearchManager getPropertyFileBySource:@"SearchableItems" andRoot:@"spotlightSearchItems"];
}
- (NSMutableArray*)getCSSearchableItemList{
    NSDictionary *searchableItemsClientDictionary = [self getSearchableItemsPlistDictionary];
    
    return [self getCSSearchableItemList:searchableItemsClientDictionary];
}

- (NSMutableArray*)getCSSearchableItemList:(NSDictionary *)searchableItemsClientDictionary {
    
    NSMutableArray *searchableItemList = [[NSMutableArray alloc]init];
    
    for (NSString *searchableItemDictKey in [searchableItemsClientDictionary allKeys]) {
        NSDictionary *searchableItemDict = [searchableItemsClientDictionary objectForKey:searchableItemDictKey];
        
        NSString *uniqueIdentifier=[searchableItemDict objectForKey:UNIQUE_IDENTIFIER_KEY];
        NSString *domainIdentifier=[searchableItemDict objectForKey:DOMAIN_IDENTIFIER_KEY];
        NSString *contentDescription=[searchableItemDict objectForKey:TITLE_KEY];
        NSString *displayName=[searchableItemDict objectForKey:CONTENT_DESCRIPTION_KEY];
        NSString *title=[searchableItemDict objectForKey:TITLE_KEY];
        NSString *thumbnailData=[searchableItemDict objectForKey:THUMBNAIL_DATA_KEY];
        NSDictionary *keywords = [searchableItemDict objectForKey:KEYWORDS_KEY];
        
        NSMutableArray *keywordsArray = [[NSMutableArray alloc]init];
        for (NSString *akeywordKey in [keywords allKeys]) {
            [keywordsArray addObject:keywords[akeywordKey]];
        }
                
        
        CSSearchableItemAttributeSet *searchableItemAttributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:uniqueIdentifier];
        searchableItemAttributeSet.contentDescription = contentDescription;
        searchableItemAttributeSet.title = title;
        searchableItemAttributeSet.displayName = displayName;
        searchableItemAttributeSet.keywords = keywordsArray;
        if(IsEmpty(thumbnailData)){
            UIImage *thumbnail = [UIImage imageNamed:@"AppIcon"];
            searchableItemAttributeSet.thumbnailData = UIImageJPEGRepresentation(thumbnail, 0.7);
        }
        
        CSSearchableItem *searchableItem = [[CSSearchableItem alloc] initWithUniqueIdentifier:uniqueIdentifier
                                                                             domainIdentifier:domainIdentifier
                                                                                 attributeSet:searchableItemAttributeSet];
        
        [searchableItemList addObject:searchableItem];
        
    }
    return searchableItemList;
}

-(NSString*) getNavigationURLForSearchableItemKey:(NSString*)searchableItemKey{
    NSDictionary *searchableItemsClientDictionary = [self getSearchableItemsPlistDictionary];
    NSDictionary *searchItem = searchableItemsClientDictionary[searchableItemKey];
    if(!IsEmpty(searchItem)){
        return (NSString*)searchItem[NAVIGATION_URL_KEY];
    }
    return @"";
}
-(void) navigateFromSpotlightSearchItemContinueUserActivity:(NSUserActivity *)userActivity{
    NSDictionary *searchUserInfo = userActivity.userInfo;
    NSString *activityIdentifier = searchUserInfo[CSSearchableItemActivityIdentifier];
  //  NSString *activityType = searchUserInfo[CSSearchableItemActionType];
    
    NSString *navigationURL = [self getNavigationURLForSearchableItemKey:activityIdentifier];
    if(!IsEmpty(navigationURL)){
        // To do - write your codes for navigation, here.
    }
}

If you don’t want to navigate the spesific point at your application. You can ignore these following methods.
-(void) navigateFromSpotlightSearchItemContunieUserActivity:(NSUserActivity*)userActivity;
-(NSString*)getNavigationURLForSearchableItemKey:(NSString*) searchableItemKey;

PS: If you find this article useful please use like button 🙂 and if you have a question or a comment, don’t hesitate to share it!

May the Force Touch be with you! 🙂 

How to add Core Spotlight to index your app content on iOS9? – iOS9 Feature (Part 4)

Apple Says That;

When you make your content searchable, users can access activities and content deep within your app through Spotlight and Safari search results, Handoff, and Siri suggestions.

Point of an developer’s view, main aim of content indexing on iOS9 is to help you make your content available in the appropriate index, drive user engagement and maybe free marketing :D.Read More »

After Fabric IO upgrade, what did I experience in Jenkins build server?

And .. What did I learn about this process?

Upgrading Crashlytics to Fabric IO framework, is not very painful at all, unless you don’t use Jenkins build server for your build. In automated builds, I did stuck in situation like dSYM files weren’t uploaded automatically in every build.

So what are dSYM files ?

dSYM files store the debug symbols for your app. Services like Crashlytics uses these files to replace the symbols in the crash logs with the appropriate methods names, so it will be readable and will make sense.Read More »

How to implement Peek and Pop & show previewActionItems- iOS9 Feature (Part 2)

“Peek and Pop” is one of the great feature of 3D Touch. In my previous post I mentioned about “Home Screen Quick Actions” and adopting force touch, you can check this article here.
Peek And Pop feature allows users to preview a content (peek) with force touch and the content is opened in full size (popwith one more gentle move which keeps the previous touch up.Read More »