How to make a message filter with subAction for iOS

378 Views Asked by At

I am making a sms message filter for iOS, I want to classify some message with subAction I returned subAction, But there is no classification in my iPhone(iOS 16), and my app will classify them to promotion or transaction this is my code:

import IdentityLookup
final class MessageFilterExtension: ILMessageFilterExtension {}

extension MessageFilterExtension: ILMessageFilterQueryHandling, ILMessageFilterCapabilitiesQueryHandling {
    func handle(_ capabilitiesQueryRequest: ILMessageFilterCapabilitiesQueryRequest, context: ILMessageFilterExtensionContext, completion: @escaping (ILMessageFilterCapabilitiesQueryResponse) -> Void) {
        let response = ILMessageFilterCapabilitiesQueryResponse()

        // TODO: Update subActions from ILMessageFilterSubAction enum
        completion(response)
    }

    func handle(_ queryRequest: ILMessageFilterQueryRequest, context: ILMessageFilterExtensionContext, completion: @escaping (ILMessageFilterQueryResponse) -> Void) {
        let (offlineAction, offlineSubAction) = self.offlineAction(for: queryRequest)
        print("====== STARTED ======")
        switch offlineAction {
        case .allow, .junk, .promotion, .transaction:
            // Based on offline data, we know this message should either be Allowed, Filtered as Junk, Promotional or Transactional. Send response immediately.
            let response = ILMessageFilterQueryResponse()
            response.action = offlineAction
            response.subAction = offlineSubAction

            completion(response)

        case .none:
            // Based on offline data, we do not know whether this message should be Allowed or Filtered. Defer to network.
            // Note: Deferring requests to network requires the extension target's Info.plist to contain a key with a URL to use. See documentation for details.
            context.deferQueryRequestToNetwork() { (networkResponse, error) in
                let response = ILMessageFilterQueryResponse()
                response.action = .none
                response.subAction = .none

                if let networkResponse = networkResponse {
                    // If we received a network response, parse it to determine an action to return in our response.
                    (response.action, response.subAction) = self.networkAction(for: networkResponse)
                } else {
                    NSLog("Error deferring query request to network: \(String(describing: error))")
                }

                completion(response)
            }

        @unknown default:
            break
        }
    }

    private func offlineAction(for queryRequest: ILMessageFilterQueryRequest) -> (ILMessageFilterAction, ILMessageFilterSubAction) {
        // TODO: Replace with logic to perform offline check whether to filter first (if possible).
        NSLog("recived msg: " + queryRequest.messageBody! + " sender: " + queryRequest.sender!)
        
        // reload setting
        SettingManager.getInstance().load()
        
        let whitelistResult = whitelistHandler(sender: queryRequest.sender!)
        if whitelistResult == .allow {
            return (.allow, .none)
        }
        let callingCodeResult = callingCodeHandler(sender: queryRequest.sender!)
        if  callingCodeResult != .none {
            return (callingCodeResult, .none)
        }
        if bombingHandler(msg: queryRequest.messageBody!) {
            return (.junk, .none)
        }
        let keywordResult = keywordHandler(msg: queryRequest.messageBody!)
        if  keywordResult != .none {
            return (keywordResult, .none)
        }
        
        let classificationResult = classifyHandler(msg: queryRequest.messageBody!)
        if classificationResult.0 != .none {
            return (classificationResult.0, classificationResult.1)
        }
        return (.none, .none)

    }


    func classifyHandler(msg: String) -> (ILMessageFilterAction, ILMessageFilterSubAction) {
        NSLog("checking msg's classification")
        let classificationFilters = DataManager.getInstance().getClassificationData()
        for filter in classificationFilters {
            NSLog("checking keyword: \(filter.keyword)")
            if (msg.contains(filter.keyword)) {
                NSLog("msg is in classification list, marked to \(filter.type)")
                return (filter.type.getAction(), filter.type.getSubAction())
            }
        }
        NSLog("msg is not in classification")
        return (.none, .none)
    }

    
    func whitelistHandler(sender: String) -> ILMessageFilterAction {
        ...
        return .none
    }
    
    func callingCodeHandler(sender: String) -> ILMessageFilterAction {
        ...
        return .none
    }
    
    // false: not need to filter
    // true: need filter
    func bombingHandler(msg: String) -> Bool {
        ...
        return false
    }
    
    func keywordHandler(msg: String) -> ILMessageFilterAction {
        ...
        return .none
    }
}

I tried to set response's promotionalSubActions and transactionSubActions in first handle function, But it does not works

1

There are 1 best solutions below

0
On

It might be late answering this question or you might have figured it out already but it's for someone who is wondering why this code haven't worked.

The problem here is in your offlineAction method where you are returning

ILMessageFilterAction and ILMessageFilterSubAction

Here you are returning .none in every second parameter

From your code :-

  • return (.allow, .none)
  • return (callingCodeResult, .none)
  • return (.junk, .none)
  • return (keywordResult, .none) etc...

Here the second parameter your returning is of type ILMessageFilterSubAction and returning .none here means you don't want to apply any subcategory based filteration to your incoming sms, so instead of returning .none, based on your logic you can return specifically like .transactionalFinance or promotionalCoupons etc

Here's the code snippet from my code of offlineAction method

    private func offlineAction(for queryRequest: ILMessageFilterQueryRequest) -> (ILMessageFilterAction, ILMessageFilterSubAction) {
    
    // TODO: Replace with logic to perform offline check whether to filter first (if possible).
    /// Offline logic goes here
    
    guard let messageBody = queryRequest.messageBody else { return (.none, .none) }
    switch messageBody {
    case _ where messageBody.lowercased().contains("code"):
        return (.transaction, .transactionalFinance)
    case _ where messageBody.lowercased().contains("discount"):
        return (.promotion, .promotionalCoupons)
    case _ where messageBody.lowercased().contains("earn"):
        return (.junk, .none)
    default:
        return (.none, .none)
    }  
}

Hope this helps...