Aracılığıyla paylaş


Xamarin.iOS'ta CallKit

iOS 10'daki yeni CallKit API,VOIP uygulamalarının i Telefon kullanıcı arabirimiyle tümleştirilmesine ve son kullanıcıya tanıdık bir arabirim ve deneyim sağlamasına yönelik bir yol sağlar. Bu API ile kullanıcılar iOS cihazının Kilit Ekranından VOIP çağrılarını görüntüleyebilir ve bunlarla etkileşimde bulunabilir ve Telefon uygulamasının Sık Kullanılanlar ve Son Kullanılanlar görünümlerini kullanarak kişileri yönetebilir.

CallKit hakkında

Apple'a göre CallKit, 3. taraf IP Üzerinden Ses (VOIP) uygulamalarını iOS 10'da birinci taraf deneyimine yükseltecek yeni bir çerçevedir. CallKit API'si, VOIP uygulamalarının i Telefon kullanıcı arabirimiyle tümleştirilmesine ve son kullanıcıya tanıdık bir arabirim ve deneyim sağlamasına olanak tanır. Yerleşik Telefon uygulamasında olduğu gibi, bir kullanıcı iOS cihazının Kilit Ekranından VOIP çağrılarını görüntüleyebilir ve bunlarla etkileşimde bulunabilir ve Telefon uygulamasının Sık Kullanılanlar ve Son Kullanılanlar görünümlerini kullanarak kişileri yönetebilir.

Buna ek olarak, CallKit API'si bir telefon numarasını bir adla ilişkilendirebilen (Arayan Kimliği) veya sisteme bir numaranın ne zaman engellenmesi gerektiğini (Arama Engelleme) söyleyebilen Uygulama Uzantıları oluşturma olanağı sağlar.

Mevcut VOIP uygulama deneyimi

Yeni CallKit API'sini ve yeteneklerini tartışmadan önce MonkeyCall adlı kurgusal bir VOIP uygulaması kullanarak iOS 9'da (ve daha küçük) 3. taraf VOIP uygulamasıyla geçerli kullanıcı deneyimine göz atın. MonkeyCall, kullanıcının mevcut iOS API'lerini kullanarak VOIP çağrıları gönderip almasını sağlayan basit bir uygulamadır.

Şu anda, kullanıcı MonkeyCall'da gelen bir çağrı alıyorsa ve i Telefon kilitlenmişse, Kilit ekranında alınan bildirim diğer herhangi bir bildirim türünden ayırt edilemez (örneğin İletiler veya Posta uygulamalarından gelenler gibi).

Kullanıcının aramayı yanıtlamak istemesi durumunda, aramayı kabul edip konuşmayı başlatabilmek için monkeycall bildirimini kaydırarak uygulamayı açması ve telefonun kilidini açmak için geçiş kodunu (veya kullanıcı Touch ID'sini) girmesi gerekir.

Telefonun kilidi açıksa deneyim de aynı derecede hantaldır. Yine gelen MonkeyCall çağrısı, ekranın üst kısmından içeri kaydırılan standart bir bildirim başlığı olarak görüntülenir. Bildirim geçici olduğundan, kullanıcının Bildirim Merkezi'ni açmaya zorlaması ve yanıtlamak için belirli bir bildirimi bulması ve ardından MonkeyCall uygulamasını el ile arayıp bulup başlatması kolayca kaçırılabilir.

CallKit VOIP uygulama deneyimi

MonkeyCall uygulamasında yeni CallKit API'leri uygulanarak, kullanıcının gelen VOIP çağrısı deneyimi iOS 10'da büyük ölçüde geliştirilebilir. Telefonu yukarıdan kilitlendiğinde VOIP araması alan kullanıcının örneğini inceleyin. CallKit uygulanarak çağrı, tam ekran, yerel kullanıcı arabirimi ve standart yanıt çekme işleviyle yerleşik Telefon uygulamasından çağrı alınırken olduğu gibi i Telefon'nin Kilit ekranında görünür.

Yine, bir MonkeyCall VOIP çağrısı alındığında i Telefon kilidi açılırsa, yerleşik Telefon uygulamasının aynı tam ekran, yerel kullanıcı arabirimi ve standart yanıta doğru çekme ve reddetme işlevselliği sunulur ve MonkeyCall özel bir zil sesi çalma seçeneğine sahiptir.

CallKit, MonkeyCall'a ek işlevsellik sağlayarak VOIP çağrılarının diğer arama türleriyle etkileşim kurmasına, yerleşik Son kullanılanlar ve Sık Kullanılanlar listelerinde görünmesine, yerleşik Rahatsız Etmeyin ve Engelleme özelliklerini kullanmasına, Siri'den MonkeyCall çağrılarını başlatmasına ve kullanıcıların Kişiler uygulamasındaki kişilere MonkeyCall çağrıları atamasına olanak tanır.

Aşağıdaki bölümlerde CallKit mimarisi, gelen ve giden çağrı akışları ve CallKit API'si ayrıntılı olarak ele alınacaktır.

CallKit mimarisi

iOS 10'da Apple, Tüm Sistem Hizmetlerinde CallKit'i benimsemiştir. Örneğin, CarPlay'de yapılan çağrılar CallKit aracılığıyla Sistem Kullanıcı Arabirimi tarafından bilinir. Aşağıda verilen örnekte MonkeyCall, CallKit'i benimsediğinden, Sistem tarafından bu yerleşik Sistem Hizmetleri ile aynı şekilde bilinir ve aynı özelliklerin tümünü alır:

CallKit Hizmet Yığını

Yukarıdaki diyagramdan MonkeyCall Uygulamasına daha yakından bakın. Uygulama, kendi ağıyla iletişim kurmak için tüm kodunu ve kendi Kullanıcı Arabirimlerini içerir. Sistemle iletişim kurmak için CallKit'e bağlanır:

MonkeyCall Uygulama Mimarisi

CallKit'te uygulamanın kullandığı iki ana arabirim vardır:

  • CXProvider - Bu, MonkeyCall uygulamasının oluşabilecek bant dışı bildirimleri sisteme bildirmesini sağlar.
  • CXCallController - MonkeyCall uygulamasının sistemi yerel kullanıcı eylemleri hakkında bilgilendirmesine izin verir.

The CXProvider

Yukarıda belirtildiği gibi, CXProvider bir uygulamanın oluşabilecek bant dışı bildirimleri sisteme bildirmesine izin verir. Bunlar, yerel kullanıcı eylemleri nedeniyle gerçekleşmeyen ancak gelen çağrılar gibi dış olaylar nedeniyle oluşan bildirimdir.

Bir uygulama aşağıdakiler için şunu CXProvider kullanmalıdır:

  • Gelen bir aramayı Sisteme bildirin.
  • Giden aramanın Sisteme bağlandığını bildirin.
  • Uzak kullanıcıyı Sistem çağrısını sonlandırarak bildirin.

Uygulama sistemle iletişim kurmak istediğinde sınıfını CXCallUpdate kullanır ve Sistemin uygulamayla iletişim kurması gerektiğinde sınıfını CXAction kullanır:

CXProvider aracılığıyla sistemle iletişim kurma

The CXCallController

, CXCallController bir uygulamanın voip çağrısı başlatan kullanıcı gibi yerel kullanıcı eylemleri hakkında sisteme bilgi vermesine olanak tanır. Bir CXCallController uygulama uygulayarak sistemdeki diğer çağrı türleriyle etkileşime geçer. Örneğin, devam eden etkin bir telefon araması varsa, CXCallController VOIP uygulamasının bu aramayı beklemeye alıp VOIP araması başlatmasına veya yanıtlamasına izin verebilir.

Bir uygulama aşağıdakiler için şunu CXCallController kullanmalıdır:

  • Kullanıcının Sisteme giden bir çağrı başlattığını bildirin.
  • Kullanıcı Sistem'e gelen bir aramayı yanıtladığında rapor edin.
  • Kullanıcının Sistem çağrısını ne zaman sonlandırdığında rapor edin.

Uygulama yerel kullanıcı eylemlerini sisteme iletmek istediğinde sınıfını CXTransaction kullanır:

CXCallController kullanarak sisteme raporlama

CallKit Uygulama

Aşağıdaki bölümlerde, Xamarin.iOS VOIP uygulamasında CallKit'in nasıl uygulanacağı gösterilir. Örneğin, bu belge kurgusal MonkeyCall VOIP uygulamasındaki kodu kullanacaktır. Burada sunulan kod birkaç destekleyici sınıfı temsil eder; CallKit'e özgü bölümler aşağıdaki bölümlerde ayrıntılı olarak ele alınacaktır.

ActiveCall sınıfı

sınıfı, ActiveCall MonkeyCall uygulaması tarafından şu anda etkin olan bir VOIP çağrısı hakkındaki tüm bilgileri aşağıdaki gibi tutmak için kullanılır:

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 çağrısının durumunu tanımlayan birkaç özellik ve çağrı durumu değiştiğinde tetiklenebilir iki olay içerir. Bu yalnızca bir örnek olduğundan, bir çağrıyı başlatma, yanıtlama ve sonlandırma benzetimini yapmak için kullanılan üç yöntem vardır.

StartCallRequest sınıfı

StartCallRequest Statik sınıfı, giden çağrılarla çalışırken kullanılacak birkaç yardımcı yöntem sağlar:

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;
            }
        }
    }
}

CallHandleFromURL ve CallHandleFromActivity sınıfları, giden aramada çağrılan kişinin kişi tanıtıcısını almak için AppDelegate içinde kullanılır. Daha fazla bilgi için lütfen aşağıdaki Giden Çağrıları İşleme bölümüne bakın.

ActiveCallManager sınıfı

sınıfı MonkeyCall ActiveCallManager uygulamasındaki tüm açık çağrıları işler.

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
    }
}

Yine, bu yalnızca bir simülasyon olduğundan, ActiveCallManager yalnızca bir nesne koleksiyonu ActiveCall tutar ve özelliği tarafından UUID belirli bir çağrıyı bulmak için bir yordamı vardır. Ayrıca, giden çağrının bekleme durumunu başlatma, sonlandırma ve değiştirme yöntemlerini de içerir. Daha fazla bilgi için lütfen aşağıdaki Giden Çağrıları İşleme bölümüne bakın.

ProviderDelegate sınıfı

Yukarıda açıklandığı gibi, CXProvider bant dışı bildirimler için uygulama ile Sistem arasında iki yönlü iletişim sağlar. Geliştiricinin bir özel CXProviderDelegate sağlaması ve bant dışı CallKit olaylarını işlemek için bunu uygulamasına eklemesi CXProvider gerekir. MonkeyCall aşağıdakileri CXProviderDelegatekullanır:

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
    }
}

Bu temsilcinin bir örneği oluşturulduğunda, herhangi bir çağrı etkinliğini işlemek için kullanacağı değeri geçirilir ActiveCallManager . Ardından, öğesinin yanıt vereceği tanıtıcı türlerini (CXHandleType) CXProvider tanımlar:

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

Ayrıca, bir arama devam ederken uygulamanın simgesine uygulanacak şablon görüntüsünü alır:

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

Bu değerler, öğesini yapılandırmak CXProvideriçin kullanılacak bir CXProviderConfiguration içinde paketlenir:

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

Temsilci daha sonra bu yapılandırmalarla yeni CXProvider bir oluşturur ve kendisini buna ekler:

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

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

CallKit kullanırken, uygulama artık kendi ses oturumlarını oluşturmaz ve işlemez, bunun yerine Sistemin kendisi için oluşturup işleyecek bir ses oturumu yapılandırması ve kullanması gerekir.

Bu gerçek bir uygulama olsaydı, çağrıyı DidActivateAudioSession Sistem tarafından sağlanan önceden yapılandırılmış AVAudioSession bir şekilde başlatmak için yöntemi kullanılırdı:

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

Sistem tarafından sağlanan ses oturumuyla bağlantısını sonlandırmak ve serbest bırakmak için yöntemini de kullanır DidDeactivateAudioSession :

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

Kodun geri kalanı, izleyen bölümlerde ayrıntılı olarak ele alınacaktır.

AppDelegate sınıfı

MonkeyCall, appDelegate'i kullanarak uygulama genelinde kullanılacak ve CXProviderDelegate örneklerini ActiveCallManager barındırıyor:

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
    }
}

OpenUrl ve ContinueUserActivity geçersiz kılma yöntemleri, uygulama giden çağrıyı işlerken kullanılır. Daha fazla bilgi için lütfen aşağıdaki Giden Çağrıları İşleme bölümüne bakın.

Gelen çağrıları işleme

Gelen VOIP çağrısının tipik bir gelen arama iş akışı sırasında geçebileceği çeşitli durumlar ve işlemler vardır:

  • Kullanıcıya (ve Sisteme) gelen bir çağrının mevcut olduğunu bildirme.
  • Kullanıcı aramayı yanıtlamak istediğinde bildirim alma ve aramayı diğer kullanıcıyla başlatma.
  • Kullanıcı geçerli çağrıyı sonlandırmak istediğinde Sistemi ve İletişim Ağını bilgilendirin.

Aşağıdaki bölümlerde, bir uygulamanın bir örnek olarak MonkeyCall VOIP uygulamasını kullanarak gelen arama iş akışını işlemek için CallKit'i nasıl kullanabileceğine ayrıntılı bir bakış sunulur.

Kullanıcıyı gelen arama hakkında bilgilendirme

Uzak kullanıcı yerel kullanıcıyla VOIP konuşması başlattığında aşağıdakiler gerçekleşir:

Uzak kullanıcı VOIP konuşması başlattı

  1. Uygulama, İletişim Ağı'ndan gelen bir VOIP çağrısı olduğuna dair bir bildirim alır.
  2. Uygulama, çağrısı hakkında CXProvider bilgilendirmek üzere Sisteme bir CXCallUpdate göndermek için öğesini kullanır.
  3. Sistem çağrısını CallKit kullanarak Sistem kullanıcı arabirimine, Sistem Hizmetleri'ne ve diğer VOIP uygulamalarına yayımlar.

Örneğin, içinde 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);
        }
    });
}

Bu kod yeni CXCallUpdate bir örnek oluşturur ve çağıranı tanımlayan bir tanıtıcı ekler. Ardından, çağrının sistemini bilgilendirmek için sınıfının yöntemini CXProvider kullanırReportNewIncomingCall. Başarılı olursa, çağrı uygulamanın etkin çağrı koleksiyonuna eklenir, aksi takdirde hatanın kullanıcıya bildirilmesi gerekir.

Gelen aramayı yanıtlayan kullanıcı

Kullanıcı gelen VOIP çağrısını yanıtlamak isterse aşağıdakiler gerçekleşir:

Kullanıcı gelen VOIP çağrısını yanıtlar

  1. Sistem kullanıcı arabirimi, kullanıcının VOIP çağrısını yanıtlamak istediğini Sisteme bildirir.
  2. Sistem, uygulamaya CXProvider Yanıt Amacı hakkında bilgi veren bir CXAnswerCallAction gönderir.
  3. Uygulama, İletişim Ağı'na kullanıcının aramayı yanıtladığını bildirir ve VOIP çağrısı her zamanki gibi devam eder.

