Skip to content

Commit 2ce3316

Browse files
authored
VOICEC-1469: iOS voice refactor (#58)
* Update iOS sample voice app to pass TestFlight validation * Wip * Use state flows * Fix android chat app * iOS Voice refactoring: SwiftUI migration * restore android stuff * kinda working app * Handle push correctly * memory leak? * Support running on simulator (with no CallKit) * Improve login * Clean up app + updated README * Modularize VoiceClientManager * Fix dialer icon * Fix buttons + navigations + persist properties * Add missing icon + logo * fix compile issue on real device - Change client visibility - Fix CXCallController init * Fix session restoration on push * Update bundle id * Use @mainactor instead of DispatchQueue.main.async * Fix Memory Leak * Simplified call view * Minimize tasks on MainActor * Improved push tokens management - Delete the previous device token on a new login * Fix CallKit - Fixed flow - Both UI and phone UI will call the same action on the client manager - If runs on device, UI action will be handled via transactions with CallKit - Was not reporting call as active (outbound flow) - Was calling push completion before reporting the call to callKit (inbound flow) * Skip audio session config on Device * Delete setupAudioSession entirely - not needed on simulator either * Removed setting isUsingCallKit - SDK is handling that internally * Use Snapshot version * Update snapshot * Run CallKit completion handlers first * Add API_KEY for custom backend * Error notification + Handle Session error * Bump to 2.2.0-alpha.1 * General fixes for real device Fixed text fields Mute state and DTMF on CallKit Add AudioRoutePicker * Passing TestFlight validation * Increment build number * Fix notifications on release build
1 parent a54c80e commit 2ce3316

File tree

86 files changed

+3417
-2602
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3417
-2602
lines changed

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"java.configuration.updateBuildConfiguration": "automatic"
3+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.example.vonage.voicesampleapp.ui.theme
2+
3+
import androidx.compose.ui.graphics.Color
4+
5+
// Primary colors from original theme
6+
val Purple200 = Color(0xFFBB86FC)
7+
val Purple500 = Color(0xFF6200EE)
8+
val Purple700 = Color(0xFF3700B3)
9+
val Teal200 = Color(0xFF03DAC5)
10+
val Teal700 = Color(0xFF018786)
11+
12+
// Custom colors from original theme
13+
val Red = Color(0xFFC23B22)
14+
val Green = Color(0xFF0EBE2C)
15+
val Gray = Color(0xFF808080)
16+
val Black = Color(0xFF000000)
17+
val White = Color(0xFFFFFFFF)
18+
19+
// Material 3 color scheme colors
20+
val Primary = Purple500
21+
val OnPrimary = White
22+
val PrimaryContainer = Purple700
23+
val OnPrimaryContainer = White
24+
25+
val Secondary = Teal700
26+
val OnSecondary = White
27+
val SecondaryContainer = Teal200
28+
val OnSecondaryContainer = Black
29+
30+
val Background = White
31+
val OnBackground = Black
32+
val Surface = White
33+
val OnSurface = Black
34+
35+
val Error = Red
36+
val OnError = White
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.example.vonage.voicesampleapp.ui.theme
2+
3+
import android.os.Build
4+
import androidx.compose.foundation.isSystemInDarkTheme
5+
import androidx.compose.material3.MaterialTheme
6+
import androidx.compose.material3.darkColorScheme
7+
import androidx.compose.material3.dynamicDarkColorScheme
8+
import androidx.compose.material3.dynamicLightColorScheme
9+
import androidx.compose.material3.lightColorScheme
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.ui.platform.LocalContext
12+
13+
private val DarkColorScheme = darkColorScheme(
14+
primary = Purple200,
15+
onPrimary = Black,
16+
primaryContainer = Purple700,
17+
onPrimaryContainer = White,
18+
secondary = Teal200,
19+
onSecondary = Black,
20+
secondaryContainer = Teal700,
21+
onSecondaryContainer = White,
22+
background = Black,
23+
onBackground = White,
24+
surface = Black,
25+
onSurface = White,
26+
error = Red,
27+
onError = White
28+
)
29+
30+
private val LightColorScheme = lightColorScheme(
31+
primary = Primary,
32+
onPrimary = OnPrimary,
33+
primaryContainer = PrimaryContainer,
34+
onPrimaryContainer = OnPrimaryContainer,
35+
secondary = Secondary,
36+
onSecondary = OnSecondary,
37+
secondaryContainer = SecondaryContainer,
38+
onSecondaryContainer = OnSecondaryContainer,
39+
background = Background,
40+
onBackground = OnBackground,
41+
surface = Surface,
42+
onSurface = OnSurface,
43+
error = Error,
44+
onError = OnError
45+
)
46+
47+
@Composable
48+
fun VoiceSampleAppTheme(
49+
darkTheme: Boolean = isSystemInDarkTheme(),
50+
// Dynamic color is available on Android 12+
51+
dynamicColor: Boolean = false,
52+
content: @Composable () -> Unit
53+
) {
54+
val colorScheme = when {
55+
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
56+
val context = LocalContext.current
57+
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
58+
}
59+
darkTheme -> DarkColorScheme
60+
else -> LightColorScheme
61+
}
62+
63+
MaterialTheme(
64+
colorScheme = colorScheme,
65+
typography = Typography,
66+
content = content
67+
)
68+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.example.vonage.voicesampleapp.ui.theme
2+
3+
import androidx.compose.material3.Typography
4+
import androidx.compose.ui.text.TextStyle
5+
import androidx.compose.ui.text.font.FontFamily
6+
import androidx.compose.ui.text.font.FontWeight
7+
import androidx.compose.ui.unit.sp
8+
9+
val Typography = Typography(
10+
displayLarge = TextStyle(
11+
fontFamily = FontFamily.Default,
12+
fontWeight = FontWeight.Bold,
13+
fontSize = 32.sp,
14+
lineHeight = 40.sp,
15+
letterSpacing = 0.sp
16+
),
17+
displayMedium = TextStyle(
18+
fontFamily = FontFamily.Default,
19+
fontWeight = FontWeight.Bold,
20+
fontSize = 28.sp,
21+
lineHeight = 36.sp,
22+
letterSpacing = 0.sp
23+
),
24+
titleLarge = TextStyle(
25+
fontFamily = FontFamily.Default,
26+
fontWeight = FontWeight.Bold,
27+
fontSize = 22.sp,
28+
lineHeight = 28.sp,
29+
letterSpacing = 0.sp
30+
),
31+
titleMedium = TextStyle(
32+
fontFamily = FontFamily.Default,
33+
fontWeight = FontWeight.Bold,
34+
fontSize = 18.sp,
35+
lineHeight = 24.sp,
36+
letterSpacing = 0.15.sp
37+
),
38+
bodyLarge = TextStyle(
39+
fontFamily = FontFamily.Default,
40+
fontWeight = FontWeight.Normal,
41+
fontSize = 16.sp,
42+
lineHeight = 24.sp,
43+
letterSpacing = 0.5.sp
44+
),
45+
bodyMedium = TextStyle(
46+
fontFamily = FontFamily.Default,
47+
fontWeight = FontWeight.Normal,
48+
fontSize = 14.sp,
49+
lineHeight = 20.sp,
50+
letterSpacing = 0.25.sp
51+
),
52+
labelLarge = TextStyle(
53+
fontFamily = FontFamily.Default,
54+
fontWeight = FontWeight.Medium,
55+
fontSize = 14.sp,
56+
lineHeight = 20.sp,
57+
letterSpacing = 0.1.sp
58+
)
59+
)

contact-center/ios-voice/Podfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Uncomment the next line to define a global platform for your project
2-
platform :ios, '13.0'
2+
platform :ios, '15.0'
33
target 'VonageSDKClientVOIPExample' do
44
# Comment the next line if you don't want to use dynamic frameworks
55
use_frameworks!
66

77
# Pods for VonageSDKClientVOIPExample
8-
pod 'VonageClientSDKVoice', '2.1.2'
8+
pod 'VonageClientSDKVoice', '2.2.0-alpha.1'
99

1010
end
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
PODS:
2-
- VonageClientSDKVoice (2.1.2):
3-
- VonageWebRTC (~> 99.14.95)
4-
- VonageWebRTC (99.14.100)
2+
- VonageClientSDKVoice (2.2.0-alpha.1):
3+
- VonageWebRTC (~> 121.1.100)
4+
- VonageWebRTC (121.1.100)
55

66
DEPENDENCIES:
7-
- VonageClientSDKVoice (= 2.1.2)
7+
- VonageClientSDKVoice (= 2.2.0-alpha.1)
88

99
SPEC REPOS:
1010
trunk:
1111
- VonageClientSDKVoice
1212
- VonageWebRTC
1313

1414
SPEC CHECKSUMS:
15-
VonageClientSDKVoice: 9b5711d2209c72d6351d856343c30b4a3b10f81c
16-
VonageWebRTC: c6c80961496762ac0b94aedf3ba15e91e9e22088
15+
VonageClientSDKVoice: ee010dfa22d8963e4d432fd2f5573e089a327e9f
16+
VonageWebRTC: cc915fbcf91f82c71ad37dc576aca1025ecdb91c
1717

18-
PODFILE CHECKSUM: 2c0fc8a7028df1ccfeff6dbb7146a183dbd05199
18+
PODFILE CHECKSUM: c2982cd7559bccdc4b73719c74aba12d7a931418
1919

2020
COCOAPODS: 1.16.2

contact-center/ios-voice/README.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,34 @@
22

33
An iOS application powered by the Vonage Voice API to make and receive VOIP Calls.
44

5+
## Architecture Overview
6+
7+
This app follows a modern SwiftUI architecture with clean separation of concerns:
8+
9+
### Core Components
10+
- **`VonageVoiceApp`** - Main SwiftUI app entry point with NavigationStack-based routing
11+
- **`CoreContext`** - Singleton managing shared services and app-wide state
12+
- **`VoiceClientManager`** - Handles Vonage SDK integration, call lifecycle, and CallKit (device) / WebSocket (simulator)
13+
- `+VGVoiceClientDelegate` - Extension implementing voice client delegate methods
14+
- `+CXProviderDelegate` - Extension implementing CallKit provider delegate (device only)
15+
- **`PushService`** - Manages VoIP and user push notifications via PushKit
16+
- **`NetworkService`** - Generic Combine-based HTTP client for authentication APIs
17+
18+
### Views & ViewModels
19+
- **`LoginView/LoginViewModel`** - Supports token-based and code-based authentication
20+
- **`MainView`** - Home screen with username calling and floating dialer button
21+
- **`DialerView`** - Phone number dialing and in-call DTMF input
22+
- **`CallView`** - In-call UI with adaptive controls (incoming vs active states)
23+
24+
### Models
25+
- **`VGCallWrapper`** - Observable wrapper around Vonage calls with state management
26+
- **`CallState`** - Enum representing call lifecycle (ringing, active, holding, disconnected, reconnecting)
27+
28+
### Services & Utilities
29+
- **`Configuration`** - Reads API URLs and tokens from xcconfig
30+
- **`AppTheme`** - Centralized design system (colors, typography, spacing, button styles)
31+
- **Extensions** - Data hex conversion, UUID utilities, Publisher helpers, Vonage SDK error conformance
32+
533
## Getting Started
634
Note: A minimum version of Xcode 14.x is required to build and run.
735

@@ -39,11 +67,12 @@ To manually add these properties, follow these steps:
3967
VONAGE_API_TOKEN = <YOUR_API_TOKEN>
4068
API_LOGIN_URL = <YOUR_API_LOGIN_URL>
4169
API_REFRESH_URL = <YOUR_API_REFRESH_URL>
70+
API_KEY = <YOUR_API_KEY>
4271
```
4372

4473
Alternatively, you can run the following command in the project root folder to automatically add the lines:
4574
```bash
46-
echo "VONAGE_API_TOKEN = <YOUR_API_TOKEN>\nAPI_LOGIN_URL = <YOUR_API_LOGIN_URL>\nAPI_REFRESH_URL = <YOUR_API_REFRESH_URL>" >> secrets.xcconfig
75+
echo "VONAGE_API_TOKEN = <YOUR_API_TOKEN>\nAPI_LOGIN_URL = <YOUR_API_LOGIN_URL>\nAPI_REFRESH_URL = <YOUR_API_REFRESH_URL>\nAPI_KEY = <YOUR_API_KEY>" >> secrets.xcconfig
4776
```
4877

4978
**Note:** Make sure not to enclose the property values in double quotes (`"`), angular brackets (`<>`) or any other symbols.

0 commit comments

Comments
 (0)