Udostępnij za pośrednictwem


CallKit w środowisku Xamarin.iOS

Nowy interfejs API zestawu CallKit w systemie iOS 10 umożliwia aplikacjom VOIP integrację z interfejsem użytkownika i Telefon oraz udostępnia znany interfejs i środowisko użytkownikowi końcowemu. Dzięki temu interfejsowi API użytkownicy mogą wyświetlać i korzystać z wywołań VOIP z ekranu blokady urządzenia z systemem iOS oraz zarządzać kontaktami przy użyciu widoków Ulubione i Ostatnie w aplikacji Telefon.

Zestaw CallKit — informacje

Według firmy Apple, CallKit to nowa struktura, która podniesie poziom aplikacji Voice Over IP (VOIP) innych firm do środowiska 1st party w systemie iOS 10. Interfejs API zestawu CallKit umożliwia aplikacjom VOIP integrację z interfejsem użytkownika i Telefon oraz udostępnia znajomy interfejs i środowisko użytkownikowi końcowemu. Podobnie jak wbudowana aplikacja Telefon, użytkownik może wyświetlać i korzystać z wywołań VOIP z ekranu blokady urządzenia z systemem iOS oraz zarządzać kontaktami przy użyciu widoków Ulubione i Ostatnie w aplikacji Telefon.

Ponadto interfejs API CallKit umożliwia tworzenie rozszerzeń aplikacji, które mogą skojarzyć numer telefonu z nazwą (identyfikatorem osoby wywołującej) lub poinformować system, kiedy numer powinien zostać zablokowany (blokowanie wywołań).

Istniejące środowisko aplikacji VOIP

Przed omówieniem nowego interfejsu API CallKit i jego możliwości zapoznaj się z bieżącym środowiskiem użytkownika z aplikacją VOIP innej firmy w systemie iOS 9 (i mniejszym) przy użyciu fikcyjnej aplikacji VOIP o nazwie MonkeyCall. MonkeyCall to prosta aplikacja, która umożliwia użytkownikowi wysyłanie i odbieranie wywołań VOIP przy użyciu istniejących interfejsów API systemu iOS.

Obecnie, jeśli użytkownik odbiera połączenie przychodzące na MonkeyCall i ich i Telefon jest zablokowany, powiadomienie odebrane na ekranie Blokada jest nie do odróżnienia od dowolnego innego typu powiadomienia (na przykład z aplikacji Wiadomości lub Poczty).

Jeśli użytkownik chciał odpowiedzieć na połączenie, musiałby przesunąć powiadomienie MonkeyCall, aby otworzyć aplikację i wprowadzić kod dostępu (lub identyfikator Touch ID użytkownika), aby odblokować telefon, zanim będzie mógł zaakceptować połączenie i rozpocząć konwersację.

Doświadczenie jest równie kłopotliwe, jeśli telefon jest odblokowany. Ponownie przychodzące wywołanie MonkeyCall jest wyświetlane jako standardowy baner powiadomień, który przesuwa się z góry ekranu. Ponieważ powiadomienie jest tymczasowe, można go łatwo przegapić przez użytkownika zmuszając ich do otwarcia Centrum powiadomień i znalezienia konkretnego powiadomienia, aby odpowiedzieć, a następnie wywołać lub znaleźć i uruchomić aplikację MonkeyCall ręcznie.

Środowisko aplikacji VoIP zestawu CallKit

Implementując nowe interfejsy API CallKit w aplikacji MonkeyCall, środowisko użytkownika z przychodzącym wywołaniem VOIP można znacznie ulepszyć w systemie iOS 10. Zapoznaj się z przykładem użytkownika odbierającego połączenie VOIP, gdy jego telefon jest zablokowany z góry. Zaimplementowanie zestawu CallKit spowoduje wyświetlenie wywołania na ekranie blokady urządzenia i Telefon tak samo jak w przypadku odebrania wywołania z wbudowanej aplikacji Telefon z pełnoekranowym, natywnym interfejsem użytkownika i standardowymi funkcjami szybkiego przesunięcia do odpowiedzi.

Ponownie, jeśli i Telefon jest odblokowany po odebraniu wywołania VOIP MonkeyCall, ten sam pełny ekran, natywny interfejs użytkownika i standardowe szybkie przesunięcie do odpowiedzi i naciśnięcie do odrzucenia funkcjonalności wbudowanej aplikacji Telefon jest prezentowany, a MonkeyCall ma możliwość odtwarzania niestandardowego dzwonka.

Zestaw CallKit udostępnia dodatkowe funkcje dla aplikacji MonkeyCall, co umożliwia interakcję wywołań VOIP z innymi typami wywołań, wyświetlanie ich we wbudowanych listach Ostatnie i Ulubione w celu korzystania z wbudowanych funkcji Nie przeszkadzać i blokuj, uruchamiania wywołań MonkeyCall z Siri i oferuje użytkownikom możliwość przypisywania wywołań MonkeyCall do osób w aplikacji Kontakty.

W poniższych sekcjach opisano architekturę CallKit, przepływy połączeń przychodzących i wychodzących oraz szczegółowo interfejs API CallKit.

Architektura zestawu CallKit

W systemie iOS 10 firma Apple przyjęła zestaw CallKit we wszystkich usługach systemowych, takich jak wywołania wykonywane na urządzeniu CarPlay, na przykład, są znane interfejsowi użytkownika systemu za pośrednictwem zestawu CallKit. W poniższym przykładzie, ponieważ aplikacja MonkeyCall przyjmuje zestaw CallKit, jest znana systemowi w taki sam sposób jak te wbudowane usługi systemowe i pobiera wszystkie te same funkcje:

Stos usługi CallKit

Przyjrzyj się bliżej aplikacji MonkeyCall z powyższego diagramu. Aplikacja zawiera cały kod do komunikowania się z własną siecią i zawiera własne interfejsy użytkownika. Łączy się on z zestawem CallKit w celu komunikowania się z systemem:

Architektura aplikacji MonkeyCall

W zestawie CallKit istnieją dwa główne interfejsy używane przez aplikację:

  • CXProvider - Umożliwia to aplikacji MonkeyCall informowanie systemu o wszelkich powiadomieniach poza pasmem, które mogą wystąpić.
  • CXCallController — Umożliwia aplikacji MonkeyCall informowanie systemu akcji użytkownika lokalnego.

Dostawca CXProvider

Jak wspomniano powyżej, CXProvider umożliwia aplikacji informowanie systemu o wszelkich powiadomieniach poza pasmem, które mogą wystąpić. Są to powiadomienia, które nie występują z powodu akcji użytkownika lokalnego, ale występują z powodu zdarzeń zewnętrznych, takich jak połączenia przychodzące.

Aplikacja powinna używać elementu CXProvider dla następujących elementów:

  • Zgłoś przychodzące wywołanie do systemu.
  • Zgłoś, że połączenie wychodzące zostało połączone z systemem.
  • Zgłoś użytkownika zdalnego kończącego wywołanie systemu.

Gdy aplikacja chce komunikować się z systemem, używa klasy i kiedy system musi komunikować się z aplikacją, używa CXCallUpdateCXAction klasy :

Komunikacja z systemem za pośrednictwem dostawcy CXProvider

The CXCallController

Aplikacja CXCallController umożliwia aplikacji informowanie systemu akcji użytkownika lokalnego, takich jak uruchamianie wywołania VOIP przez użytkownika. Implementacja CXCallController aplikacji umożliwia współdziałanie z innymi typami wywołań w systemie. Jeśli na przykład istnieje już aktywne wywołanie telefonii, CXCallController może zezwolić aplikacji VOIP na wstrzymanie tego wywołania i rozpoczęcie lub odbieranie połączenia VOIP.

Aplikacja powinna używać elementu CXCallController dla następujących elementów:

  • Zgłoś, gdy użytkownik uruchomił połączenie wychodzące do systemu.
  • Zgłoś, gdy użytkownik odpowie na przychodzące wywołanie systemu.
  • Zgłoś, gdy użytkownik kończy wywołanie systemu.

Gdy aplikacja chce przekazać lokalne akcje użytkownika do systemu, używa CXTransaction klasy :

Raportowanie do systemu przy użyciu kontrolera CXCallController

Implementowanie zestawu CallKit

W poniższych sekcjach pokazano, jak zaimplementować zestaw CallKit w aplikacji VOIP platformy Xamarin.iOS. Na przykład ten dokument będzie używać kodu z fikcyjnej aplikacji MonkeyCall VOIP. Przedstawiony tutaj kod reprezentuje kilka klas pomocniczych. Elementy specyficzne dla zestawu CallKit zostaną szczegółowo omówione w poniższych sekcjach.

Klasa ActiveCall

Klasa ActiveCall jest używana przez aplikację MonkeyCall do przechowywania wszystkich informacji o wywołaniu VOIP, który jest obecnie aktywny w następujący sposób:

using System;
using CoreFoundation;
using Foundation;

namespace MonkeyCall
{
    public class ActiveCall
    {
        #region Private Variables
        private bool isConnecting;
        private bool isConnected;
        private bool isOnhold;
        #endregion

        #region Computed Properties
        public NSUuid UUID { get; set; }
        public bool isOutgoing { get; set; }
        public string Handle { get; set; }
        public DateTime StartedConnectingOn { get; set;}
        public DateTime ConnectedOn { get; set;}
        public DateTime EndedOn { get; set; }

        public bool IsConnecting {
            get { return isConnecting; }
            set {
                isConnecting = value;
                if (isConnecting) StartedConnectingOn = DateTime.Now;
                RaiseStartingConnectionChanged ();
            }
        }

        public bool IsConnected {
            get { return isConnected; }
            set {
                isConnected = value;
                if (isConnected) {
                    ConnectedOn = DateTime.Now;
                } else {
                    EndedOn = DateTime.Now;
                }
                RaiseConnectedChanged ();
            }
        }

        public bool IsOnHold {
            get { return isOnhold; }
            set {
                isOnhold = value;
            }
        }
        #endregion

        #region Constructors
        public ActiveCall ()
        {
        }

        public ActiveCall (NSUuid uuid, string handle, bool outgoing)
        {
            // Initialize
            this.UUID = uuid;
            this.Handle = handle;
            this.isOutgoing = outgoing;
        }
        #endregion

        #region Public Methods
        public void StartCall (ActiveCallbackDelegate completionHandler)
        {
            // Simulate the call starting successfully
            completionHandler (true);

            // Simulate making a starting and completing a connection
            DispatchQueue.MainQueue.DispatchAfter (new DispatchTime(DispatchTime.Now, 3000), () => {
                // Note that the call is starting
                IsConnecting = true;

                // Simulate pause before connecting
                DispatchQueue.MainQueue.DispatchAfter (new DispatchTime (DispatchTime.Now, 1500), () => {
                    // Note that the call has connected
                    IsConnecting = false;
                    IsConnected = true;
                });
            });
        }

        public void AnswerCall (ActiveCallbackDelegate completionHandler)
        {
            // Simulate the call being answered
            IsConnected = true;
            completionHandler (true);
        }

        public void EndCall (ActiveCallbackDelegate completionHandler)
        {
            // Simulate the call ending
            IsConnected = false;
            completionHandler (true);
        }
        #endregion

        #region Events
        public delegate void ActiveCallbackDelegate (bool successful);
        public delegate void ActiveCallStateChangedDelegate (ActiveCall call);

        public event ActiveCallStateChangedDelegate StartingConnectionChanged;
        internal void RaiseStartingConnectionChanged ()
        {
            if (this.StartingConnectionChanged != null) this.StartingConnectionChanged (this);
        }

        public event ActiveCallStateChangedDelegate ConnectedChanged;
        internal void RaiseConnectedChanged ()
        {
            if (this.ConnectedChanged != null) this.ConnectedChanged (this);
        }
        #endregion
    }
}

ActiveCall zawiera kilka właściwości definiujących stan wywołania i dwa zdarzenia, które mogą być wywoływane po zmianie stanu wywołania. Ponieważ jest to tylko przykład, istnieją trzy metody używane do symulowania uruchamiania, odpowiadania i kończenia wywołania.

Klasa StartCallRequest

Klasa statyczna StartCallRequest udostępnia kilka metod pomocnika, które będą używane podczas pracy z wywołaniami wychodzącymi:

using System;
using Foundation;
using Intents;

namespace MonkeyCall
{
    public static class StartCallRequest
    {
        public static string URLScheme {
            get { return "monkeycall"; }
        }

        public static string ActivityType {
            get { return INIntentIdentifier.StartAudioCall.GetConstant ().ToString (); }
        }

        public static string CallHandleFromURL (NSUrl url)
        {
            // Is this a MonkeyCall handle?
            if (url.Scheme == URLScheme) {
                // Yes, return host
                return url.Host;
            } else {
                // Not handled
                return null;
            }
        }

