한국어

Coding

온누리070 플레이스토어 다운로드
    acrobits softphone
     온누리 070 카카오 프러스 친구추가온누리 070 카카오 프러스 친구추가친추
     카카오톡 채팅 상담 카카오톡 채팅 상담카톡
    
     라인상담
     라인으로 공유

     페북공유

   ◎위챗 : speedseoul


  
     PAYPAL
     
     PRICE
     

pixel.gif

    before pay call 0088 from app


글 수 101

https://www.raywenderlich.com/701-callkit-tutorial-for-ios



CallKit Tutorial for iOS

Learn how your app can use CallKit for system-level phone integration and how you can build a directory extension for call blocking/identification.

Version

  • Swift 3, iOS 10, Xcode 8

Life on iOS wasn’t always perfect for VoIP app developers. In particular, delivering notifications was tough. With your app in the background, your only option was a regular notification, which is easy to miss. Compare it with the rich, built-in call UI, and suddenly your app won’t feel so integrated.

Luckily, Apple introduced CallKit in iOS 10 to change all that!

In this tutorial you’ll get a glimpse of CallKit’s power by building an app which:

  • Uses system services to report incoming and outgoing calls.
  • Manages a call directory to identify, or block incoming calls.

Note: CallKit features won’t work in the simulator. In order to follow along with this tutorial, you’ll need an iPhone with iOS 10.2 installed.

Getting Started

Download the starter project for this tutorial, and unzip it. In order to debug the project on your device, you’ll need to set up code signing. Open the project file in Xcode, and select Hotline in the project navigator.

You’ll start by changing the bundle identifier. With the project selected, go to the General tab, and find the Identity section. Change the bundle identifier to something unique:

Changing the bundle identifier

Next, look for the Signing section. Select your preferred development team (in my case, it’s my personal team) in the dropdown next to Team. Also make sure, that “Automatically manage signing” is checked. This will allow Xcode to automatically create the provisioning profile for the app.

Setting up code signing

To test your setup, build and run the app on your iPhone.

First run

This app does NOTHING!

This app does NOTHING!

Currently the app won’t do much, but you’ll notice that there are already quite a few source files in the starter project. They are mostly responsible for setting up the UI, and handling user interactions, but there are two main classes which are worth a look before moving on:

  • Call represents a phone call. The class exposes properties for identifying calls (such as its UUID, or handle), and also lifecycle callbacks indicating when the user starts, answers or ends a call.
  • CallManager currently maintains the list of ongoing calls in the app, and has methods for adding or removing calls. You will expand this class further throughout the tutorial.

What is CallKit?

CallKit is a new framework that aims to improve the VoIP experience by allowing apps to integrate tightly with the native Phone UI. By adopting CallKit, your app will be able to:

  • Use the native incoming call screen in both the locked and unlocked states.
  • Start calls from the native Phone app’s Contacts, Favorites and Recents screens.
  • Interplay with other calls in the system.

In this section, you’ll get more familiar with the CallKit architecture. The diagram below shows all the key players:

arch00

When working with CallKit, there are two primary classes you’ll interact with: CXProvider, and CXCallController. Time to dive in!

CXProvider

Your app will use CXProvider to report any out-of-band notifications to the system. These are usually external events, such as an incoming call.

Whenever such an event occurs, CXProvider will create a call update to notify the system. What’s a call update, you ask? Call updates encapsulate new, or changed call-related information. They are represented by the CXCallUpdate class, which exposes properties such as the caller’s name, or whether it’s an audio-only, or a video call.

In turn, whenever the system wants to notify the app of any events, it does so in the form of CXAction instances. CXAction is an abstract class, which represents telephony actions. For each action, CallKit provides a different concrete implementation of CXAction. For instance, initiating an outgoing call is represented by CXStartCallAction, while CXAnswerCallAction is used for answering an incoming call. Actions are identified by a unique UUID, and can either fail, or fulfill.

Apps can communicate with CXProvider through the CXProviderDelegate protocol, which defines methods for provider lifecycle events, and incoming actions.

CXCallController

The app will use CXCallController to let the system know about any user-initiated requests, such as a “Start call” action. This is the key difference between the CXProvider and the CXCallController: while the provider’s job is to report to the system, the call controller makes requests from the system on behalf of the user.

The call controller uses transactions to make these requests. Transactions, represented by CXTransaction contain one or more CXAction instances. The call controller sends transactions to the system, and if everything is in order, the system will respond with the appropriate action to the provider.

