08 Marzec

CallKit

iOS 10 dostarcza wiele nowych funkcji. Jedną z nich jest CallKit, framework, który pozwala naszej aplikacji na ścisłą integrację z interfejsem użytkownika w telefonie. Aplikacje mogą wykorzystywać CallKit, aby umożliwić użytkownikom odbieranie połączeń przychodzących oraz realizację połączeń wychodzących za pomocą interfejsu użytkownika dostarczanego przez telefon. Połączenie VoIP może zostać wyciszone lub zawieszone. Możliwe jest również prowadzenie wideo połączenia.

Przygotowanie projektu

Pierwszym krokiem jest przygotowanie do odbierania powiadomień VoIP.

Konfiguracja powiadomień VoIP

Aby skonfigurować aplikację do odbierania powiadomień, należy zaimportować framework PushKit do naszego pliku AppDelegate oraz utworzyć obiekt PKPushRegistry. Należy również pamiętać o ustawieniu delegata nowo utworzonego obiektu jako self i ustawieniu jego typu jako VoIP:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
        let pushRegistry = PKPushRegistry(queue: DispatchQueue.main)
        pushRegistry.delegate = self
        pushRegistry.desiredPushTypes = [.voIP]

        return true
    }

Do przechowywania danych uwierzytelniających na serwerze dla aktywnego użytkownika oraz do przetwarzania pushy wykorzystywane są funkcje:

func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, forType type: PKPushType) {
            //Store push credentials on server for the active user.
    }

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, forType type: PKPushType) {
    }

Obsługa otrzymanych powiadomień

Ostatnim krokiem jest parsowanie danych otrzymanych z funkcji didReceiveIncomingPushWith, która potrafi między innymi wybudzić telefon ze stanu blokady urządzenia.

func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, forType type: PKPushType) {
        guard type == .voIP else { return }

        if let uuidString = payload.dictionaryPayload["UUID"] as? String,
           let handle = payload.dictionaryPayload["handle"] as? String,
           let hasVideo = payload.dictionaryPayload["hasVideo"] as? Bool,
           let uuid = UUID(uuidString: uuidString)
        {
             // Handle incoming call
        }
}
Konfiguracja CallKit

Pierwszym krokiem jest utworzenie klasy do obsługi połączeń przychodzących poprzez rozszerzenie klasy CXProviderDelegate.

final class ProviderDelegate: NSObject, CXProviderDelegate {
    private let provider: CXProvider
}

Następnym etapem jest konfiguracja dostawcy.

static var providerConfiguration: CXProviderConfiguration {
        let localizedName = NSLocalizedString("APPLICATION_NAME", comment: "name")
        let providerConfiguration = CXProviderConfiguration(localizedName: localizedName)

        providerConfiguration.supportsVideo = true

        providerConfiguration.maximumCallsPerCallGroup = 1

        providerConfiguration.supportedHandleTypes = [.phoneNumber]

        if let iconMaskImage = UIImage(named: "wideo") {
            providerConfiguration.iconTemplateImageData = UIImagePNGRepresentation(iconMaskImage)
        }

        providerConfiguration.ringtoneSound = "Ringtone.caf"

        return providerConfiguration
}

Ostatnim etapem jest dostarczenie inicjalizatora, dodanie funkcji reportIncomingCall odpowiadającej za przekazanie połączenia przychodzącego do systemu oraz dodanie funkcji providerDidReset wywoływanej gdy dostawca zostanie zrestartowany.

init() {
        provider = CXProvider(configuration: type(of: self).providerConfiguration)
        super.init()
        provider.setDelegate(self, queue: nil)
}

func reportIncomingCall(uuid: UUID, handle: String, hasVideo: Bool = false, completion: ((NSError?) -> Void)? = nil) {
        // Construct a CXCallUpdate describing the incoming call, including the caller.
        let update = CXCallUpdate()
        update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
        update.hasVideo = hasVideo

        // Report the incoming call to the system
        provider.reportNewIncomingCall(with: uuid, update: update) { error in
            completion?(error as? NSError)
        }
}

func providerDidReset(_ provider: CXProvider) {
        print("Provider did reset")
        /*
            End any ongoing calls if the provider resets, and remove them from the app's list of calls,
            since they are no longer valid.
         */
    }


Jest to tylko wstęp i jeśli chcielibyście dowiedzieć się więcej na ten temat, polecam stronę https://developer.apple.com/library/content/samplecode/Speakerbox/Introduction/Intro.html#//apple_ref/doc/uid/TP40017290-Intro-DontLinkElementID_2. Znajduje się tutaj dużo informacji oraz przykładowa aplikacja.