How to create a message app filter extension for iPhone?

As mentioned here, to create an extension all you have to do is create a new iOS app and from the Xcode menu select File/New/Target and select Message Filter Extension.

In your extension a class that inherits from ILMessageFilterExtension is automatically created for you. It also conforms to the ILMessageFilterQueryHandling protocol which has a single requirement, the handle(_ queryRequest:context:completion:) method.

In this method a query request object of type ILMessageFilterQueryRequest is passed to you and you will have access to the sender and messageBody of the message.

After you apply your filter rules, you need to call the completion closure which will take a single parameter of type ILMessageFilterQueryResponse that you need to create and set it’s action property.

If you want to prevent the message from being shown you need to set the action type to .filter. The other 2 options .none and .allow will have no effect, the message will be shown normally.

That’s all you have to do to create a Message Filter Extension.

Below is an example from the Filter Spam SMS app that uses a CoreData shared container to load the list of keywords created by the user, which are used to filter the message body:

import IdentityLookup

final class MessageFilterExtension: ILMessageFilterExtension {
var words:[Item] = []
let stack = CoreDataStack()

func loadItems() {
    let context = stack.persistentContainer.viewContext
    let itemDAO = ItemDAO(managedObjectContext: context)
    let allItems = itemDAO.fetchItmes()
    self.words = allItems.flatMap({ item in
        return item.value != nil ? item : nil

extension MessageFilterExtension: ILMessageFilterQueryHandling {

func handle(_ queryRequest: ILMessageFilterQueryRequest, context: ILMessageFilterExtensionContext, completion: @escaping (ILMessageFilterQueryResponse) -> Void) {

    let action = self.offlineAction(for: queryRequest)
    let response = ILMessageFilterQueryResponse()
    response.action = action

private func offlineAction(for queryRequest: ILMessageFilterQueryRequest) -> ILMessageFilterAction {
    guard let messageBody = queryRequest.messageBody?.lowercased() else { return .none }


    for word in self.words {
        if let value = word.value,
            messageBody.contains(value.lowercased()) {
            return .filter
    return .allow

Leave a Comment