That sure was a lot of information, but how does this work in practice?

Incoming Calls

The diagram below shows a high-level overview of an incoming call flow:

incoming

  1. Whenever there’s an incoming call, the app will construct a CXCallUpdate and use the provider to send it to the system.
  2. At this point the system will publish this as an incoming call to all of its services.
  3. When the user answers the call, the system will send a CXAnswerCallActioninstance to the provider.
  4. The app can answer the call by implementing the appropriate CXProviderDelegate method.

The first step will be creating the delegate for the provider.

Head back to Xcode, and with the App group highlighted in the project navigator, go to File\New\File…, and choose iOS\Source\Swift File. Set the name to ProviderDelegate, and click Create.

Add the following code to the file:

import AVFoundation
import CallKit

class ProviderDelegate: NSObject {
  // 1.
  fileprivate let callManager: CallManager
  fileprivate let provider: CXProvider
  
  init(callManager: CallManager) {
    self.callManager = callManager
    // 2.
    provider = CXProvider(configuration: type(of: self).providerConfiguration)
    
    super.init()
    // 3.
    provider.setDelegate(self, queue: nil)
  }
  
  // 4.
  static var providerConfiguration: CXProviderConfiguration {
    let providerConfiguration = CXProviderConfiguration(localizedName: "Hotline")
    
    providerConfiguration.supportsVideo = true
    providerConfiguration.maximumCallsPerCallGroup = 1
    providerConfiguration.supportedHandleTypes = [.phoneNumber]
    
    return providerConfiguration
  }
}

This is what’s happening:

  1. The provider delegate will interact with both the provider and the call controller, so you’ll store references to both. The properties are marked fileprivate, so that you’ll be able to reach them from extensions in the same file.
  2. You’ll initialize the provider with the appropriate CXProviderConfiguration, stored as a static variable below. A provider configuration specifies the behavior and capabilities of the calls.
  3. To respond to events coming from the provider, you’ll set its delegate. This line will cause a build error, as ProviderDelegate doesn’t conform to CXProviderDelegate yet.
  4. In the case of Hotline, the provider configuration will allow video calls, phone number handles, and restrict the number of call groups to one. For further customization, refer to the CallKit documentation.

Just below the configuration, add the following helper method:

func reportIncomingCall(uuid: UUID, handle: String, hasVideo: Bool = false, completion: ((NSError?) -> Void)?) {
  // 1.
  let update = CXCallUpdate()
  update.remoteHandle = CXHandle(type: .phoneNumber, value: handle)
  update.hasVideo = hasVideo
  
  // 2.
  provider.reportNewIncomingCall(with: uuid, update: update) { error in
    if error == nil {
      // 3.
      let call = Call(uuid: uuid, handle: handle)
      self.callManager.add(call: call)
    }
    
    // 4.
    completion?(error as? NSError)
  }
}

This helper method will allow the app to call the CXProvider API to report an incoming call. Here’s what’s going on:

  1. You prepare a call update for the system, which will contain all the relevant call metadata.
  2. Invoking reportIncomingCall(with:update:completion) on the provider will notify the system of the incoming call.
  3. The completion handler will be called once the system processes the call. If there were no errors, you create a Call instance, and add it to the list of calls via the CallManager.
  4. Invoke the completion handler, if it’s not nil.

This method can be invoked by other classes in the app in order to simulate incoming calls.

The next step is to ensure protocol conformance. Still in ProviderDelegate.swift, declare a new extension to conform to CXProviderDelegate:

extension ProviderDelegate: CXProviderDelegate {
  
  func providerDidReset(_ provider: CXProvider) {
    stopAudio()
    
    for call in callManager.calls {
      call.end()
    }
    
    callManager.removeAllCalls()
  }
}

CXProviderDelegate specifies only one required method, providerDidReset(_:). The provider invokes this method when it’s reset, giving your app the opportunity to clean up any ongoing calls, and revert to a clean state. In this implementation you’ll terminate the ongoing audio session and dispose of any active calls.

Now that ProviderDelegate offers a way to report incoming calls, it’s time to use it!

With the App group highlighted in the project navigator, open AppDelegate.swiftfor editing. You’ll start by adding a new property to the class:

lazy var providerDelegate: ProviderDelegate = ProviderDelegate(callManager: self.callManager)

The provider delegate is ready to be used! Add the following method to AppDelegate:

func displayIncomingCall(uuid: UUID, handle: String, hasVideo: Bool = false, completion: ((NSError?) -> Void)?) {
  providerDelegate.reportIncomingCall(uuid: uuid, handle: handle, hasVideo: hasVideo, completion: completion)
}

This method will let other classes access the provider delegate’s helper method.

The final piece of the puzzle is hooking up this call to the user interface. Expand the UI/View Controllers group in the project navigator, and open CallsViewController.swift, which is the controller for the main screen of the app. Find the empty implementation of unwindSegueForNewCall(_:), and replace it with the following code:

@IBAction private func unwindForNewCall(_ segue: UIStoryboardSegue) {
  // 1.
  let newCallController = segue.source as! NewCallViewController
  guard let handle = newCallController.handle else { return }
  let videoEnabled = newCallController.videoEnabled
  
  // 2.
  let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
  DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + 1.5) {
    AppDelegate.shared.displayIncomingCall(uuid: UUID(), handle: handle, hasVideo: videoEnabled) { _ in
      UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
    }
  }
}

The snippet does the following:

  1. You’ll extract the properties of the call from NewCallViewController, which is the source of this unwind segue.
  2. The user can suspend the app before the action completes, so it should use a background task.

Now that everything is hooked up, build and run the application, and do the following:

  1. Tap the plus button in the right-hand corner.
  2. Enter any number, make sure “Incoming” is selected in the segmented control, and tap Done.
  3. Lock the screen. This step is important, since it’s the only way to access the rich, native in-call UI.

Within a few seconds, you’ll be presented with the native incoming call UI:

It’s working!

It’s working!

However, as soon as you answer the call, you’ll notice that the UI remains stuck in the following state:

Or is it?

Or is it?

This is because you still have to implement the piece responsible for answering the call. Go back to Xcode, return to ProviderDelegate.swift, and add the following code to the class extension:

func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
  // 1.
  guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
    action.fail()
    return
  }
  
  // 2.
  configureAudioSession()
  // 3.
  call.answer()
  // 4.
  action.fulfill()
}

// 5.
func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
  startAudio()
}

Here is the step-by-step breakdown:

  1. You’ll start by getting a reference from the call manager, corresponding to the UUID of the call to answer.
  2. It is the app’s responsibility to configure the audio session for the call. The system will take care of activating the session at an elevated priority.
  3. By invoking answer(), you’ll indicate that the call is now active.
  4. When processing an action, it’s important to either fail or fulfill it. If there were no errors during the process, you can call fulfill() to indicate success.
  5. Once the system activates the provider’s audio session, the delegate is notified. This is your chance to begin processing the call’s audio.

Build and run the app, and start an incoming call again. When you answer the call, the system will successfully transition into an ongoing call state.

That’s more like it!

That’s more like it!

If you unlock your phone, you’ll notice that both iOS and the app now reflect the correct ongoing call state.

The ongoing call shown on the home screen, and the main screen of Hotline

The ongoing call shown on the home screen, and the main screen of Hotline

Ending the Call

Answering a call reveals a new problem: there’s currently no way to end a call. The app will support two ways of ending calls: from the native in-call screen, and from within the app.

The diagram below shows what’s going on in both cases:

endcall

Notice the difference between the first step: when the user ends the call from the in-call screen (1a), the system will automatically send a CXEndCallAction to the provider. However, if you want to end a call using Hotline (1b), it’s your job to wrap the action into a transaction, and request it from the system. Once the system processes the request, it will send the CXEndCallAction back to the provider.

No matter which way you want to support ending calls, your app has to implement the necessary CXProviderDelegate method for it to work. Open ProviderDelegate.swift, and add the following implementation to the class extension:

func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
  // 1.
  guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
    action.fail()
    return
  }
  
  // 2.
  stopAudio()
  // 3.
  call.end()
  // 4.
  action.fulfill()
  // 5.
  callManager.remove(call: call)
}

Not that difficult! Here’s what’s going on:

  1. You start by getting a reference to the call from the call manager.
  2. As the call is about to end, it’s time to stop processing the call’s audio.
  3. Invoking end() changes the status of the call, allowing other classes to react to the new state.
  4. At this point, you will mark the action as fulfilled.
  5. Since you no longer need the call, the call manager can dispose of it.

This takes care of the in-call UI. In order to end calls from the app, you’ll need to extend CallManager. With the Call Management group expanded in the project navigator open CallManager.swift.

The call manager will communicate with CXCallController, so it will need a reference to an instance. Add the following property to the CallManager class:

private let callController = CXCallController()

Now add the following methods to the class:

func end(call: Call) {
  // 1.
  let endCallAction = CXEndCallAction(call: call.uuid)
  // 2.
  let transaction = CXTransaction(action: endCallAction)
  
  requestTransaction(transaction)
}

// 3.
private func requestTransaction(_ transaction: CXTransaction) {
  callController.request(transaction) { error in
    if let error = error {
      print("Error requesting transaction: \(error)")
    } else {
      print("Requested transaction successfully")
    }
  }
}

Here’s what’s happening:

  1. You’ll start by creating an “End call” action. You’ll pass in the call’s UUID to the initializer, so it can be identified later.
  2. The next step is to wrap the action into a transaction, so you can send it to the system.
  3. Finally, you’ll invoke request(_:completion:) from the call controller. The system will request the provider to perform this transaction, which will in turn invoke the delegate method you just implemented.

The final step is to hook the action up to the user interface. Open CallsViewController.swift, and write the following call just below the tableView(_:cellForRowAt:) implementation:

override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
  let call = callManager.calls[indexPath.row]
  callManager.end(call: call)
}

When the user invokes swipe-to-delete on a row, the app will ask CallManager to end the corresponding call.

Build and run the project on your device, and perform the following steps:

  1. Tap the plus button in the right-hand corner.
  2. Enter any number, make sure “Incoming” is selected in the segmented control, and tap Done.
  3. Within a few seconds, you’ll get an incoming call. Once you answer, you should see it listed as active on the UI.
  4. Swipe left on the row representing the active call, and tap End.

At this point, your call will end. Neither the lock/home screens, nor the app will report any ongoing calls.

Hanging up now!

Hanging up now!

Other Provider Actions

If you look at the documentation page of CXProviderDelegate, you’ll notice that there are many more actions that the provider can perform, including muting, and grouping, or setting calls on hold. The latter sounds like a good feature for Hotline, so you’ll implement it now.

Whenever the user wants to set the held status of a call, the app will send an instance of CXSetHeldCallAction to the provider. It’s your job to implement the related delegate method. Open ProviderDelegate.swift, and add the following implementation to the class extension:

func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
  guard let call = callManager.callWithUUID(uuid: action.callUUID) else {
    action.fail()
    return
  }
  
  // 1.
  call.state = action.isOnHold ? .held : .active
  
  // 2.
  if call.state == .held {
    stopAudio()
  } else {
    startAudio()
  }
  
  // 3.
  action.fulfill()
}

This is fairly simple:

  1. After getting the reference to the call, you’ll update its status according to the isOnHold property of the action.
  2. Depending on the new status, you’ll want to start, or stop processing the call’s audio.
  3. At this point, you can mark the action fulfilled.

Since this is also a user-initiated action, you’ll need to expand the CallManager class as well. Open CallManager.swift, and add the following implementation just below end(call:):

func setHeld(call: Call, onHold: Bool) {
  let setHeldCallAction = CXSetHeldCallAction(call: call.uuid, onHold: onHold)
  let transaction = CXTransaction()
  transaction.addAction(setHeldCallAction)
  
  requestTransaction(transaction)
}

The code is very similar to the end(call:) method, in fact, the only difference between the two is that this one will wrap an instance of CXSetHeldCallAction into the transaction. The action will contain the call’s UUID, and the held status.

Now it’s time to hook this action up to the UI. Open CallsViewController.swift, and find the class extension marked with UITableViewDelegate at the end of the file. Add the following implementation to the class extension, just below tableView(_:titleForDeleteConfirmationButtonForRowAt:):

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  let call = callManager.calls[indexPath.row]
  call.state = call.state == .held ? .active : .held
  callManager?.setHeld(call: call, onHold: call.state == .held)
  
  tableView.reloadData()
}

Whenever the user taps a row, the code above will update the held status of the corresponding call.

Build and run the application, and start a new incoming call. If you tap the call’s cell, you’ll notice that the status label will change from “Active” to “On Hold”.

IMG_6741

Handling Outgoing Calls

The final user-initiated action you’ll implement will be making outgoing calls. Open ProviderDelegate.swift and add the following implementation to the CXProviderDelegate class extension:

