iosswiftcorespotlight

How to make CoreSpotlight predict who is calling


I want my device to predict who is calling to me based on spotlight people index. I have uploaded people info to spotlight index and system gives info when I'm searching, but not when somebody is calling. The code below do all this stuff and I can't understand what's wrong

if people.count > 0 {
    var peopleArray = [CSSearchableItem]()
    var peopleGUIDs = [String]()
    for person in people {
        let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String)

        // Basic AttributeSet setup
        attributeSet.title = person.nameForList
        attributeSet.contentDescription = person.division?.title

        // Add first phone number to AttributeSet
        var phoneNumber: NSString?
        let contacts = Array(person.contacts)
        for contact in contacts {
            if contact.type == "phone" {
                phoneNumber = contact.value as NSString
                break
            }
        }
        if phoneNumber != nil {
            if let preparedNumber = phoneNumber!.removingPercentEncoding {
                attributeSet.phoneNumbers = [preparedNumber]
                attributeSet.supportsPhoneCall = true
            }
        }

        attributeSet.displayName = person.name

        // Add photo number to AttributeSet
        if let photoPath = person.photo {
            let key = SDWebImageManager.shared().cacheKey(for: NSURL(string: photoPath) as URL!)
            let image = SDImageCache.shared().imageFromDiskCache(forKey: key)
            var data = Data()
            if let image = image {
                if let dataFromImage = UIImagePNGRepresentation(image) {
                    data = dataFromImage
                }
            } else {
                data = dataFromImage
            }
            attributeSet.thumbnailData = data
        }

        peoplesGUIDs.append(person.id)

        let item = CSSearchableItem(uniqueIdentifier: person.id, domainIdentifier: "com.it.companySpotlight", attributeSet: attributeSet)
        peopleArray.append(item)
    }

    CSSearchableIndex.default().indexSearchableItems(peopleArray) {  (error) in
        DispatchQueue.main.async(execute: {
            if let error =  error {
                print("Indexing error: \(error.localizedDescription)")
            } else {
                print("Search for people successfully indexed")
            }
        })
    }

}

Does anybody know how to solve this problem?


Solution

  • After a short time, Paulw11 said that I need to user CallKit extension, so there is the solution:

    1. Add new target to your project "CallKIt extension"
    2. Create app groups to provide text file with phone number to your extension, because it's not possible to use there databases
    3. Be sure that you contacts are in numerically ascending order for better performance
    4. Write contacts to file

      if #available(iOS 10.0, *) {
          let numbers = ["79175870629"]
      
          let labels = ["Stranger name"]
      
          // Replace it with your id
          let groupId = "group.YOUR.ID"
          let container = FileManager.default
              .containerURL(forSecurityApplicationGroupIdentifier: groupId)
          guard let fileUrl = FileManager.default
              .containerURL(forSecurityApplicationGroupIdentifier: groupId)?
              .appendingPathComponent("contacts") else { return }
      
          var string = ""
          for (number, label) in zip(numbers, labels) {
              string += "\(number),\(label)\n"
          }
      
          try? string.write(to: fileUrl, atomically: true, encoding: .utf8)
      
          CXCallDirectoryManager.sharedInstance.reloadExtension(
              withIdentifier: groupId)
      } else {
          // Fallback on earlier versions
      }
      
    5. Then add class LineReader to you extension from this post
    6. This method will be called when you call reloadExtension

      override func beginRequest(with context: CXCallDirectoryExtensionContext) {
      context.delegate = self
      if #available(iOSApplicationExtension 11.0, *) {
          if context.isIncremental {
              addOrRemoveIncrementalBlockingPhoneNumbers(to: context)
              addOrRemoveIncrementalIdentificationPhoneNumbers(to: context)
          } else {
              addAllBlockingPhoneNumbers(to: context)
              addAllIdentificationPhoneNumbers(to: context)
          }
      } else {
          addAllBlockingPhoneNumbers(to: context)
          addAllIdentificationPhoneNumbers(to: context)
      }
      
      
      context.completeRequest()
      

      }

    7. In my case,I only implemented addAllIdentificationPhoneNumbers and read there contacts from file. You need to add logic to all other methods that was generated by default

      guard let fileUrl = FileManager.default
                  .containerURL(forSecurityApplicationGroupIdentifier: "group.YOUR.ID")?
                  .appendingPathComponent("contacts") else { return }
      
              guard let reader = CBLineReader(path: fileUrl.path) else { return }
              print("\(#function) \(fileUrl)")
              for line in reader {
                  autoreleasepool {
                      let line = line.trimmingCharacters(in: .whitespacesAndNewlines)
      
                      var components = line.components(separatedBy: ",")
      
                      guard let phone = Int64(components[0]) else { return }
                      let name = components[1]
      
                      context.addIdentificationEntry(withNextSequentialPhoneNumber: phone, label: name)
                      print(#function + name)
                  }
              }
      
    8. Go to Settings -> Phone -> Call Blocking & Identification -> turn on swift opposite your app

    9. Test your app :-) Hope it will help to someone