        public static string CallHandleFromActivity (NSUserActivity activity)
        {
            // Is this a start call activity?
            if (activity.ActivityType == ActivityType) {
                // Yes, trap any errors
                try {
                    // Get first contact
                    var interaction = activity.GetInteraction ();
                    var startAudioCallIntent = interaction.Intent as INStartAudioCallIntent;
                    var contact = startAudioCallIntent.Contacts [0];

                    // Get the person handle
                    return contact.PersonHandle.Value;
                } catch {
                    // Error, report null
                    return null;
                }
            } else {
                // Not handled
                return null;
            }
        }
    }
}

Klasy CallHandleFromURL i CallHandleFromActivity są używane w aplikacji AppDelegate, aby uzyskać dojście kontaktowe osoby wywoływanej w wywołaniu wychodzącym. Aby uzyskać więcej informacji, zobacz sekcję Obsługa połączeń wychodzących poniżej.

Klasa ActiveCallManager

Klasa ActiveCallManager obsługuje wszystkie otwarte wywołania w aplikacji MonkeyCall.

using System;
using System.Collections.Generic;
using Foundation;
using CallKit;

namespace MonkeyCall
{
    public class ActiveCallManager
    {
        #region Private Variables
        private CXCallController CallController = new CXCallController ();
        #endregion

        #region Computed Properties
        public List<ActiveCall> Calls { get; set; }
        #endregion

        #region Constructors
        public ActiveCallManager ()
        {
            // Initialize
            this.Calls = new List<ActiveCall> ();
        }
        #endregion

        #region Private Methods
        private void SendTransactionRequest (CXTransaction transaction)
        {
            // Send request to call controller
            CallController.RequestTransaction (transaction, (error) => {
                // Was there an error?
                if (error == null) {
                    // No, report success
                    Console.WriteLine ("Transaction request sent successfully.");
                } else {
                    // Yes, report error
                    Console.WriteLine ("Error requesting transaction: {0}", error);
                }
            });
        }
        #endregion

        #region Public Methods
        public ActiveCall FindCall (NSUuid uuid)
        {
            // Scan for requested call
            foreach (ActiveCall call in Calls) {
                if (call.UUID.Equals(uuid)) return call;
            }

            // Not found
            return null;
        }

        public void StartCall (string contact)
        {
            // Build call action
            var handle = new CXHandle (CXHandleType.Generic, contact);
            var startCallAction = new CXStartCallAction (new NSUuid (), handle);

            // Create transaction
            var transaction = new CXTransaction (startCallAction);

            // Inform system of call request
            SendTransactionRequest (transaction);
        }

        public void EndCall (ActiveCall call)
        {
            // Build action
            var endCallAction = new CXEndCallAction (call.UUID);

            // Create transaction
            var transaction = new CXTransaction (endCallAction);

            // Inform system of call request
            SendTransactionRequest (transaction);
        }

        public void PlaceCallOnHold (ActiveCall call)
        {
            // Build action
            var holdCallAction = new CXSetHeldCallAction (call.UUID, true);

            // Create transaction
            var transaction = new CXTransaction (holdCallAction);

            // Inform system of call request
            SendTransactionRequest (transaction);
        }

        public void RemoveCallFromOnHold (ActiveCall call)
        {
            // Build action
            var holdCallAction = new CXSetHeldCallAction (call.UUID, false);

            // Create transaction
            var transaction = new CXTransaction (holdCallAction);

            // Inform system of call request
            SendTransactionRequest (transaction);
        }
        #endregion
    }
}

Ponownie, ponieważ jest to tylko symulacja, ActiveCallManager jedyny utrzymuje kolekcję ActiveCall obiektów i ma procedurę znajdowania danego wywołania przez jego UUID właściwość. Zawiera również metody uruchamiania, kończenia i zmieniania stanu wstrzymania wywołania wychodzącego. Aby uzyskać więcej informacji, zobacz sekcję Obsługa połączeń wychodzących poniżej.

Klasa ProviderDelegate

Jak wspomniano powyżej, zapewnia dwukierunkową CXProvider komunikację między aplikacją a systemem na potrzeby powiadomień poza pasmem. Deweloper musi podać niestandardowy element CXProviderDelegate i dołączyć go do CXProvider aplikacji, aby obsłużyć zdarzenia CallKit poza pasmem. Aplikacja MonkeyCall używa następującego CXProviderDelegateelementu :

using System;
using Foundation;
using CallKit;
using UIKit;

namespace MonkeyCall
{
    public class ProviderDelegate : CXProviderDelegate
    {
        #region Computed Properties
        public ActiveCallManager CallManager { get; set;}
        public CXProviderConfiguration Configuration { get; set; }
        public CXProvider Provider { get; set; }
        #endregion

        #region Constructors
        public ProviderDelegate (ActiveCallManager callManager)
        {
            // Save connection to call manager
            CallManager = callManager;

            // Define handle types
            var handleTypes = new [] { (NSNumber)(int)CXHandleType.PhoneNumber };

            // Get Image Template
            var templateImage = UIImage.FromFile ("telephone_receiver.png");

            // Setup the initial configurations
            Configuration = new CXProviderConfiguration ("MonkeyCall") {
                MaximumCallsPerCallGroup = 1,
                SupportedHandleTypes = new NSSet<NSNumber> (handleTypes),
                IconTemplateImageData = templateImage.AsPNG(),
                RingtoneSound = "musicloop01.wav"
            };

            // Create a new provider
            Provider = new CXProvider (Configuration);

            // Attach this delegate
            Provider.SetDelegate (this, null);

        }
        #endregion

        #region Override Methods
        public override void DidReset (CXProvider provider)
        {
            // Remove all calls
            CallManager.Calls.Clear ();
        }

        public override void PerformStartCallAction (CXProvider provider, CXStartCallAction action)
        {
            // Create new call record
            var activeCall = new ActiveCall (action.CallUuid, action.CallHandle.Value, true);

            // Monitor state changes
            activeCall.StartingConnectionChanged += (call) => {
                if (call.isConnecting) {
                    // Inform system that the call is starting
                    Provider.ReportConnectingOutgoingCall (call.UUID, call.StartedConnectingOn.ToNSDate());
                }
            };

            activeCall.ConnectedChanged += (call) => {
                if (call.isConnected) {
                    // Inform system that the call has connected
                    provider.ReportConnectedOutgoingCall (call.UUID, call.ConnectedOn.ToNSDate ());
                }
            };

            // Start call
            activeCall.StartCall ((successful) => {
                // Was the call able to be started?
                if (successful) {
                    // Yes, inform the system
                    action.Fulfill ();

                    // Add call to manager
                    CallManager.Calls.Add (activeCall);
                } else {
                    // No, inform system
                    action.Fail ();
                }
            });
        }

        public override void PerformAnswerCallAction (CXProvider provider, CXAnswerCallAction action)
        {
            // Find requested call
            var call = CallManager.FindCall (action.CallUuid);

            // Found?
            if (call == null) {
                // No, inform system and exit
                action.Fail ();
                return;
            }

            // Attempt to answer call
            call.AnswerCall ((successful) => {
                // Was the call successfully answered?
                if (successful) {
                    // Yes, inform system
                    action.Fulfill ();
                } else {
                    // No, inform system
                    action.Fail ();
                }
            });
        }

        public override void PerformEndCallAction (CXProvider provider, CXEndCallAction action)
        {
            // Find requested call
            var call = CallManager.FindCall (action.CallUuid);

            // Found?
            if (call == null) {
                // No, inform system and exit
                action.Fail ();
                return;
            }

            // Attempt to answer call
            call.EndCall ((successful) => {
                // Was the call successfully answered?
                if (successful) {
                    // Remove call from manager's queue
                    CallManager.Calls.Remove (call);

                    // Yes, inform system
                    action.Fulfill ();
                } else {
                    // No, inform system
                    action.Fail ();
                }
            });
        }

        public override void PerformSetHeldCallAction (CXProvider provider, CXSetHeldCallAction action)
        {
            // Find requested call
            var call = CallManager.FindCall (action.CallUuid);

            // Found?
            if (call == null) {
                // No, inform system and exit
                action.Fail ();
                return;
            }

            // Update hold status
            call.isOnHold = action.OnHold;

            // Inform system of success
            action.Fulfill ();
        }

        public override void TimedOutPerformingAction (CXProvider provider, CXAction action)
        {
            // Inform user that the action has timed out
        }

        public override void DidActivateAudioSession (CXProvider provider, AVFoundation.AVAudioSession audioSession)
        {
            // Start the calls audio session here
        }

        public override void DidDeactivateAudioSession (CXProvider provider, AVFoundation.AVAudioSession audioSession)
        {
            // End the calls audio session and restart any non-call
            // related audio
        }
        #endregion

        #region Public Methods
        public void ReportIncomingCall (NSUuid uuid, string handle)
        {
            // Create update to describe the incoming call and caller
            var update = new CXCallUpdate ();
            update.RemoteHandle = new CXHandle (CXHandleType.Generic, handle);

            // Report incoming call to system
            Provider.ReportNewIncomingCall (uuid, update, (error) => {
                // Was the call accepted
                if (error == null) {
                    // Yes, report to call manager
                    CallManager.Calls.Add (new ActiveCall (uuid, handle, false));
                } else {
                    // Report error to user here
                    Console.WriteLine ("Error: {0}", error);
                }
            });
        }
        #endregion
    }
}

Po utworzeniu wystąpienia tego delegata zostanie ono ActiveCallManager przekazane, które będzie używane do obsługi dowolnego działania wywołania. Następnie definiuje typy dojść (CXHandleType), na które CXProvider będą odpowiadać:

// Define handle types
var handleTypes = new [] { (NSNumber)(int)CXHandleType.PhoneNumber };

Pobiera obraz szablonu, który zostanie zastosowany do ikony aplikacji, gdy wywołanie jest w toku:

// Get Image Template
var templateImage = UIImage.FromFile ("telephone_receiver.png");

Te wartości są dołączane do elementu CXProviderConfiguration , który będzie używany do konfigurowania elementu CXProvider:

// Setup the initial configurations
Configuration = new CXProviderConfiguration ("MonkeyCall") {
    MaximumCallsPerCallGroup = 1,
    SupportedHandleTypes = new NSSet<NSNumber> (handleTypes),
    IconTemplateImageData = templateImage.AsPNG(),
    RingtoneSound = "musicloop01.wav"
};

Delegat następnie tworzy nowy CXProvider z tymi konfiguracjami i dołącza się do niego:

// Create a new provider
Provider = new CXProvider (Configuration);

// Attach this delegate
Provider.SetDelegate (this, null);

W przypadku korzystania z zestawu CallKit aplikacja nie będzie już tworzyć i obsługiwać własnych sesji audio, zamiast tego będzie musiała skonfigurować i użyć sesji audio, którą system utworzy i obsłuży dla niej.

Gdyby była to prawdziwa aplikacja, DidActivateAudioSession metoda zostanie użyta do uruchomienia wywołania przy użyciu wstępnie skonfigurowanego AVAudioSession przez system:

public override void DidActivateAudioSession (CXProvider provider, AVFoundation.AVAudioSession audioSession)
{
    // Start the call's audio session here...
}

Za pomocą DidDeactivateAudioSession metody można również sfinalizować i zwolnić połączenie z sesją audio dostarczonej przez system:

public override void DidDeactivateAudioSession (CXProvider provider, AVFoundation.AVAudioSession audioSession)
{
    // End the calls audio session and restart any non-call
    // releated audio
}

Pozostała część kodu zostanie szczegółowo omówiona w kolejnych sekcjach.

Klasa AppDelegate

Aplikacja MonkeyCall używa elementu AppDelegate do przechowywania wystąpień ActiveCallManager elementu i CXProviderDelegate , które będą używane w całej aplikacji:

using Foundation;
using UIKit;
using Intents;
using System;

namespace MonkeyCall
{
    [Register ("AppDelegate")]
    public class AppDelegate : UIApplicationDelegate
    {
        #region Constructors
        public override UIWindow Window { get; set; }
        public ActiveCallManager CallManager { get; set; }
        public ProviderDelegate CallProviderDelegate { get; set; }
        #endregion

        #region Override Methods
        public override bool FinishedLaunching (UIApplication application, NSDictionary launchOptions)
        {
            // Initialize the call handlers
            CallManager = new ActiveCallManager ();
            CallProviderDelegate = new ProviderDelegate (CallManager);

            return true;
        }

        public override bool OpenUrl (UIApplication app, NSUrl url, NSDictionary options)
        {
            // Get handle from url
            var handle = StartCallRequest.CallHandleFromURL (url);

            // Found?
            if (handle == null) {
                // No, report to system
                Console.WriteLine ("Unable to get call handle from URL: {0}", url);
                return false;
            } else {
                // Yes, start call and inform system
                CallManager.StartCall (handle);
                return true;
            }
        }

        public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
        {
            var handle = StartCallRequest.CallHandleFromActivity (userActivity);

            // Found?
            if (handle == null) {
                // No, report to system
                Console.WriteLine ("Unable to get call handle from User Activity: {0}", userActivity);
                return false;
            } else {
                // Yes, start call and inform system
                CallManager.StartCall (handle);
                return true;
            }
        }

        ...
        #endregion
    }
}

