15 | Congratulations you just started a new Twilio 16 | Serverless project. 17 |
18 | 19 |Assets
20 |21 | Assets are static files, like HTML, CSS, JavaScript, images or audio 22 | files. 23 |
24 | 25 |26 | This HTML page is an example of a public asset, you can 27 | access this by loading it in the browser. The HTML also refers to 28 | another public asset for CSS styles. 29 |
30 |
31 | You can also have private assets, there is an example
32 | private asset called message.private.js in the
33 | /assets directory. This file cannot be loaded in the
34 | browser, but you can load it as part of a function by finding its path
35 | using Runtime.getAssets() and then requiring the file.
36 | There is an example of this in
37 | /functions/private-message.js.
38 |
Functions
41 |42 | Functions are JavaScript files that will respond to incoming HTTP 43 | requests. There are public and 44 | protected functions. 45 |
46 | 47 |
48 | Public functions respond to all HTTP requests. There is
49 | an example of a public function in
50 | /functions/hello-world.js.
51 |
54 | Protected functions will only respond to HTTP requests
55 | with a valid Twilio signature in the header. You can read more about
56 | validating requests from Twilio in the documentation. There is an example of a protected function in
59 | /functions/sms/reply.protected.js
60 |
twilio-run
63 | 64 |
65 | Functions and assets are served, deployed and debugged using
66 | twilio-run. You can serve the project locally with the command
69 | npm start which is really running
70 | twilio-run --env under the hood. If you want to see what
71 | else you can do with twilio-run enter
72 | npx twilio-run --help on the command line or check out
73 | the project documentation on GitHub.
76 |
13 |
14 | Please note, ExampleAVAudioEngineDevice requires iOS 11.0 or above.
15 |
16 | ### Setup
17 |
18 | See the master [README](https://github.com/twilio/voice-quickstart-ios/blob/master/README.md) for instructions on how to generate access tokens and make an outbound Call.
19 |
20 | This example requires Xcode 11.0 and the iOS 12.0 SDK, as well as a device running iOS 11.0 or above.
21 |
22 | ### Running
23 |
24 | Once you have configured your access token, build and run the example. Use the text field to make an outgoing call:
25 |
26 |
27 |
28 | Once the Call is connected, tap the "Play Music" button and let ExampleAVAudioEngineDevice mix some tunes into your track.
29 |
30 |
31 |
32 | ### Known Issues
33 |
34 | The AVAudioSession is configured and activated at device initialization time. Ideally, it would be better to activate the AVAudioSession only when audio playback or recording is needed.
--------------------------------------------------------------------------------
/Docs/verified-caller-number.md:
--------------------------------------------------------------------------------
1 | # Display verified caller number using TVOCallerInfo
2 |
3 | The [TVOCallerInfo](https://twilio.github.io/twilio-voice-ios/docs/latest/Classes/TVOCallerInfo.html) provides information about the caller. The `verified` property represents whether or not the caller's phone number has been verified by Twilio using **SHAKEN/STIR** validation.
4 |
5 | Use the information and display in the CallKit incoming call UI upon receiving the call invite
6 |
7 | ```.swift
8 | func callInviteReceived(_ callInvite: TVOCallInvite) {
9 | var callKitProviderName = "Voice Quickstart\n"
10 | let callerInfo: TVOCallerInfo = callInvite.callerInfo
11 | if let verified: NSNumber = callerInfo.verified {
12 | if verified.boolValue {
13 | callKitProviderName = "✅ Caller Verified\n"
14 | }
15 | }
16 |
17 | let configuration = CXProviderConfiguration(localizedName: callKitProviderName)
18 | configuration.maximumCallGroups = 1
19 | configuration.maximumCallsPerCallGroup = 1
20 |
21 | callKitProvider = CXProvider(configuration: configuration)
22 | callKitProvider.setDelegate(self, queue: nil)
23 |
24 | // Report to CallKit
25 | let callHandle = CXHandle(type: .generic, value: callInvite.from)
26 | let callUpdate = CXCallUpdate()
27 | callUpdate.remoteHandle = callHandle
28 |
29 | callKitProvider.reportNewIncomingCall(with: uuid, update: callUpdate) { error in
30 | if let error = error {
31 | NSLog("Failed to report incoming call successfully: \(error.localizedDescription).")
32 | } else {
33 | NSLog("Incoming call successfully reported.")
34 | }
35 | }
36 | }
37 | ```
38 |
39 | * Please note that the designated initializer `[CXProviderConfiguration initWithLocalizedName:]` has been deprecated on iOS 14.
40 |
41 | If the number of the caller is verified
42 |
43 |
44 |
45 | If the number is not validated at **A** level or there is no verification information
46 |
47 |
48 |
--------------------------------------------------------------------------------
/Docs/managing-push-credentials.md:
--------------------------------------------------------------------------------
1 | ## Managing Push Credentials
2 |
3 | A push credential is a record for a push notification channel, for iOS this push credential is a push notification channel record for APNS VoIP. Push credentials are managed in the console under [Mobile Push Credentials](https://www.twilio.com/console/voice/sdks/credentials).
4 |
5 | Whenever a registration is performed via `TwilioVoice.registerWithAccessToken:deviceTokenData:completion` in the iOS SDK the `identity` and the `Push Credential SID` provided in the JWT based access token, along with the `device token` are used as a unique address to send APNS VoIP push notifications to this application instance whenever a call is made to reach that `identity`. Using `TwilioVoice.unregisterWithAccessToken:deviceTokenData:completion` removes the association for that `identity`.
6 |
7 | ### Updating a Push Credential
8 |
9 | If you need to change or update your credentials provided by Apple you can do so by selecting the Push Credential in the [console](https://www.twilio.com/console/voice/sdks/credentials) and adding your new `certificate` and `private key` in the text box provided on the Push Credential page shown below:
10 |
11 |
12 |
13 | ### Deleting a Push Credential
14 |
15 | We **do not recommend that you delete a Push Credential** unless the application that it was created for is no longer being used.
16 |
17 | If **your APNS VoIP certificate is expiring soon or has expired you should not delete your Push Credential**, instead you should update the Push Credential by following the `Updating a Push Credential` section.
18 |
19 | When a Push Credential is deleted **any associated registrations made with this Push Credential will be deleted**. Future attempts to reach an `identity` that was registered using the Push Credential SID of this deleted push credential **will fail**.
20 |
21 | If you are certain you want to delete a Push Credential you can click on `Delete this Credential` on the [console](https://www.twilio.com/console/voice/sdks/credentials) page of the selected Push Credential.
22 |
23 | Please ensure that after deleting the Push Credential you remove or replace the Push Credential SID when generating new access tokens.
--------------------------------------------------------------------------------
/Docs/call-from-history.md:
--------------------------------------------------------------------------------
1 | ## Making Calls from Call History
2 |
3 | The document show a quick example of how to make outgoing calls from recent call history or from contacts.
4 |
5 | ### Steps
6 |
7 | #### 1. Specify supported handle types when initializing the `CXProvider` object. User activity callback will not be triggered if this is not set.
8 |
9 | ```.swift
10 | let configuration = CXProviderConfiguration(localizedName: "Voice Quickstart")
11 | configuration.maximumCallGroups = 1
12 | configuration.maximumCallsPerCallGroup = 1
13 |
14 | // Specify supported handle types so the app gets user activity callback when a call is made from call history
15 | configuration.supportedHandleTypes = [.generic, .phoneNumber]
16 |
17 | if let provider = CXProvider(configuration: configuration) {
18 | provider.setDelegate(self, queue: nil)
19 | }
20 |
21 | ```
22 |
23 | ```.objc
24 | CXProviderConfiguration *configuration = [[CXProviderConfiguration alloc] initWithLocalizedName:@"Voice"];
25 | configuration.maximumCallGroups = 1;
26 | configuration.maximumCallsPerCallGroup = 1;
27 |
28 | // Specify supported handle types so the app gets user activity callback when a call is made from call history
29 | configuration.supportedHandleTypes = [NSSet setWithArray:@[@(CXHandleTypeGeneric), @(CXHandleTypePhoneNumber)]];
30 |
31 | self.callKitProvider = [[CXProvider alloc] initWithConfiguration:configuration];
32 | [self.callKitProvider setDelegate:self queue:nil];
33 |
34 | ```
35 |
36 | #### 2. Implement the `[UIApplication continueUserActivity:restorationHandler:]` delegate method and use the `INStartAudioCallIntent` to start a call.
37 |
38 | ```.swift
39 | func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool {
40 | if let callIntent = userActivity.interaction?.intent as? INStartAudioCallIntent,
41 | let contact = callIntent.contacts?[0] {
42 | guard let handle = contact.personHandle?.value else { return false }
43 | // Start a new call with CallKit
44 | makeCall(handle)
45 | }
46 |
47 | return true
48 | }
49 | ```
50 |
51 | ```.objc
52 | - (BOOL)application:(UIApplication *)application
53 | continueUserActivity:(NSUserActivity *)userActivity
54 | restorationHandler:(void(^)(NSArray
64 |
65 | This app requires the [Serverless plug-in](https://github.com/twilio-labs/plugin-serverless). Install the CLI plugin with:
66 |
67 | $ twilio plugins:install @twilio-labs/plugin-serverless
68 |
69 | Before deploying, create a `Server/.env` by copying from `Server/.env.example`
70 |
71 | $ cp Server/.env.example Server/.env
72 |
73 | Update `Server/.env` with your Account SID, auth token, API Key and secret
74 |
75 | ACCOUNT_SID=ACxxxx
76 | AUTH_TOKEN=xxxxxx
77 | API_KEY_SID=SKxxxx
78 | API_SECRET=xxxxxx
79 | APP_SID=APxxxx (available in step 3)
80 | PUSH_CREDENTIAL_SID=CRxxxx (available in step 6)
81 |
82 | The `Server` folder contains a basic server component which can be used to vend access tokens or generate TwiML response for making call to a number or another client. The app is deployed to Twilio Serverless with the `serverless` plug-in:
83 |
84 | $ cd Server
85 | $ twilio serverless:deploy
86 |
87 | The server component that's baked into this quickstart is in Node.js. If you’d like to roll your own or better understand the Twilio Voice server side implementations, please see the list of starter projects in the following supported languages below:
88 |
89 | * [voice-quickstart-server-java](https://github.com/twilio/voice-quickstart-server-java)
90 | * [voice-quickstart-server-node](https://github.com/twilio/voice-quickstart-server-node)
91 | * [voice-quickstart-server-php](https://github.com/twilio/voice-quickstart-server-php)
92 | * [voice-quickstart-server-python](https://github.com/twilio/voice-quickstart-server-python)
93 |
94 | Follow the instructions in the project's README to get the application server up and running locally and accessible via the public Internet.
95 |
96 | ### 3. Create a TwiML application for the Access Token
97 |
98 | Next, we need to create a TwiML application. A TwiML application identifies a public URL for retrieving [TwiML call control instructions](https://www.twilio.com/docs/voice/twiml). When your iOS app makes a call to the Twilio cloud, Twilio will make a webhook request to this URL, your application server will respond with generated TwiML, and Twilio will execute the instructions you’ve provided.
99 |
100 | Use Twilio CLI to create a TwiML app with the `make-call` endpoint you have just deployed (**Note: replace the value of `--voice-url` parameter with your `make-call` endpoint you just deployed to Twilio Serverless**)
101 |
102 | $ twilio api:core:applications:create \
103 | --friendly-name=my-twiml-app \
104 | --voice-method=POST \
105 | --voice-url="https://my-quickstart-dev.twil.io/make-call"
106 |
107 | You should receive an Appliciation SID that looks like this
108 |
109 | APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
110 |
111 | ### 4. Generate an access token for the quickstart
112 |
113 | Install the `token` plug-in
114 |
115 | $ twilio plugins:install @twilio-labs/plugin-token
116 |
117 | Use the TwiML App SID you just created to generate an access token
118 |
119 | $ twilio token:voice --identity=alice --voice-app-sid=APxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
120 |
121 | Copy the access token string. Your iOS app will use this token to connect to Twilio.
122 |
123 | ### 5. Run the Swift Quickstart app
124 |
125 | Now let’s go back to the `VoiceQuickstart.xcworkspace`. Update the placeholder of `accessToken` with access token string you just copied
126 |
127 | ```swift
128 | import UIKit
129 | import AVFoundation
130 | import PushKit
131 | import CallKit
132 | import TwilioVoice
133 |
134 | let accessToken = "PASTE_YOUR_ACCESS_TOKEN_HERE"
135 | let twimlParamTo = "to"
136 |
137 | let kCachedDeviceToken = "CachedDeviceToken"
138 |
139 | class ViewController: UIViewController {
140 | ...
141 | }
142 | ```
143 |
144 | Build and run the app. Leave the text field empty and press the call button to start a call. You will hear the congratulatory message. Support for dialing another client or number is described in steps 8 and 9. Tap "Hang Up" to disconnect.
145 |
146 |
147 |
148 | ### 6. Create a Push Credential with your VoIP Service Certificate
149 |
150 | The Programmable Voice SDK uses Apple’s VoIP Services to let your application know when it is receiving an incoming call. If you want your users to receive incoming calls, you’ll need to enable VoIP Services in your application and generate a VoIP Services Certificate.
151 |
152 | Go to [Apple Developer portal](https://developer.apple.com/) and generate a VoIP Service Certificate.
153 |
154 | Once you have generated the VoIP Services Certificate, you will need to provide the certificate and key to Twilio so that Twilio can send push notifications to your app on your behalf.
155 |
156 | Export your VoIP Service Certificate as a `.p12` file from *Keychain Access* and extract the certificate and private key from the `.p12` file using the `openssl` command.
157 |
158 | $ openssl pkcs12 -in PATH_TO_YOUR_P12 -nokeys -out cert.pem -nodes
159 | $ openssl x509 -in cert.pem -out cert.pem
160 | $ openssl pkcs12 -in PATH_TO_YOUR_P12 -nocerts -out key.pem -nodes
161 | $ openssl rsa -in key.pem -out key.pem
162 |
163 | **Note: if you run into the unsupported encryption algorithm (RC2-40-CBC) issue like below, try adding `-legacy` to the `openssl pkcs12` command.**
164 |
165 | ```
166 | Error outputting keys and certificates
167 | C0EF094DF87F0000:error:0308010C:digital envelope routines:inner_evp_generic_fetch:unsupported:crypto/evp/evp_fetch.c:355:Global default library context, Algorithm (RC2-40-CBC : 0), Properties ()
168 | ```
169 |
170 | Use Twilio CLI to create a Push Credential using the cert and key.
171 |
172 | $ twilio api:chat:v2:credentials:create \
173 | --type=apn \
174 | --sandbox \
175 | --friendly-name="voice-push-credential (sandbox)" \
176 | --certificate="$(cat PATH_TO_CERT_PEM)" \
177 | --private-key="$(cat PATH_TO_KEY_PEM)"
178 |
179 | This will return a Push Credential SID that looks like this
180 |
181 | CRxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
182 |
183 | The `--sandbox` option tells Twilio to send the notification requests to the sandbox endpoint of Apple's APNS service. Once the app is ready for distribution or store submission, create a separate Push Credential with a new VoIP Service certificate **without** the `--sandbox` option.
184 |
185 | **Note: we strongly recommend using different Twilio accounts (or subaccounts) to separate VoIP push notification requests for development and production apps.**
186 |
187 | Now let's generate another access token and add the Push Credential to the Voice Grant.
188 |
189 | $ twilio token:voice \
190 | --identity=alice \
191 | --voice-app-sid=APxxxx \
192 | --push-credential-sid=CRxxxxs
193 |
194 | ### 7. Receive an incoming call
195 |
196 | You are now ready to receive incoming calls. Update your app with the access token generated from step 6 and rebuild your app. The `TwilioVoiceSDK.register()` method will register your mobile client with the PushKit device token as well as the access token. Once registered, hit your application server's **/place-call** endpoint: `https://my-quickstart-dev.twil.io/place-call?to=alice`. This will trigger a Twilio REST API request that will make an inbound call to the identity registered on your mobile app. Once your app accepts the call, you should hear a congratulatory message.
197 |
198 | Register your mobile client with the PushKit device token:
199 |
200 | ```.swift
201 | TwilioVoiceSDK.register(accessToken: accessToken, deviceToken: cachedDeviceToken) { error in
202 | if let error = error {
203 | NSLog("An error occurred while registering: \(error.localizedDescription)")
204 | } else {
205 | NSLog("Successfully registered for VoIP push notifications.")
206 | }
207 | }
208 | ```
209 |
210 | Please note that your application must have `voip` enabled in the `UIBackgroundModes` of your app's plist in order to be able to receive push notifications.
211 |
212 |
213 |
214 | ### 8. Make client to client call
215 |
216 | To make client to client calls, you need the application running on two devices. To run the application on an additional device, make sure you use a different identity in your access token when registering the new device.
217 |
218 | Use the text field to specify the identity of the call receiver, then tap the "Call" button to make a call. The TwiML parameters used in `TwilioVoice.connect()` method should match the name used in the server.
219 |
220 |
221 |
222 | ### 9. Make client to PSTN call
223 |
224 | To make client to number calls, first get a verified Twilio number to your account via https://www.twilio.com/console/phone-numbers/verified. Update your server code and replace the `callerNumber` variable with the verified number. Restart the server so it uses the new value.
225 |
226 |
227 |
228 | ## Examples
229 |
230 | You will also find additional examples that provide more advanced use cases of the Voice SDK:
231 |
232 | - [AudioDevice](https://github.com/twilio/voice-quickstart-ios/tree/master/AudioDeviceExample) - Provide your own means to playback and record audio using a custom `TVOAudioDevice` and [CoreAudio](https://developer.apple.com/documentation/coreaudio).
233 | - [Making calls from history](https://github.com/twilio/voice-quickstart-ios/blob/master/Docs/call-from-history.md) - Use the `INStartAudioCallIntent` in the user activity delegate method to start a call from the history.
234 |
235 | ## More Documentation
236 |
237 | You can find the API documentation of the Voice SDK:
238 |
239 | * [TwilioVoice SDK API Doc](https://twilio.github.io/twilio-voice-ios/docs/latest/)
240 |
241 | ## Twilio Helper Libraries
242 |
243 | To learn more about how to use TwiML and the Programmable Voice Calls API, check out our TwiML quickstarts:
244 |
245 | * [TwiML Quickstart for Python](https://www.twilio.com/docs/voice/quickstart/python)
246 | * [TwiML Quickstart for Ruby](https://www.twilio.com/docs/voice/quickstart/ruby)
247 | * [TwiML Quickstart for PHP](https://www.twilio.com/docs/voice/quickstart/php)
248 | * [TwiML Quickstart for Java](https://www.twilio.com/docs/voice/quickstart/java)
249 | * [TwiML Quickstart for C#](https://www.twilio.com/docs/voice/quickstart/csharp)
250 |
251 | ## Issues and Support
252 |
253 | Please file any issues you find here on Github: [Voice Swift Quickstart](https://github.com/twilio/voice-quickstart-ios).
254 | Please ensure that you are not sharing any
255 | [Personally Identifiable Information(PII)](https://www.twilio.com/docs/glossary/what-is-personally-identifiable-information-pii)
256 | or sensitive account information (API keys, credentials, etc.) when reporting an issue.
257 |
258 | For general inquiries related to the Voice SDK you can [file a support ticket](https://support.twilio.com/hc/en-us/requests/new).
259 |
260 | ## License
261 |
262 | MIT
263 |
--------------------------------------------------------------------------------
/SwiftVoiceQuickstart.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 24BA34371D874AC800B0B4F7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24BA34361D874AC800B0B4F7 /* AppDelegate.swift */; };
11 | 24BA34391D874AC800B0B4F7 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24BA34381D874AC800B0B4F7 /* ViewController.swift */; };
12 | 24BA343C1D874AC800B0B4F7 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 24BA343A1D874AC800B0B4F7 /* Main.storyboard */; };
13 | 24BA343E1D874AC800B0B4F7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 24BA343D1D874AC800B0B4F7 /* Assets.xcassets */; };
14 | 24BA34411D874AC800B0B4F7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 24BA343F1D874AC800B0B4F7 /* LaunchScreen.storyboard */; };
15 | 24BA34491D874BBD00B0B4F7 /* TwilioLogo.png in Resources */ = {isa = PBXBuildFile; fileRef = 24BA34481D874BBD00B0B4F7 /* TwilioLogo.png */; };
16 | 8F56E8D625C8B25E00B5AB19 /* TwilioVoice in Frameworks */ = {isa = PBXBuildFile; productRef = 8F56E8D525C8B25E00B5AB19 /* TwilioVoice */; };
17 | 8F6BABF92399B2F00030BE4F /* ringtone.wav in Resources */ = {isa = PBXBuildFile; fileRef = 8F6BABF82399B2F00030BE4F /* ringtone.wav */; };
18 | /* End PBXBuildFile section */
19 |
20 | /* Begin PBXFileReference section */
21 | 24BA34331D874AC800B0B4F7 /* SwiftVoiceQuickstart.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftVoiceQuickstart.app; sourceTree = BUILT_PRODUCTS_DIR; };
22 | 24BA34361D874AC800B0B4F7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "