스팸 전화좀 그만해라 (1)

2024년 11월 28일
8

스팸 문자/전화좀 그만해라스팸 문자/전화좀 그만해라

잡담

요즘 매일 나를 반겨주는 전화들이 있다.

070-4768-xxxx
070-4499-xxxx
070-7738-xxxx

매번 인터넷을 바꾸라니, 통신사를 바꾸라니 하는 전화인데,
뒤에 4자리만 변경해서 하루에 한번씩 랜덤시간에 전화가 온다.

문제는, 요즘 이리저리 기다리는 전화들이 있다는 것인데...

거기서 070번호를 쓸일이 없겟지만, 연락을 받아야되는 입장에서는 아주 짜증나는 일이 아닐수 없다.

이전에 후후앤컴퍼니에서 관련 로직을 구현해본 적도 있어서,
차단부분만 간단한 예제로 만들어보려고 한다.


목표

모든 070전화 차단

iOS에서는 앱에서 시스템의 전화와 관련되어, 동작을 제어(?)할 수 있는 Callkit을 제공한다.

솔직히 차단에 있어서는, 제어보다는 별도 공간에 데이터를 넣고,
전화앱에서 이 부분을 참조해주세요! 라고 표헌하는게 맞는것 같지만...

차단을 위해서는 Callkit - CallDirectoryExteion을 추가하면 되는데,

class CallDirectoryHandler: CXCallDirectoryProvider {
    override func beginRequest(with context: CXCallDirectoryExtensionContext) {
        if context.isIncremental {
            context.removeAllBlockingEntries()
            context.removeAllIdentificationEntries()
        }
        context.addBlockingEntry(withNextSequentialPhoneNumber: CXCallDirectoryPhoneNumber("+827047689483")!)
        context.addIdentificationEntry(withNextSequentialPhoneNumber: CXCallDirectoryPhoneNumber("+827047689483")!, label: "070전화")
        context.completeRequest()
    }
}

위 코드만 알면 다 안다고 할 수 있다. (솔직히 위 코드가 전부다)

CallDirectoryExteion의 동작원리를 보면,

  1. 앱에 CallDirectoryExteion 추가 후 코드 구현
  2. 앱 설치시, CallDirectoryExteion이 있으면, 시스템 - 앱 - 전화 - 전화 차단 및 발신자 확인에 앱이 나타남
  3. On시, Extension상태가 enable되며, beginRequest 동작 실행
  4. 앱에서도 getEnabledStatusForExtension, reloadExtension 함수를 통해 beginRequest 동작 실행 가능

차단/식별번호를 추가하면, 시스템DB에서 참조하는 앱별 별도의 Callkit DB에 저장한뒤, 필요에 따라 조회해서 사용하는 원리이다.


하!지!만!
간결하다 못해 빈약한 코드에 비해, 제약사항들이 꽤 많은데... 둘이 합쳐지면 환장할 시너지가 발생하게 된다.

CallDirectoryExteion 제약사항

  • 모든 앱 extension의 메모리는 최대 12MB를 넘으면 안됨
  • 저장되는 전화번호는 2MB를 넘의면 안됨
  • 전화번호는 오름차순으로만 저장가능
  • 중복되는 전화번호를 넣으면 안됨
  • 최대 저장가능 개수는 200만개 (예전에는 20만개 였음). 초과되면 안됨
  • DB에 젖아되는 개수는 1000개씩 끊어서 저장
  • 동작중, 앱을 강제종료하면 일정시간(?) 실행되다 멈춤 (CallkitDB 저장시점으로...)

에러 예시 1

  • extenion이 실행
  • 이때 특정 에러로 인해, extension이 강제종료된다면 (메모리 부족 등)
  • On 상태이기 때문에, 시스템에서 다시 extenion을 로드 (재시작) - 에러 (종료)
  • 강제종료 - 재시작의 무한 반복!!
  • 이 상태에 들어서면, 앱에서는 해당 extension에 대해 제어가 불가능함

에러 예시 2

  • 만약 isIncremental - removeAll함수를 사용하지 않는다면,
  • 강제종료 - 재시작의 무한 반복에서, 종료된 뒤에도 일정시간(?) 실행된다는 조건으로 인해
  • 중복 전화번호 저장 에러 + 강제종료 - 재시작의 무한 반복도 일어날 수 있음

전화 차단 및 발신자 확인 활성화 에러 무한반복전화 차단 및 발신자 확인 활성화 에러 무한반복

환장할 에러 3

  • 이런 에러가 몇번 반복되면, 앱에서 저장할 수 있는 CallkitDB가 깨지개 됨
  • 이 상태까지 오게 된다면, 앱 삭제로도 해결되지 않음 (휴대폰 초기화로만 리셋 가능함)

환장할 에러 4

  • 앱 디버깅을 위해, 실행 - 중단 - 실행을 반복하게 된다면...
  • 저장 - 종료 - 하지만 저장중임.... - 다시 저장 - 저장중인 상태 겹침 - 에러 ...
  • DB 깨짐
Error Domain=com.apple.callkit.database.sqlite Code=11 "... returned 11 (11) errorMessage 'database disk image is malformed'}

불행히도 이런 에러가 발생하면... 초기화 밖에 답이 없음

developer 포럼을 찾아보니, 현재도 간간히 보고되는 이슈인듯하다.
https://developer.apple.com/forums/thread/684432
https://developer.apple.com/forums/thread/74549


당장 생각나는데로 적어봐도, 처음 저 로직 구현하면서 시스템 초기화를 어마어마하게 한것 같다.

해결방법

  1. 당연히 한두개 전화번호를 저장하지 않음으로, autoreleasepool을 사용해 빠르게 메모리를 반환해야한다.
  2. extension 시작시, 이전에 저장된 데이터는 무조건 지우고 새로 넣는다.
if context.isIncremental {
    context.removeAllBlockingEntries()
    context.removeAllIdentificationEntries()
}
  1. extension에서 사용되는 데이터는, 미리 가공된 (정렬/중족제거/직렬화) 데이터만 사용한다.
    (extension내에서 뭔가 하려고 하면 안됨)
  2. 시스템 설정에서 on하는 경우는 초기화만 진행하며, 앱에서 요청하는 경우만 데이터를 저장한다.
    (커스텀 인터페이스는 제공하지 않고, 독립되어 있어서 객체전달 불가능. AppGroup으로 설정값 설정)

다시 목표로 돌아가서,
070-xxxx-xxxx로 해당하는 모든 번호를 차단목록에 넣으려면, 1억개의 전화번호가 필요하다.

예전에 extension 최대 저장개수가 20만개였을때에는 500개의 extension이 필요했으나,
현재는 200만개로 늘어서 소소하게 50개(?)의 extension으로 커버가 가능하다.

이걸 전부 xcode에서 일일히 만들거 넣어주기는 힘드니,
다음 포스팅으로는 tuist를 활용해, 프로젝트 셋팅 - 구현 - 검증까지 해보려고 한다.

스팸 전화좀 그만해라 (2)


추가사항

테스트해보던 중, DB가 깨져서 결국 디바이스 초기화를 진행하게 되었다.
환장할 에러 4 발생시, 초기화하면 해결되는 걸 다시 한번 확인했다.