func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
  let call = Call(uuid: action.callUUID, outgoing: true, handle: action.handle.value)
  // 1.
  configureAudioSession()
  // 2.
  call.connectedStateChanged = { [weak self, weak call] in
    guard let strongSelf = self, let call = call else { return }

    if call.connectedState == .pending {
      strongSelf.provider.reportOutgoingCall(with: call.uuid, startedConnectingAt: nil)
    } else if call.connectedState == .complete {
      strongSelf.provider.reportOutgoingCall(with: call.uuid, connectedAt: nil)
    }
  }
  // 3.
  call.start { [weak self, weak call] success in
    guard let strongSelf = self, let call = call else { return }

    if success {
      action.fulfill()
      strongSelf.callManager.add(call: call)
    } else {
      action.fail()
    }
  }
}

The provider will invoke this delegate method when an outgoing call request is made:

  1. After creating a Call with the call’s UUID from the call manager, you’ll have to configure the app’s audio session. Just as with incoming calls, your responsibility at this point is only configuration. The actual processing will start later, when the provider(_:didActivate) delegate method is invoked.
  2. The delegate will monitor the call’s lifecycle. It will initially report that the outgoing call has started connecting. When the call is finally connected, the provider delegate will report that as well.
  3. Calling start() on the call will trigger its lifecycle changes. Upon a successful connection, the call can be marked as fulfilled.

Now that the provider delegate is ready to handle outgoing calls, it’s time to teach the app how to make one. :] Open CallManager.swift, and add the following method to the class:

func startCall(handle: String, videoEnabled: Bool) {
  // 1
  let handle = CXHandle(type: .phoneNumber, value: handle)
  // 2
  let startCallAction = CXStartCallAction(call: UUID(), handle: handle)
  // 3
  startCallAction.isVideo = videoEnabled
  let transaction = CXTransaction(action: startCallAction)
  
  requestTransaction(transaction)
}

This method will wrap a “Start call” action into a CXTransaction, and request it from the system.

  1. A handle, represented by CXHandle can specify the handle type, and its value. Hotline supports phone number handles, so you’ll use it here as well.
  2. CXStartCallAction will receive a unique UUID, and a handle as input.
  3. You can specify whether the call is audio-only, or a video call by setting the isVideo property of the action.

It’s time to hook up the new action to the UI. Open CallsViewController.swift, and replace the previous implementation of unwindForNewCall(_:) to match the following:

@IBAction private func unwindForNewCall(_ segue: UIStoryboardSegue) {
  let newCallController = segue.source as! NewCallViewController
  guard let handle = newCallController.handle else { return }
  let incoming = newCallController.incoming
  let videoEnabled = newCallController.videoEnabled
  
  if incoming {
    let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil)
    DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + 1.5) {
      AppDelegate.shared.displayIncomingCall(uuid: UUID(), handle: handle, hasVideo: videoEnabled) { _ in
        UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier)
      }
    }
  } else {
    callManager.startCall(handle: handle, videoEnabled: videoEnabled)
  }
}

There’s one subtle change in the code: whenever incoming is false, the view controller will ask the call manager to start an outgoing call.

That’s all you’ll need to make calls. It’s time to start testing! Build and run the app on your device. Tap the plus button in the right-hand corner to start a new call, but this time make sure that you select “Outgoing” from the segmented control.

Outgoing

At this point you should see the new call appear in your list. You’ll also see different status labels based on the current stage of the call:

calling2

I can make calls now?!

I can make calls now?!

Managing Multiple Calls

It’s easy to imagine a scenario where a Hotline user would receive multiple calls. You can simulate this by first placing an outgoing call, then an incoming call and pressing the Home button before the incoming call comes in. At this point, the app presents the user with the following screen:

IMG_6753

The system will let the user decide how to resolve the issue. Based on the choice, it will wrap up multiple actions into a CXTransaction. For example if the user chooses to end the ongoing call, and answer the new one, the system will create a CXEndCallAction for the former and a CXStartCallAction for the latter. Both actions will be wrapped into a transaction and sent to the provider, which will process them individually. So if your app already knows how to fulfill the individual requests, there’s no further action required!

Implementing features without additional code!

Implementing features without additional code!

You can test it by resolving the scenario above; the list of calls will reflect your choice. The app will only process one audio session at a time. If you choose to resume a call, the other will be put on hold automatically.

multicall

Creating a Call Directory Extension

The directory extension is a new extension point offered by CallKit. It allows your VoIP app to:

  • Add phone numbers to the system’s block list.
  • Identify incoming calls by their phone number or other uniquely identifying information, such as email address.

Whenever the system receives a call, it will check the address book for a match; if it doesn’t find one, it can also check in app-specific directory extensions. Why not add a directory extension to Hotline?