Metody OpenUrl i ContinueUserActivity zastąpienia są używane, gdy aplikacja przetwarza wywołanie wychodzące. Aby uzyskać więcej informacji, zobacz sekcję Obsługa połączeń wychodzących poniżej.

Obsługa połączeń przychodzących

Istnieje kilka stanów i procesów, przez które przychodzące wywołanie VOIP może przechodzić podczas typowego przepływu pracy wywołania przychodzącego, takiego jak:

  • Informowanie użytkownika (i systemu) o tym, że istnieje wywołanie przychodzące.
  • Otrzymywanie powiadomienia, gdy użytkownik chce odpowiedzieć na połączenie i zainicjować połączenie z innym użytkownikiem.
  • Poinformuj system i sieć komunikacji, gdy użytkownik chce zakończyć bieżące wywołanie.

W poniższych sekcjach opisano szczegółowo, jak aplikacja może używać zestawu CallKit do obsługi przepływu pracy wywołań przychodzących, ponownie przy użyciu aplikacji MonkeyCall VOIP jako przykładu.

Informowanie użytkownika o połączeniu przychodzącym

Gdy użytkownik zdalny rozpoczął rozmowę VOIP z użytkownikiem lokalnym, następuje:

Użytkownik zdalny rozpoczął konwersację VOIP

  1. Aplikacja otrzymuje powiadomienie z sieci komunikacji, że istnieje przychodzące wywołanie VOIP.
  2. Aplikacja używa elementu CXProvider , aby wysłać element do CXCallUpdate systemu, informując go o wywołaniu.
  3. System publikuje wywołanie interfejsu użytkownika systemu, usług systemowych i innych aplikacji VOIP przy użyciu zestawu CallKit.

Na przykład w pliku CXProviderDelegate:

public void ReportIncomingCall (NSUuid uuid, string handle)
{
    // Create update to describe the incoming call and caller
    var update = new CXCallUpdate ();
    update.RemoteHandle = new CXHandle (CXHandleType.Generic, handle);

    // Report incoming call to system
    Provider.ReportNewIncomingCall (uuid, update, (error) => {
        // Was the call accepted
        if (error == null) {
            // Yes, report to call manager
            CallManager.Calls.Add (new ActiveCall (uuid, handle, false));
        } else {
            // Report error to user here
            Console.WriteLine ("Error: {0}", error);
        }
    });
}

Ten kod tworzy nowe CXCallUpdate wystąpienie i dołącza do niego uchwyt, który zidentyfikuje obiekt wywołujący. Następnie używa ReportNewIncomingCall metody CXProvider klasy do informowania systemu o wywołaniu. Jeśli to się powiedzie, wywołanie zostanie dodane do kolekcji aktywnych wywołań aplikacji, jeśli tak nie jest, błąd musi zostać zgłoszony użytkownikowi.

Użytkownik odbierający połączenie przychodzące

Jeśli użytkownik chce odpowiedzieć na przychodzące wywołanie VOIP, następuje:

Użytkownik odpowiada na przychodzące wywołanie VOIP

  1. Interfejs użytkownika systemu informuje system, że użytkownik chce odpowiedzieć na wywołanie VOIP.
  2. System wysyła element CXAnswerCallAction do aplikacji CXProvider z informacją o intencji odpowiedzi.
  3. Aplikacja informuje sieć komunikacji, że użytkownik odpowiada na połączenie, a wywołanie VOIP przebiega jak zwykle.

Na przykład w pliku CXProviderDelegate:

public override void PerformAnswerCallAction (CXProvider provider, CXAnswerCallAction action)
{
    // Find requested call
    var call = CallManager.FindCall (action.CallUuid);

    // Found?
    if (call == null) {
        // No, inform system and exit
        action.Fail ();
        return;
    }

    // Attempt to answer call
    call.AnswerCall ((successful) => {
        // Was the call successfully answered?
        if (successful) {
            // Yes, inform system
            action.Fulfill ();
        } else {
            // No, inform system
            action.Fail ();
        }
    });
}

Ten kod najpierw wyszukuje podane wywołanie na liście aktywnych wywołań. Jeśli nie można odnaleźć wywołania, system zostanie powiadomiony i metoda zakończy działanie. Jeśli zostanie znaleziona, metoda ActiveCall klasy jest wywoływana w AnswerCall celu uruchomienia wywołania, a system zawiera informacje, jeśli zakończy się powodzeniem lub niepowodzeniem.

Użytkownik kończący połączenie przychodzące

Jeśli użytkownik chce zakończyć wywołanie z poziomu interfejsu użytkownika aplikacji, wystąpią następujące czynności:

Użytkownik kończy wywołanie z poziomu interfejsu użytkownika aplikacji

  1. Aplikacja tworzy CXEndCallAction pakiet, który jest wysyłany CXTransaction do systemu, aby poinformować o zakończeniu wywołania.
  2. System weryfikuje CXEndCallAction intencję wywołania końcowego i wysyła z powrotem do aplikacji za pośrednictwem elementu CXProvider.
  3. Następnie aplikacja informuje sieć komunikacji o zakończeniu wywołania.

Na przykład w pliku CXProviderDelegate:

public override void PerformEndCallAction (CXProvider provider, CXEndCallAction action)
{
    // Find requested call
    var call = CallManager.FindCall (action.CallUuid);

    // Found?
    if (call == null) {
        // No, inform system and exit
        action.Fail ();
        return;
    }

    // Attempt to answer call
    call.EndCall ((successful) => {
        // Was the call successfully answered?
        if (successful) {
            // Remove call from manager's queue
            CallManager.Calls.Remove (call);

            // Yes, inform system
            action.Fulfill ();
        } else {
            // No, inform system
            action.Fail ();
        }
    });
}

Ten kod najpierw wyszukuje podane wywołanie na liście aktywnych wywołań. Jeśli nie można odnaleźć wywołania, system zostanie powiadomiony i metoda zakończy działanie. Jeśli zostanie znaleziona, metoda ActiveCall klasy jest wywoływana w EndCall celu zakończenia wywołania, a system zawiera informacje, jeśli zakończy się powodzeniem lub niepowodzeniem. Jeśli operacja zakończy się pomyślnie, wywołanie zostanie usunięte z kolekcji aktywnych wywołań.

Zarządzanie wieloma wywołaniami

Większość aplikacji VOIP może obsługiwać wiele wywołań jednocześnie. Jeśli na przykład istnieje aktywne wywołanie VOIP, a aplikacja otrzymuje powiadomienie o tym, że istnieje nowe połączenie przychodzące, użytkownik może wstrzymać lub zawiesić się przy pierwszym wywołaniu, aby odpowiedzieć na drugie.