Örneğin, içinde 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 ();
        }
    });
}

Bu kod ilk olarak etkin çağrı listesinde verilen çağrıyı arar. Çağrı bulunamazsa sisteme bildirim gönderilir ve yöntemden çıkılır. Bulunursa, AnswerCall çağrıyı ActiveCall başlatmak için sınıfının yöntemi çağrılır ve sistem başarılı veya başarısız olursa bilgidir.

Gelen aramayı sonlandıran kullanıcı

Kullanıcı, uygulamanın kullanıcı arabiriminden çağrıyı sonlandırmak isterse aşağıdakiler gerçekleşir:

Kullanıcı, uygulamanın kullanıcı arabiriminden aramayı sonlandırır

  1. Uygulama, çağrının sona erdiğini bildirmek için Sisteme gönderilen bir CXTransaction içinde paketlenen bir oluştururCXEndCallAction.
  2. Sistem, Arama Sonlandırma Amacını doğrular ve aracılığıyla CXProvideruygulamaya geri gönderirCXEndCallAction.
  3. Ardından uygulama, İletişim Ağı'na çağrının sona erdiğini bildirir.

Örneğin, içinde 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 ();
        }
    });
}

Bu kod ilk olarak etkin çağrı listesinde verilen çağrıyı arar. Çağrı bulunamazsa sisteme bildirim gönderilir ve yöntemden çıkılır. Bulunursa, EndCall çağrıyı ActiveCall sonlandırmak için sınıfının yöntemi çağrılır ve sistem başarılı veya başarısız olursa bilgidir. Başarılı olursa, çağrı etkin çağrılar koleksiyonundan kaldırılır.

Birden çok çağrıyı yönetme

Çoğu VOIP uygulaması aynı anda birden çok çağrıyı işleyebilir. Örneğin, şu anda etkin bir VOIP araması varsa ve uygulama yeni bir gelen arama olduğuna ilişkin bildirim alıyorsa, kullanıcı ikinci aramayı yanıtlamak için ilk aramada duraklatabilir veya kapatabilir.

Yukarıda belirtilen durumda, Sistem uygulamaya birden çok eylemin (ve CXAnswerCallActiongibiCXEndCallAction) bir listesini içeren bir gönderirCXTransaction. Sistemin kullanıcı arabirimini uygun şekilde güncelleştirebilmesi için bu eylemlerin tümünün tek tek yerine getirilmesi gerekir.

Giden çağrıları işleme

Kullanıcı Son Kullanılanlar listesinden (Telefon uygulamasında) bir girişe dokunursa( örneğin, uygulamaya ait bir çağrıdan), sistem tarafından bir Arama Başlatma Amacı gönderilir:

Başlangıç Çağrısı Amacı Alma

  1. Uygulama, Sistemden aldığı Çağrıyı Başlatma Amacını temel alan bir Arama Başlatma Eylemi oluşturur.
  2. Uygulama, sistemden Çağrı Başlatma Eylemini istemek için öğesini kullanır CXCallController .
  3. Sistem Eylemi kabul ederse, temsilci aracılığıyla XCProvider uygulamaya döndürülür.
  4. Uygulama giden çağrıyı İletişim Ağı ile başlatır.

Amaçlar hakkında daha fazla bilgi için lütfen Intents and Intents UI Extensions belgelerimize bakın.

Giden arama yaşam döngüsü

CallKit ve giden bir çağrıyla çalışırken uygulamanın sistemi aşağıdaki yaşam döngüsü olayları hakkında bilgilendirmesi gerekir:

  1. Başlatılıyor - Giden bir çağrının başlamak üzere olduğunu sisteme bildirin.
  2. Başlatıldı - Giden bir çağrının başlatıldığını sisteme bildirin.
  3. Bağlan ing - Giden çağrının bağlandığını sisteme bildirin.
  4. Bağlan- Giden aramanın bağlandığını ve her iki tarafın da şimdi konuşabileceğini bildirin.