Back in Xcode, go to File\New\Target… and choose Call Directory Extension. Name it HotlineDirectory, and click Finish. Xcode will automatically create a new file, CallDirectoryHandler.swift. Locate it in the project navigator, and check what’s inside.

The first method you’ll find is beginRequest(with:). This method will be invoked when your extension is initialized. In case of any errors, the extension will tell the host app to cancel the extension request by invoking cancelRequest(withError:). It relies on two other methods to build the app-specific directory.

addBlockingPhoneNumber(to:) will collect all the phone numbers, which should be blocked. Replace its implementation with the following:

private func addBlockingPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws {
  let phoneNumbers: [CXCallDirectoryPhoneNumber] = [ 1234 ]
  for phoneNumber in phoneNumbers {
    context.addBlockingEntry(withNextSequentialPhoneNumber: phoneNumber)
  }
}

Invoking addBlockingEntry(withNextSequentialPhoneNumber:) with a given phone number will add it to the block list. When a number is blocked, the system telephony provider will not display any calls from that number.

Now take a look at addIdentificationPhoneNumbers(to:). Replace the method body with the code below:

private func addIdentificationPhoneNumbers(to context: CXCallDirectoryExtensionContext) throws {
  let phoneNumbers: [CXCallDirectoryPhoneNumber] = [ 1111 ]
  let labels = [ "RW Tutorial Team" ]
  
  for (phoneNumber, label) in zip(phoneNumbers, labels) {
    context.addIdentificationEntry(withNextSequentialPhoneNumber: phoneNumber, label: label)
  }
}

Invoking addIdentificationEntry(withNextSequentialPhoneNumber:label:) with a specified phone number and label will create a new identification entry. Whenever the system receives a call from this number, the call UI will display the matching label to the user.

It’s time to test your new extension. Build and run the Hotline scheme on your device. At this point your extension may not yet be active. To enable it, do the following steps:

  1. Go to the Settings app
  2. Select Phone
  3. Select Call Blocking & Identification
  4. Enable Hotline
Note: If you’re having trouble getting the system to recognize or use your extension, try killing the app and relaunching it. Sometimes iOS needs a little extra help to use your extension.

Allowing the extension

Testing a blocked call is easy: just launch Hotline, and simulate an incoming call from the number 1234. You’ll notice that the system doesn’t report anything. In fact, if you put a breakpoint in the implementation of reportIncomingCall(uuid:handle:hasVideo:completion:) in ProviderDelegate, you’ll notice that reportNewIncomingCall(withupdate:completion:) will even report an error.

To test identifying calls, launch Hotline again, and simulate a new call; but this time, enter the number 1111. You’ll be presented with the following call UI:

FYI: that’s not a real number :]

FYI: that’s not a real number :]

This app is awesome!

This app is awesome!

Congratulations! You’ve created an app which leverages CallKit to provide a first-party VoIP experience! :]

Where to Go From Here?

You can download the completed project for this tutorial here.

If you wish to learn more about CallKit, check out Session 230 from WWDC 2016.

I hope you enjoyed this CallKit tutorial. If you have any questions or comments, please join the forum discussion below!

Contributors

Comments