W powyższej sytuacji system wyśle CXTransaction element do aplikacji, która będzie zawierać listę wielu akcji (takich jak CXEndCallAction i CXAnswerCallAction). Wszystkie te akcje należy wykonać indywidualnie, aby system mógł odpowiednio zaktualizować interfejs użytkownika.

Obsługa połączeń wychodzących

Jeśli użytkownik naciśnie wpis z listy Ostatnie (w aplikacji Telefon), na przykład z wywołania należącego do aplikacji, zostanie wysłany intencja wywołania początkowego przez system:

Odbieranie intencji wywołania początkowego

  1. Aplikacja utworzy akcję wywołania początkowego na podstawie intencji wywołania początkowego otrzymanego z systemu.
  2. Aplikacja będzie używać polecenia , CXCallController aby zażądać akcji Rozpoczęcia wywołania z systemu.
  3. Jeśli system zaakceptuje akcję, zostanie on zwrócony do aplikacji za pośrednictwem delegata XCProvider .
  4. Aplikacja uruchamia połączenie wychodzące z siecią komunikacji.

Aby uzyskać więcej informacji na temat intencji, zobacz dokumentację rozszerzenia interfejsu użytkownika intencji i intencji .

Cykl życia połączeń wychodzących

Podczas pracy z zestawem CallKit i wywołaniem wychodzącym aplikacja musi poinformować system o następujących zdarzeniach cyklu życia:

  1. Uruchamianie — informuje system, że zostanie uruchomione wywołanie wychodzące.
  2. Rozpoczęto — informuje system, że uruchomiono połączenie wychodzące.
  3. Połączenie — informuje system, że połączenie wychodzące jest nawiązywane.
  4. Połączenie - Poinformuj, że połączenie wychodzące nawiązało połączenie i że obie strony mogą teraz porozmawiać.

Na przykład następujący kod uruchomi wywołanie wychodzące:

private CXCallController CallController = new CXCallController ();
...

private void SendTransactionRequest (CXTransaction transaction)
{
    // Send request to call controller
    CallController.RequestTransaction (transaction, (error) => {
        // Was there an error?
        if (error == null) {
            // No, report success
            Console.WriteLine ("Transaction request sent successfully.");
        } else {
            // Yes, report error
            Console.WriteLine ("Error requesting transaction: {0}", error);
        }
    });
}

public void StartCall (string contact)
{
    // Build call action
    var handle = new CXHandle (CXHandleType.Generic, contact);
    var startCallAction = new CXStartCallAction (new NSUuid (), handle);

    // Create transaction
    var transaction = new CXTransaction (startCallAction);

    // Inform system of call request
    SendTransactionRequest (transaction);
}

Tworzy element CXHandle i używa go do skonfigurowania CXStartCallAction elementu, który jest powiązany z elementem CXTransaction wysyłanym do systemu przy użyciu RequestTransaction metody CXCallController klasy . Wywołując metodęRequestTransaction, system może umieścić wszystkie istniejące wywołania w wstrzymaniu, niezależnie od źródła (Telefon aplikacji, FaceTime, VOIP itp.), przed rozpoczęciem nowego wywołania.

Żądanie uruchomienia wychodzącego wywołania VOIP może pochodzić z kilku różnych źródeł, takich jak Siri, wpis na karcie Kontakt (w aplikacji Kontakty) lub z listy Ostatnie (w aplikacji Telefon). W takich sytuacjach aplikacja zostanie wysłana intencją wywołania początkowego wewnątrz elementu , NSUserActivity a element AppDelegate będzie musiał go obsłużyć:

public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
    var handle = StartCallRequest.CallHandleFromActivity (userActivity);

    // Found?
    if (handle == null) {
        // No, report to system
        Console.WriteLine ("Unable to get call handle from User Activity: {0}", userActivity);
        return false;
    } else {
        // Yes, start call and inform system
        CallManager.StartCall (handle);
        return true;
    }
}

CallHandleFromActivity Tutaj metoda klasy StartCallRequest pomocniczej jest używana do pobrania dojścia do wywoływanej osoby (zobacz klasę StartCallRequest powyżej).

PerformStartCallAction Metoda klasy ProviderDelegate służy do zakończenia rzeczywistego wywołania wychodzącego i informowania systemu o jego cyklu życia:

public override void PerformStartCallAction (CXProvider provider, CXStartCallAction action)
{
    // Create new call record
    var activeCall = new ActiveCall (action.CallUuid, action.CallHandle.Value, true);

    // Monitor state changes
    activeCall.StartingConnectionChanged += (call) => {
        if (call.IsConnecting) {
            // Inform system that the call is starting
            Provider.ReportConnectingOutgoingCall (call.UUID, call.StartedConnectingOn.ToNSDate());
        }
    };

    activeCall.ConnectedChanged += (call) => {
        if (call.IsConnected) {
            // Inform system that the call has connected
            Provider.ReportConnectedOutgoingCall (call.UUID, call.ConnectedOn.ToNSDate ());
        }
    };

    // Start call
    activeCall.StartCall ((successful) => {
        // Was the call able to be started?
        if (successful) {
            // Yes, inform the system
            action.Fulfill ();

            // Add call to manager
            CallManager.Calls.Add (activeCall);
        } else {
            // No, inform system
            action.Fail ();
        }
    });
}

Tworzy wystąpienie ActiveCall klasy (w celu przechowywania informacji o wywołaniu w toku) i wypełnia się wywoływaną osobą. Zdarzenia StartingConnectionChanged i ConnectedChanged służą do monitorowania i raportowania cyklu życia połączeń wychodzących. Wywołanie zostało uruchomione, a system poinformował, że akcja została spełniona.

Kończenie połączenia wychodzącego

Gdy użytkownik zakończy połączenie wychodzące i chce go zakończyć, można użyć następującego kodu:

private CXCallController CallController = new CXCallController ();
...

private void SendTransactionRequest (CXTransaction transaction)
{
    // Send request to call controller
    CallController.RequestTransaction (transaction, (error) => {
        // Was there an error?
        if (error == null) {
            // No, report success
            Console.WriteLine ("Transaction request sent successfully.");
        } else {
            // Yes, report error
            Console.WriteLine ("Error requesting transaction: {0}", error);
        }
    });
}

public void EndCall (ActiveCall call)
{
    // Build action
    var endCallAction = new CXEndCallAction (call.UUID);

    // Create transaction
    var transaction = new CXTransaction (endCallAction);

    // Inform system of call request
    SendTransactionRequest (transaction);
}

Jeśli tworzy element CXEndCallAction z identyfikatorem UUID wywołania do końca, tworzy go w CXTransaction klasie, która jest wysyłana do systemu przy użyciu RequestTransaction metody CXCallController klasy .