Örneğin, aşağıdaki kod giden bir çağrı başlatır:

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);
}

bir oluşturur CXHandle ve sınıfı yöntemini CXCallController kullanarak RequestTransaction Sisteme gönderilen bir içine paketlenmiş bir CXTransaction yapılandırmak CXStartCallAction için kullanır. Sistem, yöntemini çağırarakRequestTransaction, yeni çağrı başlamadan önce kaynak (Telefon uygulaması, FaceTime, VOIP vb.) fark etmeden mevcut aramaları beklemeye alabilir.

Giden VOIP araması başlatma isteği Siri, Kişi kartındaki bir giriş (Kişiler uygulamasında) veya Son Kullanılanlar listesinden (Telefon uygulamasında) gibi birkaç farklı kaynaktan gelebilir. Bu durumlarda, uygulamaya içinde NSUserActivity bir Çağrı Başlatma Amacı gönderilir ve AppDelegate'in bunu işlemesi gerekir:

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 Burada yardımcı sınıfının StartCallRequest yöntemi, tanıtıcıyı çağrılan kişiye almak için kullanılır (yukarıdaki StartCallRequest Sınıfına bakın).

PerformStartCallAction ProviderDelegate Sınıfı yöntemi, son olarak gerçek giden çağrıyı başlatmak ve Sistem'e yaşam döngüsünü bildirmek için kullanılır:

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 ();
        }
    });
}

Sınıfının bir örneğini ActiveCall oluşturur (devam eden çağrı hakkındaki bilgileri tutmak için) ve çağrılan kişiyle doldurulur. StartingConnectionChanged ve ConnectedChanged olayları, giden arama yaşam döngüsünü izlemek ve raporlamak için kullanılır. Çağrı başlatılır ve Sistem Eylemin yerine getirildiğini bildirir.

Giden aramayı sonlandırma

Kullanıcı giden bir çağrıyı bitirdiğinde ve aramayı sonlandırmak istediğinde aşağıdaki kod kullanılabilir:

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);
}

Çağrısının UUID değeriyle uçtan uca bir CXEndCallAction oluşturursa, sınıfı yöntemini CXCallController kullanarak RequestTransaction Sisteme gönderilen bir CXTransaction içinde paketler.

Ek CallKit ayrıntıları

Bu bölümde, geliştiricinin CallKit ile çalışırken dikkate alması gereken bazı ek ayrıntılar ele alınacaktır:

  • Sağlayıcı Yapılandırması
  • Eylem Hataları
  • Sistem Kısıtlamaları
  • VOIP Ses

Sağlayıcı yapılandırması

Sağlayıcı yapılandırması, iOS 10 VOIP uygulamasının CallKit ile çalışırken kullanıcı deneyimini (yerel Arama İçi Kullanıcı Arabirimi içinde) özelleştirmesine olanak tanır.

Bir uygulama aşağıdaki özelleştirme türlerini yapabilir:

  • Yerelleştirilmiş bir ad görüntüleme.
  • Görüntülü arama desteğini etkinleştirin.
  • Kendi şablon görüntüsü simgesini sunarak Arama İçi kullanıcı arabirimindeki düğmeleri özelleştirin. Özel düğmelerle kullanıcı etkileşimi doğrudan işlenecek uygulamaya gönderilir.

Eylem hataları

CallKit kullanan iOS 10 VOIP uygulamalarının, başarısız olan Eylemleri düzgün bir şekilde işlemesi ve kullanıcıyı Eylem durumu hakkında her zaman bilgilendirmesi gerekir.

