MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Objective-C中的Core Spotlight内容搜索集成

2021-06-083.4k 阅读

Core Spotlight简介

Core Spotlight是iOS 9引入的一项强大功能,它允许应用程序将其内容索引到系统搜索中。这意味着用户在使用设备的全局搜索功能(如在主屏幕上向下滑动)时,不仅可以搜索到系统自带应用的数据,还能搜索到集成了Core Spotlight的第三方应用的内容。这种无缝集成提升了用户体验,因为他们无需打开特定应用就能快速找到所需信息。

Core Spotlight的工作原理

Core Spotlight依赖于一个称为CSSearchableIndex的类来管理应用程序的搜索索引。当应用程序创建或更新内容时,它会向CSSearchableIndex添加或更新相应的搜索记录。这些记录包含了要被搜索的内容的元数据,如标题、描述、唯一标识符等。系统在后台会对这些索引进行优化,以便快速响应搜索请求。

当用户发起搜索时,系统会查询所有已注册的CSSearchableIndex,包括来自第三方应用的索引。如果搜索词匹配任何索引中的记录,相关结果就会显示在搜索结果列表中。用户点击结果后,系统会启动相应的应用,并将用户导航到与该搜索结果相关的内容页面。

在Objective - C中集成Core Spotlight

配置项目

首先,在Xcode项目中,需要确保应用支持Core Spotlight。这通常涉及在Info.plist文件中进行一些配置。打开项目的Info.plist文件,添加以下键值对:

<key>NSUserActivityTypes</key>
<array>
    <string>your.activity.type</string>
</array>

这里的your.activity.type是自定义的活动类型,用于标识应用内特定的活动。它应该遵循反向域名命名规则,例如com.example.app.activityType。这个活动类型将在后续配置搜索结果的深层链接时用到。

创建搜索记录

在Objective - C中,创建搜索记录涉及到CSSearchableItemAttributeSetCSSearchableItem这两个类。CSSearchableItemAttributeSet用于定义搜索记录的属性,如标题、描述、关键字等。而CSSearchableItem则将这些属性集与一个唯一标识符关联起来,形成一个完整的搜索记录。

以下是创建一个简单搜索记录的代码示例:

#import <CoreSpotlight/CoreSpotlight.h>
#import <MobileCoreServices/MobileCoreServices.h>

// 创建一个CSSearchableItemAttributeSet对象
CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeText];
attributeSet.title = @"示例文章";
attributeSet.contentDescription = @"这是一篇关于Core Spotlight集成的示例文章";
attributeSet.keywords = @[@"Core Spotlight", @"iOS开发", @"Objective - C"];

// 创建一个CSSearchableItem对象
CSSearchableItem *searchableItem = [[CSSearchableItem alloc] initWithUniqueIdentifier:@"article1" domainIdentifier:@"com.example.app" attributeSet:attributeSet];

在上述代码中,首先创建了一个CSSearchableItemAttributeSet对象,并设置了标题、描述和关键字。itemContentType指定了内容的类型,这里使用kUTTypeText表示文本类型。然后,通过CSSearchableItem的初始化方法,将唯一标识符article1、域名标识符com.example.app与属性集关联起来,创建了一个完整的搜索记录。

添加搜索记录到索引

创建好搜索记录后,需要将其添加到CSSearchableIndex中。CSSearchableIndex类提供了一个共享实例,可以通过defaultSearchableIndex方法获取。

CSSearchableIndex *searchableIndex = [CSSearchableIndex defaultSearchableIndex];
[searchableIndex indexSearchableItems:@[searchableItem] completionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"添加搜索记录失败: %@", error);
    } else {
        NSLog(@"搜索记录添加成功");
    }
}];

在这段代码中,获取了CSSearchableIndex的共享实例,并调用indexSearchableItems:completionHandler:方法将之前创建的搜索记录添加到索引中。完成处理程序会在操作完成后被调用,通过检查error参数可以判断操作是否成功。

更新和删除搜索记录

如果应用中的内容发生了变化,可能需要更新或删除相应的搜索记录。

更新搜索记录

更新搜索记录的过程与添加类似,只需要创建一个具有相同唯一标识符但属性已更新的新CSSearchableItem,然后调用indexSearchableItems:方法。例如,如果要更新之前创建的文章的标题:

// 更新CSSearchableItemAttributeSet的标题
attributeSet.title = @"更新后的示例文章";

// 创建一个新的CSSearchableItem,使用相同的唯一标识符
CSSearchableItem *updatedSearchableItem = [[CSSearchableItem alloc] initWithUniqueIdentifier:@"article1" domainIdentifier:@"com.example.app" attributeSet:attributeSet];

// 更新索引
[searchableIndex indexSearchableItems:@[updatedSearchableItem] completionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"更新搜索记录失败: %@", error);
    } else {
        NSLog(@"搜索记录更新成功");
    }
}];

删除搜索记录

删除搜索记录可以通过调用CSSearchableIndexdeleteSearchableItemsWithIdentifiers:completionHandler:方法实现。例如,要删除之前创建的文章的搜索记录:

[searchableIndex deleteSearchableItemsWithIdentifiers:@[@"article1"] completionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"删除搜索记录失败: %@", error);
    } else {
        NSLog(@"搜索记录删除成功");
    }
}];

处理搜索结果

当用户点击搜索结果时,应用需要处理该点击事件,并将用户导航到相应的内容页面。这涉及到在应用的AppDelegate中实现application:continueUserActivity:restorationHandler:方法。

- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
    if ([userActivity.activityType isEqualToString:@"your.activity.type"]) {
        NSString *uniqueIdentifier = userActivity.userInfo[CSSearchableItemActivityIdentifier];
        // 根据唯一标识符导航到相应的内容页面
        // 这里假设存在一个ArticleViewController用于显示文章内容
        ArticleViewController *articleVC = [[ArticleViewController alloc] initWithArticleID:uniqueIdentifier];
        UINavigationController *navController = (UINavigationController *)self.window.rootViewController;
        [navController pushViewController:articleVC animated:YES];
        return YES;
    }
    return NO;
}

在上述代码中,首先检查userActivityactivityType是否与在Info.plist中定义的活动类型一致。如果一致,从userActivityuserInfo字典中获取搜索记录的唯一标识符。然后,根据这个唯一标识符创建相应的视图控制器,并将其推送到导航堆栈中,实现用户到具体内容页面的导航。

Core Spotlight的高级应用

搜索记录的权重设置

Core Spotlight允许为搜索记录设置权重,以影响它们在搜索结果中的排名。可以通过CSSearchableItemAttributeSetweight属性来设置权重。权重值的范围是从0.0(最低权重)到1.0(最高权重)。例如,对于更重要的文章,可以设置较高的权重:

// 设置较高的权重
attributeSet.weight = 0.8;

这样,在搜索结果中,权重较高的记录更有可能出现在靠前的位置。

搜索记录的分组

有时候,可能希望将搜索记录进行分组展示,以便用户更清晰地浏览搜索结果。Core Spotlight支持通过domainIdentifier来实现分组。如果多个搜索记录具有相同的domainIdentifier,它们将在搜索结果中被分组显示。例如,可以根据文章的类别来设置domainIdentifier

// 假设文章属于“技术”类别
CSSearchableItem *techArticle = [[CSSearchableItem alloc] initWithUniqueIdentifier:@"techArticle1" domainIdentifier:@"com.example.app.tech" attributeSet:techAttributeSet];
// 假设文章属于“生活”类别
CSSearchableItem *lifeArticle = [[CSSearchableItem alloc] initWithUniqueIdentifier:@"lifeArticle1" domainIdentifier:@"com.example.app.life" attributeSet:lifeAttributeSet];

在搜索结果中,属于“技术”类别的文章会被归为一组,属于“生活”类别的文章会被归为另一组。

搜索记录的过滤

在某些情况下,可能只希望特定类型的搜索记录出现在搜索结果中。可以通过设置CSSearchableIndexfilter属性来实现过滤。例如,如果只想显示文本类型的搜索记录,可以这样设置:

CSSearchableIndex *searchableIndex = [CSSearchableIndex defaultSearchableIndex];
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"contentType == %@", (NSString *)kUTTypeText];
searchableIndex.filter = predicate;

上述代码创建了一个谓词,用于筛选出内容类型为文本的搜索记录,并将其设置为CSSearchableIndex的过滤条件。这样,在搜索时,只有符合过滤条件的记录才会出现在搜索结果中。

Core Spotlight的性能优化

批量操作

为了提高性能,尽量使用批量操作来添加、更新或删除搜索记录。例如,一次添加多个搜索记录:

// 创建多个搜索记录
CSSearchableItem *item1 = [[CSSearchableItem alloc] initWithUniqueIdentifier:@"item1" domainIdentifier:@"com.example.app" attributeSet:attributeSet1];
CSSearchableItem *item2 = [[CSSearchableItem alloc] initWithUniqueIdentifier:@"item2" domainIdentifier:@"com.example.app" attributeSet:attributeSet2];

// 批量添加
[searchableIndex indexSearchableItems:@[item1, item2] completionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"批量添加搜索记录失败: %@", error);
    } else {
        NSLog(@"批量搜索记录添加成功");
    }
}];

通过批量操作,可以减少与系统索引交互的次数,从而提高效率。

异步操作

所有与CSSearchableIndex的交互操作,如添加、更新和删除,都应该在后台线程中进行,以避免阻塞主线程,影响应用的响应性。可以使用dispatch_async来实现异步操作:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    [searchableIndex indexSearchableItems:@[searchableItem] completionHandler:^(NSError * _Nullable error) {
        if (error) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"添加搜索记录失败: %@", error);
            });
        } else {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"搜索记录添加成功");
            });
        }
    }];
});

在上述代码中,首先在后台队列中执行添加搜索记录的操作。如果操作完成后有错误或成功的消息需要处理,再将相应的日志打印操作切换到主线程执行,以确保UI相关的操作在主线程进行。