Dodatkowe szczegóły zestawu CallKit

W tej sekcji omówiono dodatkowe szczegóły, które deweloper będzie musiał wziąć pod uwagę podczas pracy z zestawem CallKit, na przykład:

  • Konfiguracja dostawcy
  • Błędy akcji
  • Ograniczenia systemowe
  • Dźwięk VOIP

Konfiguracja dostawcy

Konfiguracja dostawcy umożliwia aplikacji VOIP dla systemu iOS 10 dostosowywanie środowiska użytkownika (wewnątrz natywnego interfejsu użytkownika wywołania) podczas pracy z zestawem CallKit.

Aplikacja może wprowadzać następujące typy dostosowań:

  • Wyświetl zlokalizowaną nazwę.
  • Włącz obsługę połączeń wideo.
  • Dostosuj przyciski w interfejsie użytkownika wywołania, przedstawiając własną ikonę obrazu szablonu. Interakcja użytkownika z przyciskami niestandardowymi jest wysyłana bezpośrednio do aplikacji do przetworzenia.

Błędy akcji

Aplikacje voIP dla systemu iOS 10 korzystające z zestawu CallKit muszą obsługiwać akcje, które kończą się niepowodzeniem, i informować użytkownika o stanie akcji przez cały czas.

Weź pod uwagę następujący przykład:

  1. Aplikacja otrzymała akcję Rozpoczęcia połączenia i rozpoczęła proces inicjowania nowego wywołania VOIP za pomocą sieci komunikacyjnej.
  2. Ze względu na ograniczoną lub brak możliwości komunikacji sieciowej to połączenie kończy się niepowodzeniem.
  3. Aby poinformować system o awarii, aplikacja musi wysłaćkomunikat Niepowodzenie z powrotem do akcji Uruchom wywołanie (Action.Fail()).
  4. Dzięki temu system może poinformować użytkownika o stanie wywołania. Aby na przykład wyświetlić interfejs użytkownika niepowodzenia wywołania.

Ponadto aplikacja VOIP dla systemu iOS 10 musi odpowiadać na błędy przekroczenia limitu czasu, które mogą wystąpić, gdy nie można przetworzyć oczekiwanej akcji w danym czasie. Każdy typ akcji dostarczony przez zestaw CallKit ma skojarzona maksymalną wartość limitu czasu. Te wartości limitu czasu zapewniają, że każda akcja CallKit żądana przez użytkownika jest obsługiwana w sposób dynamiczny, zapewniając płyn systemu operacyjnego i reagujący.

Istnieje kilka metod delegata dostawcy (CXProviderDelegate), które powinny zostać zastąpione, aby bezpiecznie obsłużyć te sytuacje przekroczenia limitu czasu, jak również.

Ograniczenia systemowe

Na podstawie bieżącego stanu urządzenia z systemem iOS z uruchomioną aplikacją VOIP systemu iOS 10 można wymusić pewne ograniczenia systemowe.

Na przykład przychodzące wywołanie VOIP może być ograniczone przez system, jeśli:

  1. Osoba wywołująca znajduje się na liście zablokowanych rozmówców użytkownika.
  2. Urządzenie z systemem iOS użytkownika jest w trybie Nie przeszkadzać.

Jeśli wywołanie VOIP jest ograniczone przez dowolną z tych sytuacji, użyj następującego kodu, aby go obsłużyć:

public class ProviderDelegate : CXProviderDelegate
{
...

    public void ReportIncomingCall (NSUuid uuid, string handle)
    {
        // Create update to describe the incoming call and caller
        var update = new CXCallUpdate ();
        update.RemoteHandle = new CXHandle (CXHandleType.Generic, handle);

        // Report incoming call to system
        Provider.ReportNewIncomingCall (uuid, update, (error) => {
            // Was the call accepted
            if (error == null) {
                // Yes, report to call manager
                CallManager.Calls.Add (new ActiveCall (uuid, handle, false));
            } else {
                // Report error to user here
                if (error.Code == (int)CXErrorCodeIncomingCallError.CallUuidAlreadyExists) {
                    // Handle duplicate call ID
                } else if (error.Code == (int)CXErrorCodeIncomingCallError.FilteredByBlockList) {
                    // Handle call from blocked user
                } else if (error.Code == (int)CXErrorCodeIncomingCallError.FilteredByDoNotDisturb) {
                    // Handle call while in do-not-disturb mode
                } else {
                    // Handle unknown error
                }
            }
        });
    }

}

Dźwięk VOIP

Zestaw CallKit zapewnia kilka korzyści związanych z obsługą zasobów audio, których aplikacja VOIP systemu iOS 10 będzie wymagała podczas połączenia VOIP na żywo. Jedną z największych korzyści jest sesja audio aplikacji będzie miała podwyższone priorytety podczas uruchamiania w systemie iOS 10. Jest to ten sam poziom priorytetu co wbudowane aplikacje Telefon i FaceTime, a ten rozszerzony poziom priorytetu uniemożliwi innym uruchomionym aplikacjom przerwanie sesji audio aplikacji VOIP.

Ponadto zestaw CallKit ma dostęp do innych wskazówek routingu audio, które mogą zwiększyć wydajność i inteligentnie kierować dźwięk VOIP do określonych urządzeń wyjściowych podczas połączenia na żywo na podstawie preferencji użytkownika i stanów urządzeń. Na przykład na podstawie dołączonych urządzeń, takich jak słuchawki Bluetooth, połączenie Na żywo CarPlay lub ustawienia ułatwień dostępu.

W trakcie cyklu życia typowego wywołania VOIP przy użyciu zestawu CallKit aplikacja będzie musiała skonfigurować strumień audio, który udostępni zestaw CallKit. Zapoznaj się z poniższym przykładem:

Sekwencja akcji Rozpocznij wywołanie

  1. Akcja rozpoczęcia połączenia jest odbierana przez aplikację w celu udzielenia odpowiedzi na połączenie przychodzące.
  2. Przed spełnieniem tej akcji przez aplikację udostępnia ona konfigurację, która będzie wymagana dla jej AVAudioSessionelementu .
  3. Aplikacja informuje system, że akcja została spełniona.
  4. Przed nawiązaniem połączenia zestaw CallKit zapewnia wysoki priorytet AVAudioSession zgodny z konfiguracją żądaną przez aplikację. Aplikacja zostanie powiadomiona DidActivateAudioSession za pośrednictwem metody .CXProviderDelegate

Praca z rozszerzeniami katalogu wywołań

Podczas pracy z zestawem CallKit rozszerzenia katalogu wywołań umożliwiają dodawanie zablokowanych numerów wywołań i identyfikowanie numerów specyficznych dla danej aplikacji VOIP do kontaktów w aplikacji Kontakt na urządzeniu z systemem iOS.

Implementowanie rozszerzenia katalogu wywołań

Aby zaimplementować rozszerzenie katalogu wywołań w aplikacji platformy Xamarin.iOS, wykonaj następujące czynności:

  1. Otwórz rozwiązanie aplikacji w Visual Studio dla komputerów Mac.

  2. Kliknij prawym przyciskiem myszy nazwę rozwiązania w Eksplorator rozwiązań i wybierz polecenie Dodaj>nowy projekt.

  3. Wybierz pozycję Rozszerzenia katalogu wywołania rozszerzeń> systemu iOS>, a następnie kliknij przycisk Dalej:

    Tworzenie nowego rozszerzenia katalogu wywołań

  4. Wprowadź nazwę rozszerzenia i kliknij przycisk Dalej:

    Wprowadzanie nazwy rozszerzenia

  5. Dostosuj nazwę projektu i/lub nazwę rozwiązania, jeśli jest to wymagane, a następnie kliknij przycisk Utwórz:

    Tworzenie projektu

Spowoduje to dodanie CallDirectoryHandler.cs klasy do projektu, która wygląda następująco:

using System;

using Foundation;
using CallKit;

namespace MonkeyCallDirExtension
{
    [Register ("CallDirectoryHandler")]
    public class CallDirectoryHandler : CXCallDirectoryProvider, ICXCallDirectoryExtensionContextDelegate
    {
        #region Constructors
        protected CallDirectoryHandler (IntPtr handle) : base (handle)
        {
            // Note: this .ctor should not contain any initialization logic.
        }
        #endregion

        #region Override Methods
        public override void BeginRequest (CXCallDirectoryExtensionContext context)
        {
            context.Delegate = this;

            if (!AddBlockingPhoneNumbers (context)) {
                Console.WriteLine ("Unable to add blocking phone numbers");
                var error = new NSError (new NSString ("CallDirectoryHandler"), 1, null);
                context.CancelRequest (error);
                return;
            }

            if (!AddIdentificationPhoneNumbers (context)) {
                Console.WriteLine ("Unable to add identification phone numbers");
                var error = new NSError (new NSString ("CallDirectoryHandler"), 2, null);
                context.CancelRequest (error);
                return;
            }

            context.CompleteRequest (null);
        }
        #endregion

        #region Private Methods
        private bool AddBlockingPhoneNumbers (CXCallDirectoryExtensionContext context)
        {
            // Retrieve phone numbers to block from data store. For optimal performance and memory usage when there are many phone numbers,
            // consider only loading a subset of numbers at a given time and using autorelease pool(s) to release objects allocated during each batch of numbers which are loaded.
            //
            // Numbers must be provided in numerically ascending order.

            long [] phoneNumbers = { 14085555555, 18005555555 };

            foreach (var phoneNumber in phoneNumbers)
                context.AddBlockingEntry (phoneNumber);

            return true;
        }

        private bool AddIdentificationPhoneNumbers (CXCallDirectoryExtensionContext context)
        {
            // Retrieve phone numbers to identify and their identification labels from data store. For optimal performance and memory usage when there are many phone numbers,
            // consider only loading a subset of numbers at a given time and using autorelease pool(s) to release objects allocated during each batch of numbers which are loaded.
            //
            // Numbers must be provided in numerically ascending order.

            long [] phoneNumbers = { 18775555555, 18885555555 };
            string [] labels = { "Telemarketer", "Local business" };

            for (var i = 0; i < phoneNumbers.Length; i++) {
                long phoneNumber = phoneNumbers [i];
                string label = labels [i];
                context.AddIdentificationEntry (phoneNumber, label);
            }

            return true;
        }
        #endregion

        #region Public Methods
        public void RequestFailed (CXCallDirectoryExtensionContext extensionContext, NSError error)
        {
            // An error occurred while adding blocking or identification entries, check the NSError for details.
            // For Call Directory error codes, see the CXErrorCodeCallDirectoryManagerError enum.
            //
            // This may be used to store the error details in a location accessible by the extension's containing app, so that the
            // app may be notified about errors which occurred while loading data even if the request to load data was initiated by
            // the user in Settings instead of via the app itself.
        }
        #endregion
    }
}

Aby BeginRequest zapewnić wymaganą funkcjonalność, należy zmodyfikować metodę w procedurze obsługi katalogów wywołań. W przypadku powyższego przykładu próbuje ustawić listę zablokowanych i dostępnych numerów w bazie danych kontaktów aplikacji VOIP. Jeśli którekolwiek z żądań zakończy się niepowodzeniem z jakiegokolwiek powodu, utwórz element , NSError aby opisać błąd i przekazać go do CancelRequest metody CXCallDirectoryExtensionContext klasy.

Aby ustawić zablokowane liczby, użyj AddBlockingEntry metody CXCallDirectoryExtensionContext klasy . Liczby podane w metodzie muszą być w kolejności liczbowej rosnącej. Aby uzyskać optymalną wydajność i użycie pamięci, jeśli istnieje wiele numerów telefonów, rozważ załadowanie tylko podzestawu liczb w danym momencie i użycie puli wersji automatycznych do zwolnienia obiektów przydzielonych podczas każdej partii numerów, które są ładowane.

Aby poinformować aplikację kontaktową o numerach kontaktowych znanych aplikacji VOIP, użyj AddIdentificationEntry metody CXCallDirectoryExtensionContext klasy i podaj zarówno numer, jak i etykietę identyfikującą. Ponownie liczby podane w metodzie muszą być w kolejności liczbowej rosnącej. Aby uzyskać optymalną wydajność i użycie pamięci, jeśli istnieje wiele numerów telefonów, rozważ załadowanie tylko podzestawu liczb w danym momencie i użycie puli wersji automatycznych do zwolnienia obiektów przydzielonych podczas każdej partii numerów, które są ładowane.

Podsumowanie

W tym artykule omówiono nowy interfejs API CallKit wydany przez firmę Apple w systemie iOS 10 oraz sposób implementacji go w aplikacjach VOIP platformy Xamarin.iOS. Pokazano, jak zestaw CallKit umożliwia aplikacji integrację z systemem iOS, jak zapewnia równoważność funkcji z wbudowanymi aplikacjami (takimi jak Telefon) i jak zwiększa widoczność aplikacji w całym systemie iOS w lokalizacjach, takich jak blokada i ekrany główne, za pośrednictwem interakcji Siri i aplikacji Kontakty.