번호
제목
글쓴이
101 Objective-c JSON Parsing 제이슨 파싱 Dictionary Array 구분 기호
admin
2019-06-06 6835
100 사용자 정의 컨테이너보기 컨트롤러 전환 xcode objective c
admin
2019-05-25 6806
99 Xcode 글꼴 크기 변경
admin
2019-05-25 9178
98 실습 Objective C Code 연습 실행 온라인 무료 사이트 회원가입 불필요 광고 무
admin
2019-05-22 6471
97 초보) delegate 는 왜필요한가 이것만알면 간단 쉽게 이해 objective C 날로먹기가능
admin
2019-05-22 6373
96 What is a Composite Object? objective c 강의 사이트 아주 훌륭한 유익한 좋은
admin
2019-05-22 11945
95 Segue를 통한 뷰 컨트롤러 전환과 데이터 교환 방법. Delegate 가장 정확하게 설명 쉽게
admin
2019-05-21 6437
94 hide/show text fields with auto layouts and programmatically objective c
admin
2019-05-19 7218
93 Text field 숨기기
admin
2019-05-19 6429
92 Objective-C (전역) 상수 선언 및 사용
admin
2019-05-18 7759
91 키체인 uuid keychaine 설명 및 사용
admin
2019-04-29 6490
90 개발자 등록 인증서 다운로드 푸시 인증요청서 등록 인증서 다운
admin
2019-04-27 7277
89 iOS Application 개발 시작하기 개발자 등록, 등록요청서 , 인증서 다운
admin
2019-04-25 7437
88 iOS Application 개발 시작하기 프로비저닝 생성, XCode 프로젝트 설정
admin
2019-04-25 7645
87 Easy APNs Provider 사용방법 iOS Push발송 테스트
admin
2019-04-23 9403
86 IOS Push php
admin
2019-04-20 6650
85 How to rename an XCode project
admin
2019-04-20 6892
84 Push Notifications and Local Notifications – Tutorial
admin
2019-04-19 7354
83 Push Notifications and Local Notifications (Xcode 9, iOS 11)
admin
2019-04-19 7276
82 A macOS, Linux, Windows app to test push notifications on iOS and Android
admin
2019-04-19 6417
81 Push Notifications by Tutorials iOS developers
admin
2019-04-19 7681
80 Sample app in Swift Objective C receive VoIP push notifications 푸시 테스트 앱
admin
2019-04-19 7933
79 How to create Apple Certificates - Step 1: CSR file creation CSR 생성
admin
2019-04-19 7092
78 Voice Over IP (VoIP) Best Practices Xcode
admin
2019-04-19 10530
77 ios 개발자 계정 만들기 지불 방법 핵심 키포인트 인증서요청 앱등록 앱출시 요점만 쉽게 설명
admin
2019-04-17 6705
76 HybridApp Cordova 코르도바 란
admin
2019-04-15 7730
75 Linphone rebuild
admin
2019-04-09 6513
74 How to rename Xcode project
admin
2019-04-08 7080
73 Part 2: Implementing PushBots SDK for your iOS App
admin
2019-04-08 7148
72 Part 1: Preparing Certificates + Provisioning Profile (iOS)
admin
2019-04-08 6989
71 ios push notification pushkit xcode swift
admin
2019-04-08 6646
70 Xcode 9, Swift 4 Remote Push Notification Setup W/ Firebase
admin
2019-04-08 7386
69 VoIP Push Notifications using iOS Pushkit
admin
2019-04-08 7785
68 UILocalNotification is deprecated in iOS10
admin
2019-04-08 7206
67 아이폰 UserNotifications 사용법
admin
2019-04-08 7276
66 아이폰 VoIP Services Certificate 생성 방법 - 서비스 인증서 생성하는 방법
admin
2019-04-08 7148
65 [아이폰] PushKit 앱 개발 방법
admin
2019-04-08 6908
64 PushKit이란 ? 푸시 알람을 앱에 보내는 것
admin
2019-04-08 10052
63 OSX 키체인접근 p12 파일 인증서, 개인키를 PEM 파일로 openssl
admin
2019-04-08 7322
62 APNs 애플 푸시 앱 개발 push 키 등록 만들기 가장 잘된 동영상 설명
admin
2019-04-08 8318
61 푸시 메시지를 활성화하기 위한 전제 조건 사전작업
admin
2019-04-06 6488
60 푸시 메시지 구성
admin
2019-04-06 6523
59 APNS 또는 GCM 사용을 위한 앱 구성 방법 순서
admin
2019-04-06 7254
58 iOS 인증서 및 프로비저닝 프로파일 만들기
admin
2019-04-04 11350
57 Mac용 키체인 접근이란 무엇입니까?
admin
2019-04-04 7531
56 Apple Push Notification Service 시작하기 AWS 샘플앱 이용 푸시 테스트
admin
2019-04-04 7437
55 iOS Notification 만들기 push 푸시
admin
2019-04-04 9478
54 Mac OS X에서 Java(JDK) 설치 및 삭제하기
admin
2019-04-03 6715
53 iOS12 Chat, Learn Swift 4.2, build iOS 12 Chat Application 스터디
admin
2019-04-03 7088
52 IOS 앱이 시스템 수준 전화 통합을 위해 CallKit을 사용하는 방법과 통화
admin
2019-04-02 12289
CallKit Tutorial for iOS 콜킷 사용
admin
2019-04-02 12096
50 Enhancing VoIP Apps with CallKit - Apple WWDC 2016 콜킷
admin
2019-04-02 6997
49 iOS Beginner : Messaging & Phone call (Swift 4 & Xcode 9)
admin
2019-04-01 7205
48 맥 개발 환경 설정하기
admin
2019-04-01 7395
47 Homebrew 설치 (MacOSX) 동영상
admin
2019-04-01 6942
46 개발자를 위한 맥(Mac) 정보 - 패키지관리자 Homebrew
admin
2019-04-01 7021
45 installing MacPorts on macOS Sierra (plus installing ffmpeg)
admin
2019-04-01 6992
44 MacPort 의 소개와 간단 사용법 - OSX 환경 세팅하기
admin
2019-04-01 7011
43 MacPorts 설치 및 사용법
admin
2019-04-01 7315
42 MacPorts to use system for compiling, installing, and managing open source software. file
admin
2019-04-01 8041
41 애플앱개발 앱스토어등록 개발자사이트등록 앱튠스콘넥트사이트등록
admin
2019-03-24 7075
40 애플 개발자 사이트 apple 앱스토어에 앱을 등록하는 순서
admin
2019-03-24 7796
39 맥 모하비 설치 VM Vmware Workstation Pro 15 설치 ebay 싸게구매
admin
2019-03-23 7480
38 포인터에 익숙해지기 포인터 정리
admin
2019-01-20 6828
37 푸시 알림을 수신하려면 응용 프로그램이 Apple 푸시 알림 서비스 APN
admin
2019-01-10 7567
36 아이폰 카톡 푸시 알림이 안올때! 상세안내 체크
admin
2019-01-10 27700
35 매우 천천히 반응하는 Apple iPhone XS Max 터치 스크린을 수정하는 방법, 터치 스크린 응답 지연됨 문제 해결 가이드
admin
2018-11-09 8112
34 Apple iPhone XS Max 셀룰러 데이터가 작동하지 않는 문제 해결 가이드
admin
2018-11-09 11707
33 Apple iPhone XS Max에서 지속적으로 충돌하는 응용 프로그램을 수정하는 방법 문제 해결 가이드
admin
2018-11-09 7792
32 Apple iPhone XS Max 작동하지 않는 얼굴 ID를 수정하는 방법 문제 해결 가이드
admin
2018-11-09 9123
31 작동하지 않는 Apple iPhone XS Max 마이크를 해결 방법 문제 해결 가이드
admin
2018-11-09 8152
30 iPhone XS Max를 문제 해결, iPhone 과열 문제 해결 [문제 해결 가이드]
admin
2018-11-09 8517
29 Apple iPhone XS Max 배터리가 빨리 소모되면 어떻게해야합니까? 문제 해결 가이드 및 배터리 절약 팁
admin
2018-11-09 8702
28 iPhone XS Max 시스템 가이드 소프트 강제 재시작 모든 설정 네트워크 설정 초기화
admin
2018-11-08 11354
27 IPAD IPHONE X 아이패드 아이폰 전문가 리뷰 트러블슈팅 문제해결
admin
2018-11-08 7014
26 IPad Air와 iPad Pro의 차이점
admin
2018-11-08 7610
25 IPHONE 아이폰 푸쉬 노티피케이션 동작 안되는 현상 해결방법
admin
2018-11-08 7501
24 IOS12 업데이트 후 작동하지 않는 Apple iPhone X 알림 수정 방법
admin
2018-11-08 8068
23 아이폰 푸시 노티피케이션 온/오프
admin
2018-11-08 7362
22 Objective-C 입문
admin
2018-06-01 8071
21 프로토콜
admin
2018-06-01 7637
20 카테고리
admin
2018-06-01 7555
19 메소드의 포인터
admin
2018-06-01 7906
18 선택기
admin
2018-06-01 7512
17 클래스 형
admin
2018-06-01 7517
16 클래스 메소드
admin
2018-06-01 7549
15 가시성
admin
2018-06-01 7523
14 정적 형식
admin
2018-06-01 7418
13 오브젝트의 해방
admin
2018-06-01 16000
12 이니셜 라이저
admin
2018-06-01 7703
11 재정
admin
2018-06-01 12477
10 상속
admin
2018-06-01 7634
9 메소드
admin
2018-06-01 7898
8 클래스의 선언과 정의
admin
2018-06-01 8209
7 가져 오기
admin
2018-06-01 8037
6 Objective-C는?
admin
2018-06-01 8052
5 [프로그래밍]아이폰 SDK와 Xcode 통합 개발환경
admin
2018-06-01 8246
4 [프로그래밍]Objective-C 셀렉터(@selector)
admin
2018-06-01 8115
3 VMWare를 이용한 mac os를 설치방법
admin
2018-05-11 9383
2 mac OS 여러가지 맥 버젼 별 설명 구분 시에라 까지
admin
2018-05-11 15986