索引维护

定期清理不再需要的搜索记录,以保持索引的整洁和高效。例如,当应用中的某些内容被永久删除时,相应的搜索记录也应该从索引中删除。同时,对于更新频率较高的内容,合理安排更新时间,避免过于频繁地更新索引,从而减少性能开销。

Core Spotlight与其他技术的结合

与Core Data的结合

如果应用使用Core Data来管理数据,可以很方便地将Core Data中的数据与Core Spotlight集成。每当Core Data中的数据发生变化(添加、更新或删除)时,相应地更新Core Spotlight的索引。例如,假设应用有一个Article实体在Core Data中,并且有一个对应的Article类:

// 获取Core Data上下文
NSManagedObjectContext *context = [self persistentContainer.viewContext];

// 获取所有文章
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Article"];
NSArray<Article *> *articles = [context executeFetchRequest:fetchRequest error:nil];

// 创建搜索记录数组
NSMutableArray<CSSearchableItem *> *searchableItems = [NSMutableArray array];
for (Article *article in articles) {
    CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeText];
    attributeSet.title = article.title;
    attributeSet.contentDescription = article.summary;
    attributeSet.keywords = article.keywords;
    
    CSSearchableItem *searchableItem = [[CSSearchableItem alloc] initWithUniqueIdentifier:article.articleID domainIdentifier:@"com.example.app" attributeSet:attributeSet];
    [searchableItems addObject:searchableItem];
}

// 批量添加搜索记录
CSSearchableIndex *searchableIndex = [CSSearchableIndex defaultSearchableIndex];
[searchableIndex indexSearchableItems:searchableItems completionHandler:^(NSError * _Nullable error) {
    if (error) {
        NSLog(@"添加搜索记录失败: %@", error);
    } else {
        NSLog(@"搜索记录添加成功");
    }
}];

上述代码从Core Data中获取所有文章,为每篇文章创建一个搜索记录,并批量添加到Core Spotlight索引中。当文章数据在Core Data中更新或删除时,也需要相应地更新或删除Core Spotlight中的搜索记录,以保持数据的一致性。

与CloudKit的结合

对于使用CloudKit进行数据存储和同步的应用,可以将CloudKit中的数据集成到Core Spotlight。CloudKit提供了一种方便的方式来在多个设备之间同步数据,而Core Spotlight则提供了本地搜索功能。例如,当用户在一个设备上创建了一篇新文章并同步到CloudKit后,其他设备可以获取到该文章并将其添加到Core Spotlight索引中。

首先,需要在应用启动时,从CloudKit中获取最新的数据,并更新Core Spotlight索引。假设应用使用CKQuery来查询CloudKit中的文章数据:

// 创建一个查询,获取所有文章
CKQuery *query = [[CKQuery alloc] initWithRecordType:@"Article" predicate:[NSPredicate predicateWithValue:YES]];

CKQueryOperation *queryOperation = [[CKQueryOperation alloc] initWithQuery:query];
queryOperation.recordFetchedBlock = ^(CKRecord * _Nonnull record) {
    CSSearchableItemAttributeSet *attributeSet = [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeText];
    attributeSet.title = record[@"title"];
    attributeSet.contentDescription = record[@"summary"];
    attributeSet.keywords = record[@"keywords"];
    
    CSSearchableItem *searchableItem = [[CSSearchableItem alloc] initWithUniqueIdentifier:record.recordID.recordName domainIdentifier:@"com.example.app" attributeSet:attributeSet];
    
    CSSearchableIndex *searchableIndex = [CSSearchableIndex defaultSearchableIndex];
    [searchableIndex indexSearchableItems:@[searchableItem] completionHandler:^(NSError * _Nullable error) {
        if (error) {
            NSLog(@"添加搜索记录失败: %@", error);
        } else {
            NSLog(@"搜索记录添加成功");
        }
    }];
};

queryOperation.queryCompletionBlock = ^(CKQueryCursor * _Nullable cursor, NSError * _Nullable error) {
    if (error) {
        NSLog(@"查询CloudKit数据失败: %@", error);
    }
};

[[CKContainer defaultContainer].publicCloudDatabase addOperation:queryOperation];

上述代码创建了一个CKQuery来获取所有Article类型的记录。当获取到每条记录时,为其创建一个搜索记录并添加到Core Spotlight索引中。通过这种方式,实现了CloudKit数据与Core Spotlight的集成,使用户可以在本地快速搜索到来自CloudKit的内容。

通过以上详细的介绍和代码示例,相信你对Objective - C中Core Spotlight内容搜索集成有了深入的了解,可以在自己的应用中灵活运用这一强大功能,提升用户体验。无论是简单的内容搜索集成,还是与其他数据管理技术的结合,Core Spotlight都为应用开发带来了更多的可能性。在实际开发过程中,要根据应用的具体需求和特点,合理地进行配置和优化,以达到最佳的搜索效果和性能表现。