Aşağıdaki örneği dikkate alın:

  1. Uygulama bir Arama Başlatma Eylemi aldı ve İletişim Ağı ile yeni bir VOIP çağrısı başlatma işlemine başladı.
  2. Ağ iletişiminin sınırlı olması veya olmaması nedeniyle bu bağlantı başarısız olur.
  3. Uygulamanın, Sistemi hatayla ilgili bilgilendirmek için Yeniden Başlat Çağrısı Eylemine (Action.Fail()) Başarısız iletisi göndermesi gerekir.
  4. Bu, Sistemin kullanıcıya çağrının durumunu bildirmesini sağlar. Örneğin, Arama Hatası kullanıcı arabirimini görüntülemek için.

Ayrıca, bir iOS 10 VOIP uygulamasının, beklenen bir Eylemin belirli bir süre içinde işlenememesi durumunda oluşabilecek Zaman Aşımı Hatalarına yanıt vermesi gerekir. CallKit tarafından sağlanan her Eylem Türünün kendisiyle ilişkilendirilmiş en fazla Zaman Aşımı değeri vardır. Bu Zaman Aşımı değerleri, kullanıcı tarafından istenen Tüm CallKit Eyleminin duyarlı bir şekilde işlenmesini sağlar, böylece işletim sistemi akıcı ve hızlı yanıt verir.

Sağlayıcı Temsilcisinde (CXProviderDelegate) bu Zaman Aşımı durumlarını düzgün bir şekilde işlemek için geçersiz kılınması gereken çeşitli yöntemler vardır.

Sistem kısıtlamaları

iOS 10 VOIP uygulamasını çalıştıran iOS cihazının geçerli durumuna bağlı olarak, belirli sistem kısıtlamaları uygulanabilir.

Örneğin, gelen VOIP çağrısı şu durumlarda Sistem tarafından kısıtlanabilir:

  1. Arayan kişi, kullanıcının Engellenen Arayan Listesi'ndedir.
  2. Kullanıcının iOS cihazı Rahatsız Etmeyin modundadır.

VoIP çağrısı bu durumlardan herhangi biri tarafından kısıtlanmışsa, bunu işlemek için aşağıdaki kodu kullanın:

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
                }
            }
        });
    }

}

VOIP sesi

CallKit, bir iOS 10 VOIP uygulamasının canlı VOIP araması sırasında gerektireceği ses kaynaklarını işlemek için çeşitli avantajlar sağlar. En büyük avantajlardan biri, iOS 10'da çalışırken uygulamanın ses oturumunun yükseltilmiş öncelikleri olmasıdır. Bu, yerleşik Telefon ve FaceTime uygulamalarıyla aynı öncelik düzeyidir ve bu gelişmiş öncelik düzeyi, çalışan diğer uygulamaların VOIP uygulamasının ses oturumunu kesintiye uğratmasını engeller.

Buna ek olarak CallKit, kullanıcı tercihlerine ve cihaz durumlarına göre canlı arama sırasında performansı geliştirebilen ve VOIP sesini belirli çıkış cihazlarına akıllı bir şekilde yönlendirebilen diğer ses yönlendirme ipuçlarına erişebilir. Örneğin, bluetooth kulaklık, canlı CarPlay bağlantısı veya Erişilebilirlik ayarları gibi bağlı cihazlara göre.

CallKit kullanan tipik bir VOIP çağrısının yaşam döngüsü sırasında uygulamanın CallKit'in sağlayacağı Ses Akışını yapılandırması gerekir. Aşağıdaki örneğe göz atın:

Başlatma Çağrısı Eylem Dizisi

  1. Gelen aramayı yanıtlamak için uygulama tarafından Bir Arama Başlat Eylemi alınır.
  2. Bu Eylem uygulama tarafından yerine getirilmeden önce, uygulaması için gereken yapılandırmayı AVAudioSessionsağlar.
  3. Uygulama, Eylemin yerine getirildiğini Sisteme bildirir.
  4. Çağrı bağlanmadan önce CallKit, uygulamanın istediği yapılandırmayla eşleşen yüksek öncelikli AVAudioSession bir özellik sağlar. Uygulama, yöntemiyle DidActivateAudioSessionCXProviderDelegatebildirilir.

Arama dizini uzantılarıyla çalışma

CallKit ile çalışırken, Arama Dizini Uzantıları iOS cihazındaki Kişi uygulamasındaki kişilere engellenen arama numaraları eklemenin ve belirli bir VOIP uygulamasına özgü numaraları tanımlamanın bir yolunu sağlar.

Arama dizini uzantısı uygulama

Xamarin.iOS uygulamasında Çağrı Dizini Uzantısı uygulamak için aşağıdakileri yapın:

  1. Uygulamanın çözümünü Mac için Visual Studio açın.

  2. Çözüm Gezgini Çözüm Adı'na sağ tıklayın ve Yeni Proje Ekle'yi>seçin.

  3. iOS>Uzantıları Dizin Uzantılarını> Çağır'ı seçin ve İleri düğmesine tıklayın:

    Yeni Arama Dizini Uzantısı Oluşturma

  4. Uzantı için bir Ad girin ve İleri düğmesine tıklayın:

    Uzantı için bir ad girme

  5. Gerekirse Proje Adını ve/veya Çözüm Adını ayarlayın ve Oluştur düğmesine tıklayın:

    Projeyi oluşturma

Bu, projeye aşağıdakine benzer bir CallDirectoryHandler.cs sınıf ekler:

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
    }
}

BeginRequest Çağrı Dizin İşleyicisi'ndeki yöntemin gerekli işlevselliği sağlamak için değiştirilmesi gerekir. Yukarıdaki örnekte VOIP uygulamasının kişiler veritabanında engellenen ve kullanılabilir sayıların listesini ayarlamaya çalışır. herhangi bir nedenle isteklerden biri başarısız olursa, hatayı açıklamak için bir NSError oluşturun ve sınıfın CancelRequestCXCallDirectoryExtensionContext yöntemini geçirin.

Engellenen sayıları ayarlamak için sınıfının yöntemini CXCallDirectoryExtensionContext kullanınAddBlockingEntry. yöntemine sağlanan sayılar sayısal olarak artan sırada olmalıdır . Çok sayıda telefon numarası olduğunda en iyi performans ve bellek kullanımı için, belirli bir zamanda yalnızca bir sayı alt kümesini yüklemeyi ve yüklenen her numara toplu işlemi sırasında ayrılan nesneleri serbest bırakmak için otomatik sürüm havuzlarını kullanmayı göz önünde bulundurun.

VOIP uygulaması tarafından bilinen kişi numaralarını Kişi uygulamasına bildirmek için sınıfının yöntemini CXCallDirectoryExtensionContext kullanın AddIdentificationEntry ve hem sayıyı hem de tanımlayıcı etiketi sağlayın. Yine yönteme sağlanan sayıların sayısal olarak artan sırada olması gerekir . Çok sayıda telefon numarası olduğunda en iyi performans ve bellek kullanımı için, belirli bir zamanda yalnızca bir sayı alt kümesini yüklemeyi ve yüklenen her numara toplu işlemi sırasında ayrılan nesneleri serbest bırakmak için otomatik sürüm havuzlarını kullanmayı göz önünde bulundurun.

Özet

Bu makalede, Apple'ın iOS 10'da yayımladığı yeni CallKit API'si ve Xamarin.iOS VOIP uygulamalarında nasıl uygulandığı ele alınmıştır. CallKit'in bir uygulamanın iOS Sistemi ile tümleştirilmesine nasıl izin verdiği, yerleşik uygulamalarla (Telefon gibi) özellik eşliği sağlama şekli ve Siri etkileşimleri ve Kişiler uygulamaları aracılığıyla Kilit ve Giriş Ekranları gibi konumlarda iOS genelinde uygulamanın görünürlüğünü nasıl artırdığını göstermiştir.