diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bbf14cd..a7420ba 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -20,10 +20,10 @@ jobs: include: # https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md - os: macos-15 - xcode: 16.2 - platform: "iOS Simulator,OS=18.1,name=iPhone 16 Pro" + xcode: 16.4 + platform: "iOS Simulator,OS=18.5,name=iPhone 16 Pro" # - os: macos-15 - # xcode: 16.2 + # xcode: 16.4 # platform: "macOS" runs-on: ${{ matrix.os }} @@ -45,12 +45,12 @@ jobs: run: xcrun swift --version - name: Setup .env - run: cat VoiceAssistant/.env.example.xcconfig > VoiceAssistant/.env.xcconfig + run: cat VoiceAgent/.env.example.xcconfig > VoiceAgent/.env.xcconfig - name: Run Tests run: | set -o pipefail && xcodebuild test \ - -scheme VoiceAssistant \ + -scheme VoiceAgent \ -destination 'platform=${{ matrix.platform }}' | xcbeautify --renderer github-actions lint: diff --git a/.swift-version b/.swift-version index 15df682..e0ea36f 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.7 # Xcode 14 +6.0 diff --git a/README.md b/README.md index 6b619ff..1449d78 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ -Voice Assistant App Icon +Voice Agent App Icon -# Swift Voice Assistant +# Swift Voice Agent This is a starter template for [LiveKit Agents](https://docs.livekit.io/agents/overview/) that provides a simple voice interface using the [LiveKit Swift SDK](https://github.com/livekit/client-sdk-swift). It supports [voice](https://docs.livekit.io/agents/start/voice-ai), [transcriptions](https://docs.livekit.io/agents/build/text/), and [virtual avatars](https://docs.livekit.io/agents/integrations/avatar/). This template is compatible with iOS, iPadOS, macOS, and visionOS and is free for you to use or modify as you see fit. -Voice Assistant Screenshot +Voice Agent Screenshot ## Getting started @@ -17,15 +17,15 @@ First, create a new [Sandbox Token Server](https://cloud.livekit.io/projects/p_/ Then, run the following command to automatically clone this template and connect it to LiveKit Cloud. ```bash -lk app create --template voice-assistant-swift --sandbox +lk app create --template agent-starter-swift --sandbox ``` -Built and run the app from Xcode by opening `VoiceAssistant.xcodeproj`. You may need to adjust your app signing settings to run the app on your device. +Built and run the app from Xcode by opening `VoiceAgent.xcodeproj`. You may need to adjust your app signing settings to run the app on your device. You'll also need an agent to speak with. Try our [voice AI quickstart](https://docs.livekit.io/agents/start/voice-ai) for the easiest way to get started. > [!NOTE] -> To setup without the LiveKit CLI, clone the repository and then either create a `VoiceAssistant/.env.xcconfig` with a `LIVEKIT_SANDBOX_ID` (if using a [Sandbox Token Server](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server)), or open `TokenService.swift` and add your [manually generated](#token-generation) URL and token. +> To setup without the LiveKit CLI, clone the repository and then either create a `VoiceAgent/.env.xcconfig` with a `LIVEKIT_SANDBOX_ID` (if using a [Sandbox Token Server](https://cloud.livekit.io/projects/p_/sandbox/templates/token-server)), or open `TokenService.swift` and add your [manually generated](#token-generation) URL and token. ## Token generation diff --git a/VoiceAssistant.xcodeproj/project.pbxproj b/VoiceAgent.xcodeproj/project.pbxproj similarity index 88% rename from VoiceAssistant.xcodeproj/project.pbxproj rename to VoiceAgent.xcodeproj/project.pbxproj index dbeedf1..69f4a2d 100644 --- a/VoiceAssistant.xcodeproj/project.pbxproj +++ b/VoiceAgent.xcodeproj/project.pbxproj @@ -29,7 +29,7 @@ containerPortal = B5B5E3AA2D124AE00099C9BE /* Project object */; proxyType = 1; remoteGlobalIDString = B5B5E3B12D124AE00099C9BE; - remoteInfo = VoiceAssistant; + remoteInfo = VoiceAgent; }; /* End PBXContainerItemProxy section */ @@ -50,8 +50,8 @@ /* Begin PBXFileReference section */ ACAEBA582DE6EE970072E93E /* BroadcastExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = BroadcastExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; ACAEBA5A2DE6EE970072E93E /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; }; - ACC280272DEDDA1D0023C137 /* VoiceAssistantTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VoiceAssistantTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - B5B5E3B22D124AE00099C9BE /* VoiceAssistant.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoiceAssistant.app; sourceTree = BUILT_PRODUCTS_DIR; }; + ACC280272DEDDA1D0023C137 /* VoiceAgentTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VoiceAgentTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B5B5E3B22D124AE00099C9BE /* VoiceAgent.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VoiceAgent.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -62,15 +62,15 @@ ); target = ACAEBA572DE6EE970072E93E /* BroadcastExtension */; }; - B577A91F2D15FDB800B59A0B /* Exceptions for "VoiceAssistant" folder in "VoiceAssistant" target */ = { + B577A91F2D15FDB800B59A0B /* Exceptions for "VoiceAgent" folder in "VoiceAgent" target */ = { isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( .env.example.xcconfig, .env.xcconfig, Info.plist, - VoiceAssistant.xcconfig, + VoiceAgent.xcconfig, ); - target = B5B5E3B12D124AE00099C9BE /* VoiceAssistant */; + target = B5B5E3B12D124AE00099C9BE /* VoiceAgent */; }; /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -83,17 +83,17 @@ path = BroadcastExtension; sourceTree = ""; }; - ACC280282DEDDA1D0023C137 /* VoiceAssistantTests */ = { + ACC280282DEDDA1D0023C137 /* VoiceAgentTests */ = { isa = PBXFileSystemSynchronizedRootGroup; - path = VoiceAssistantTests; + path = VoiceAgentTests; sourceTree = ""; }; - B5B5E3B42D124AE00099C9BE /* VoiceAssistant */ = { + B5B5E3B42D124AE00099C9BE /* VoiceAgent */ = { isa = PBXFileSystemSynchronizedRootGroup; exceptions = ( - B577A91F2D15FDB800B59A0B /* Exceptions for "VoiceAssistant" folder in "VoiceAssistant" target */, + B577A91F2D15FDB800B59A0B /* Exceptions for "VoiceAgent" folder in "VoiceAgent" target */, ); - path = VoiceAssistant; + path = VoiceAgent; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ @@ -140,9 +140,9 @@ B5B5E3A92D124AE00099C9BE = { isa = PBXGroup; children = ( - B5B5E3B42D124AE00099C9BE /* VoiceAssistant */, + B5B5E3B42D124AE00099C9BE /* VoiceAgent */, ACAEBA5C2DE6EE970072E93E /* BroadcastExtension */, - ACC280282DEDDA1D0023C137 /* VoiceAssistantTests */, + ACC280282DEDDA1D0023C137 /* VoiceAgentTests */, ACAEBA592DE6EE970072E93E /* Frameworks */, B5B5E3B32D124AE00099C9BE /* Products */, ); @@ -151,9 +151,9 @@ B5B5E3B32D124AE00099C9BE /* Products */ = { isa = PBXGroup; children = ( - B5B5E3B22D124AE00099C9BE /* VoiceAssistant.app */, + B5B5E3B22D124AE00099C9BE /* VoiceAgent.app */, ACAEBA582DE6EE970072E93E /* BroadcastExtension.appex */, - ACC280272DEDDA1D0023C137 /* VoiceAssistantTests.xctest */, + ACC280272DEDDA1D0023C137 /* VoiceAgentTests.xctest */, ); name = Products; sourceTree = ""; @@ -184,9 +184,9 @@ productReference = ACAEBA582DE6EE970072E93E /* BroadcastExtension.appex */; productType = "com.apple.product-type.app-extension"; }; - ACC280262DEDDA1D0023C137 /* VoiceAssistantTests */ = { + ACC280262DEDDA1D0023C137 /* VoiceAgentTests */ = { isa = PBXNativeTarget; - buildConfigurationList = ACC2802F2DEDDA1D0023C137 /* Build configuration list for PBXNativeTarget "VoiceAssistantTests" */; + buildConfigurationList = ACC2802F2DEDDA1D0023C137 /* Build configuration list for PBXNativeTarget "VoiceAgentTests" */; buildPhases = ( ACC280232DEDDA1D0023C137 /* Sources */, ACC280242DEDDA1D0023C137 /* Frameworks */, @@ -198,18 +198,18 @@ ACC2802C2DEDDA1D0023C137 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( - ACC280282DEDDA1D0023C137 /* VoiceAssistantTests */, + ACC280282DEDDA1D0023C137 /* VoiceAgentTests */, ); - name = VoiceAssistantTests; + name = VoiceAgentTests; packageProductDependencies = ( ); - productName = VoiceAssistantTests; - productReference = ACC280272DEDDA1D0023C137 /* VoiceAssistantTests.xctest */; + productName = VoiceAgentTests; + productReference = ACC280272DEDDA1D0023C137 /* VoiceAgentTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - B5B5E3B12D124AE00099C9BE /* VoiceAssistant */ = { + B5B5E3B12D124AE00099C9BE /* VoiceAgent */ = { isa = PBXNativeTarget; - buildConfigurationList = B5B5E3C12D124AE20099C9BE /* Build configuration list for PBXNativeTarget "VoiceAssistant" */; + buildConfigurationList = B5B5E3C12D124AE20099C9BE /* Build configuration list for PBXNativeTarget "VoiceAgent" */; buildPhases = ( B5B5E3AE2D124AE00099C9BE /* Sources */, B5B5E3AF2D124AE00099C9BE /* Frameworks */, @@ -222,17 +222,17 @@ ACAEBA612DE6EE970072E93E /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( - B5B5E3B42D124AE00099C9BE /* VoiceAssistant */, + B5B5E3B42D124AE00099C9BE /* VoiceAgent */, ); - name = VoiceAssistant; + name = VoiceAgent; packageProductDependencies = ( B5E1B90E2D14E9EC00A38CB6 /* LiveKitComponents */, B5E1B9112D14E9F500A38CB6 /* LiveKit */, ACBC17792D8C34B5007EE71F /* AsyncAlgorithms */, ACFBA1DA2D8D5CBE0021202B /* Collections */, ); - productName = VoiceAssistant; - productReference = B5B5E3B22D124AE00099C9BE /* VoiceAssistant.app */; + productName = VoiceAgent; + productReference = B5B5E3B22D124AE00099C9BE /* VoiceAgent.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -257,7 +257,7 @@ }; }; }; - buildConfigurationList = B5B5E3AD2D124AE00099C9BE /* Build configuration list for PBXProject "VoiceAssistant" */; + buildConfigurationList = B5B5E3AD2D124AE00099C9BE /* Build configuration list for PBXProject "VoiceAgent" */; developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( @@ -277,8 +277,8 @@ projectDirPath = ""; projectRoot = ""; targets = ( - B5B5E3B12D124AE00099C9BE /* VoiceAssistant */, - ACC280262DEDDA1D0023C137 /* VoiceAssistantTests */, + B5B5E3B12D124AE00099C9BE /* VoiceAgent */, + ACC280262DEDDA1D0023C137 /* VoiceAgentTests */, ACAEBA572DE6EE970072E93E /* BroadcastExtension */, ); }; @@ -344,7 +344,7 @@ }; ACC2802C2DEDDA1D0023C137 /* PBXTargetDependency */ = { isa = PBXTargetDependency; - target = B5B5E3B12D124AE00099C9BE /* VoiceAssistant */; + target = B5B5E3B12D124AE00099C9BE /* VoiceAgent */; targetProxy = ACC2802B2DEDDA1D0023C137 /* PBXContainerItemProxy */; }; /* End PBXTargetDependency section */ @@ -355,11 +355,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = BroadcastExtension/BroadcastExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; DEVELOPMENT_TEAM = 76TVFCUKK7; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = BroadcastExtension/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = VoiceAssistant; + INFOPLIST_KEY_CFBundleDisplayName = VoiceAgent; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -388,11 +387,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = BroadcastExtension/BroadcastExtension.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; DEVELOPMENT_TEAM = 76TVFCUKK7; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = BroadcastExtension/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = VoiceAssistant; + INFOPLIST_KEY_CFBundleDisplayName = VoiceAgent; INFOPLIST_KEY_NSHumanReadableCopyright = ""; IPHONEOS_DEPLOYMENT_TARGET = 18.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -424,14 +422,13 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 76TVFCUKK7; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 18.0; MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.livekit.VoiceAssistantTests; + PRODUCT_BUNDLE_IDENTIFIER = com.livekit.VoiceAgentTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; @@ -439,7 +436,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,7"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VoiceAssistant.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VoiceAssistant"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VoiceAgent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VoiceAgent"; XROS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; @@ -451,14 +448,13 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_TEAM = 76TVFCUKK7; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 18.0; MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.livekit.VoiceAssistantTests; + PRODUCT_BUNDLE_IDENTIFIER = com.livekit.VoiceAgentTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; @@ -466,7 +462,7 @@ SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2,7"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VoiceAssistant.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VoiceAssistant"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VoiceAgent.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/VoiceAgent"; XROS_DEPLOYMENT_TARGET = 2.0; }; name = Release; @@ -505,6 +501,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -569,6 +566,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -593,24 +591,24 @@ }; B5B5E3C22D124AE20099C9BE /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = B5B5E3B42D124AE00099C9BE /* VoiceAssistant */; - baseConfigurationReferenceRelativePath = VoiceAssistant.xcconfig; + baseConfigurationReferenceAnchor = B5B5E3B42D124AE00099C9BE /* VoiceAgent */; + baseConfigurationReferenceRelativePath = VoiceAgent.xcconfig; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = fgAccent; - CODE_SIGN_ENTITLEMENTS = VoiceAssistant/VoiceAssistant.entitlements; + CODE_SIGN_ENTITLEMENTS = VoiceAgent/VoiceAgent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_ASSET_PATHS = "\"VoiceAssistant/Preview Content\""; + DEVELOPMENT_ASSET_PATHS = "\"VoiceAgent/Preview Content\""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = VoiceAssistant/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = ""; + INFOPLIST_FILE = VoiceAgent/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = VoiceAgent; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_LSApplicationCategoryType = ""; - INFOPLIST_KEY_NSCameraUsageDescription = "VoiceAssistant uses your camera"; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "VoiceAssistant uses your microphone"; + INFOPLIST_KEY_NSCameraUsageDescription = "VoiceAgent uses your camera"; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "VoiceAgent uses your microphone"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -626,6 +624,7 @@ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.livekit.example.VoiceAssistant; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; SDKROOT = auto; @@ -638,24 +637,24 @@ }; B5B5E3C32D124AE20099C9BE /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReferenceAnchor = B5B5E3B42D124AE00099C9BE /* VoiceAssistant */; - baseConfigurationReferenceRelativePath = VoiceAssistant.xcconfig; + baseConfigurationReferenceAnchor = B5B5E3B42D124AE00099C9BE /* VoiceAgent */; + baseConfigurationReferenceRelativePath = VoiceAgent.xcconfig; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = fgAccent; - CODE_SIGN_ENTITLEMENTS = VoiceAssistant/VoiceAssistant.entitlements; + CODE_SIGN_ENTITLEMENTS = VoiceAgent/VoiceAgent.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 6; DEAD_CODE_STRIPPING = YES; - DEVELOPMENT_ASSET_PATHS = "\"VoiceAssistant/Preview Content\""; + DEVELOPMENT_ASSET_PATHS = "\"VoiceAgent/Preview Content\""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = VoiceAssistant/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = ""; + INFOPLIST_FILE = VoiceAgent/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = VoiceAgent; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_LSApplicationCategoryType = ""; - INFOPLIST_KEY_NSCameraUsageDescription = "VoiceAssistant uses your camera"; - INFOPLIST_KEY_NSMicrophoneUsageDescription = "VoiceAssistant uses your microphone"; + INFOPLIST_KEY_NSCameraUsageDescription = "VoiceAgent uses your camera"; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "VoiceAgent uses your microphone"; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; "INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents[sdk=iphoneos*]" = YES; @@ -671,6 +670,7 @@ "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 15.0; MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.livekit.example.VoiceAssistant; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; SDKROOT = auto; @@ -693,7 +693,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - ACC2802F2DEDDA1D0023C137 /* Build configuration list for PBXNativeTarget "VoiceAssistantTests" */ = { + ACC2802F2DEDDA1D0023C137 /* Build configuration list for PBXNativeTarget "VoiceAgentTests" */ = { isa = XCConfigurationList; buildConfigurations = ( ACC2802D2DEDDA1D0023C137 /* Debug */, @@ -702,7 +702,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - B5B5E3AD2D124AE00099C9BE /* Build configuration list for PBXProject "VoiceAssistant" */ = { + B5B5E3AD2D124AE00099C9BE /* Build configuration list for PBXProject "VoiceAgent" */ = { isa = XCConfigurationList; buildConfigurations = ( B5B5E3BF2D124AE20099C9BE /* Debug */, @@ -711,7 +711,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - B5B5E3C12D124AE20099C9BE /* Build configuration list for PBXNativeTarget "VoiceAssistant" */ = { + B5B5E3C12D124AE20099C9BE /* Build configuration list for PBXNativeTarget "VoiceAgent" */ = { isa = XCConfigurationList; buildConfigurations = ( B5B5E3C22D124AE20099C9BE /* Debug */, diff --git a/VoiceAssistant.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/VoiceAgent.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from VoiceAssistant.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to VoiceAgent.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/VoiceAssistant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/VoiceAgent.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved similarity index 100% rename from VoiceAssistant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved rename to VoiceAgent.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/VoiceAssistant.xcodeproj/xcshareddata/xcschemes/BroadcastExtension.xcscheme b/VoiceAgent.xcodeproj/xcshareddata/xcschemes/BroadcastExtension.xcscheme similarity index 81% rename from VoiceAssistant.xcodeproj/xcshareddata/xcschemes/BroadcastExtension.xcscheme rename to VoiceAgent.xcodeproj/xcshareddata/xcschemes/BroadcastExtension.xcscheme index 17e02e5..d685fb3 100644 --- a/VoiceAssistant.xcodeproj/xcshareddata/xcschemes/BroadcastExtension.xcscheme +++ b/VoiceAgent.xcodeproj/xcshareddata/xcschemes/BroadcastExtension.xcscheme @@ -19,7 +19,7 @@ BlueprintIdentifier = "ACAEBA572DE6EE970072E93E" BuildableName = "BroadcastExtension.appex" BlueprintName = "BroadcastExtension" - ReferencedContainer = "container:VoiceAssistant.xcodeproj"> + ReferencedContainer = "container:VoiceAgent.xcodeproj"> + BuildableName = "VoiceAgent.app" + BlueprintName = "VoiceAgent" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> @@ -51,9 +51,9 @@ + BuildableName = "VoiceAgentTests.xctest" + BlueprintName = "VoiceAgentTests" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> @@ -73,15 +73,15 @@ + RemotePath = "/var/containers/Bundle/Application/0D446371-3B0C-4020-AA6B-22058F398E0D/VoiceAgent.app"> + BuildableName = "VoiceAgent.app" + BlueprintName = "VoiceAgent" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> @@ -98,9 +98,9 @@ + BuildableName = "VoiceAgent.app" + BlueprintName = "VoiceAgent" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> diff --git a/VoiceAssistant.xcodeproj/xcshareddata/xcschemes/VoiceAssistant.xcscheme b/VoiceAgent.xcodeproj/xcshareddata/xcschemes/VoiceAgent.xcscheme similarity index 78% rename from VoiceAssistant.xcodeproj/xcshareddata/xcschemes/VoiceAssistant.xcscheme rename to VoiceAgent.xcodeproj/xcshareddata/xcschemes/VoiceAgent.xcscheme index e4361dc..e1b74c4 100644 --- a/VoiceAssistant.xcodeproj/xcshareddata/xcschemes/VoiceAssistant.xcscheme +++ b/VoiceAgent.xcodeproj/xcshareddata/xcschemes/VoiceAgent.xcscheme @@ -16,9 +16,9 @@ + BuildableName = "VoiceAgent.app" + BlueprintName = "VoiceAgent" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> @@ -30,7 +30,7 @@ shouldUseLaunchSchemeArgsEnv = "YES"> @@ -41,9 +41,9 @@ + BuildableName = "VoiceAgentTests.xctest" + BlueprintName = "VoiceAgentTests" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> @@ -63,9 +63,9 @@ + BuildableName = "VoiceAgent.app" + BlueprintName = "VoiceAgent" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> @@ -80,9 +80,9 @@ + BuildableName = "VoiceAgent.app" + BlueprintName = "VoiceAgent" + ReferencedContainer = "container:VoiceAgent.xcodeproj"> diff --git a/VoiceAssistant/.env.example.xcconfig b/VoiceAgent/.env.example.xcconfig similarity index 100% rename from VoiceAssistant/.env.example.xcconfig rename to VoiceAgent/.env.example.xcconfig diff --git a/VoiceAssistant/App/AppView.swift b/VoiceAgent/App/AppView.swift similarity index 100% rename from VoiceAssistant/App/AppView.swift rename to VoiceAgent/App/AppView.swift diff --git a/VoiceAssistant/App/AppViewModel.swift b/VoiceAgent/App/AppViewModel.swift similarity index 90% rename from VoiceAssistant/App/AppViewModel.swift rename to VoiceAgent/App/AppViewModel.swift index b2f7b88..1aa5641 100644 --- a/VoiceAssistant/App/AppViewModel.swift +++ b/VoiceAgent/App/AppViewModel.swift @@ -25,7 +25,7 @@ final class AppViewModel { var errorDescription: String? { switch self { case .agentNotConnected: - return "Agent did not connect to the Room" + "Agent did not connect to the Room" } } } @@ -51,9 +51,9 @@ final class AppViewModel { .connecting where isListening, .connected, .reconnecting: - return true + true default: - return false + false } } @@ -117,7 +117,8 @@ final class AppViewModel { isScreenShareEnabled = room.localParticipant.isScreenShareEnabled() screenShareTrack = room.localParticipant.firstScreenShareVideoTrack - agentAudioTrack = room.agentParticipant?.firstAudioTrack + agentAudioTrack = room.agentParticipant?.audioTracks + .first(where: { $0.source == .microphone })?.track as? AudioTrack avatarCameraTrack = room.agentParticipant?.avatarWorker?.firstCameraVideoTrack } } @@ -125,6 +126,7 @@ final class AppViewModel { private func observeDevices() { do { + try AudioManager.shared.set(microphoneMuteMode: .inputMixer) // don't play mute sound effect try AudioManager.shared.setRecordingAlwaysPreparedMode(true) } catch { errorHandler(error) @@ -245,17 +247,28 @@ final class AppViewModel { } func toggleCamera() async { + let enable = !isCameraEnabled do { + // One video track at a time + if enable, isScreenShareEnabled { + try await room.localParticipant.setScreenShare(enabled: false) + } + let device = try await CameraCapturer.captureDevices().first(where: { $0.uniqueID == selectedVideoDeviceID }) - try await room.localParticipant.setCamera(enabled: !isCameraEnabled, captureOptions: CameraCaptureOptions(device: device)) + try await room.localParticipant.setCamera(enabled: enable, captureOptions: CameraCaptureOptions(device: device)) } catch { errorHandler(error) } } func toggleScreenShare() async { + let enable = !isScreenShareEnabled do { - try await room.localParticipant.setScreenShare(enabled: !isScreenShareEnabled) + // One video track at a time + if enable, isCameraEnabled { + try await room.localParticipant.setCamera(enabled: false) + } + try await room.localParticipant.setScreenShare(enabled: enable) } catch { errorHandler(error) } diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/128.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/128.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/128.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/128.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/128@2x.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/128@2x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/128@2x.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/128@2x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/16.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/16.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/16.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/16.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/16@2x.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/16@2x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/16@2x.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/16@2x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/256.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/256.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/256.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/256.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/256@2x.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/256@2x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/256@2x.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/256@2x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/32.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/32.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/32.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/32.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/32@2x.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/32@2x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/32@2x.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/32@2x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/512.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/512.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/512.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/512.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/512@2x.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/512@2x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/512@2x.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/512@2x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/App_store_1024_1x.png b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/App_store_1024_1x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/App_store_1024_1x.png rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/App_store_1024_1x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.appiconset/Contents.json b/VoiceAgent/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.appiconset/Contents.json rename to VoiceAgent/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/512@2x.png b/VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/512@2x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/512@2x.png rename to VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/512@2x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json b/VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json rename to VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Content.imageset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json b/VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json rename to VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Back.solidimagestacklayer/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Contents.json b/VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Contents.json rename to VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/512@2x.png b/VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/512@2x.png similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/512@2x.png rename to VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/512@2x.png diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json b/VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json rename to VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Content.imageset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json b/VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json rename to VoiceAgent/Assets.xcassets/AppIcon.solidimagestack/Front.solidimagestacklayer/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/Contents.json b/VoiceAgent/Assets.xcassets/Colors/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/bg1.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/bg1.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/bg1.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/bg1.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/bg2.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/bg2.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/bg2.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/bg2.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/bg3.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/bg3.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/bg3.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/bg3.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/bgAccent.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/bgAccent.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/bgAccent.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/bgAccent.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/bgModerate.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/bgModerate.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/bgModerate.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/bgModerate.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/bgSerious.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/bgSerious.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/bgSerious.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/bgSerious.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/bgSuccess.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/bgSuccess.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/bgSuccess.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/bgSuccess.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fg0.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fg0.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fg0.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fg0.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fg1.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fg1.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fg1.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fg1.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fg2.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fg2.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fg2.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fg2.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fg3.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fg3.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fg3.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fg3.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fg4.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fg4.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fg4.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fg4.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fgAccent.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fgAccent.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fgAccent.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fgAccent.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fgModerate.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fgModerate.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fgModerate.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fgModerate.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fgSerious.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fgSerious.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fgSerious.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fgSerious.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/fgSuccess.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/fgSuccess.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/fgSuccess.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/fgSuccess.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/separator1.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/separator1.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/separator1.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/separator1.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/separator2.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/separator2.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/separator2.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/separator2.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/separatorAccent.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/separatorAccent.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/separatorAccent.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/separatorAccent.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/separatorModerate.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/separatorModerate.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/separatorModerate.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/separatorModerate.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/separatorSerious.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/separatorSerious.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/separatorSerious.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/separatorSerious.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Colors/separatorSuccess.colorset/Contents.json b/VoiceAgent/Assets.xcassets/Colors/separatorSuccess.colorset/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Colors/separatorSuccess.colorset/Contents.json rename to VoiceAgent/Assets.xcassets/Colors/separatorSuccess.colorset/Contents.json diff --git a/VoiceAssistant/Assets.xcassets/Contents.json b/VoiceAgent/Assets.xcassets/Contents.json similarity index 100% rename from VoiceAssistant/Assets.xcassets/Contents.json rename to VoiceAgent/Assets.xcassets/Contents.json diff --git a/VoiceAssistant/Auth/TokenService.swift b/VoiceAgent/Auth/TokenService.swift similarity index 100% rename from VoiceAssistant/Auth/TokenService.swift rename to VoiceAgent/Auth/TokenService.swift diff --git a/VoiceAssistant/Chat/ChatViewModel.swift b/VoiceAgent/Chat/ChatViewModel.swift similarity index 100% rename from VoiceAssistant/Chat/ChatViewModel.swift rename to VoiceAgent/Chat/ChatViewModel.swift diff --git a/VoiceAssistant/Chat/Message.swift b/VoiceAgent/Chat/Message.swift similarity index 100% rename from VoiceAssistant/Chat/Message.swift rename to VoiceAgent/Chat/Message.swift diff --git a/VoiceAssistant/Chat/Receive/MessageReceiver.swift b/VoiceAgent/Chat/Receive/MessageReceiver.swift similarity index 100% rename from VoiceAssistant/Chat/Receive/MessageReceiver.swift rename to VoiceAgent/Chat/Receive/MessageReceiver.swift diff --git a/VoiceAssistant/Chat/Receive/TranscriptionDelegateReceiver.swift b/VoiceAgent/Chat/Receive/TranscriptionDelegateReceiver.swift similarity index 100% rename from VoiceAssistant/Chat/Receive/TranscriptionDelegateReceiver.swift rename to VoiceAgent/Chat/Receive/TranscriptionDelegateReceiver.swift diff --git a/VoiceAssistant/Chat/Receive/TranscriptionStreamReceiver.swift b/VoiceAgent/Chat/Receive/TranscriptionStreamReceiver.swift similarity index 100% rename from VoiceAssistant/Chat/Receive/TranscriptionStreamReceiver.swift rename to VoiceAgent/Chat/Receive/TranscriptionStreamReceiver.swift diff --git a/VoiceAssistant/Chat/Send/LocalMessageSender.swift b/VoiceAgent/Chat/Send/LocalMessageSender.swift similarity index 100% rename from VoiceAssistant/Chat/Send/LocalMessageSender.swift rename to VoiceAgent/Chat/Send/LocalMessageSender.swift diff --git a/VoiceAssistant/Chat/Send/MessageSender.swift b/VoiceAgent/Chat/Send/MessageSender.swift similarity index 100% rename from VoiceAssistant/Chat/Send/MessageSender.swift rename to VoiceAgent/Chat/Send/MessageSender.swift diff --git a/VoiceAssistant/Chat/View/ChatTextInputView.swift b/VoiceAgent/Chat/View/ChatTextInputView.swift similarity index 91% rename from VoiceAssistant/Chat/View/ChatTextInputView.swift rename to VoiceAgent/Chat/View/ChatTextInputView.swift index dab87b7..72c69e6 100644 --- a/VoiceAssistant/Chat/View/ChatTextInputView.swift +++ b/VoiceAgent/Chat/View/ChatTextInputView.swift @@ -10,7 +10,7 @@ struct ChatTextInputView: View { @State private var messageText = "" var body: some View { - HStack(spacing: 12) { + HStack(alignment: .bottom, spacing: 12) { textField() sendButton() } @@ -55,7 +55,11 @@ struct ChatTextInputView: View { Image(systemName: "arrow.up") .frame(width: 8 * .grid, height: 8 * .grid) } - .padding(.trailing, 3 * .grid) + #if os(iOS) + .padding([.bottom, .trailing], 3 * .grid) + #else + .padding([.bottom, .trailing], 2 * .grid) + #endif .disabled(messageText.isEmpty) #if os(visionOS) .buttonStyle(.plain) diff --git a/VoiceAssistant/Chat/View/ChatView.swift b/VoiceAgent/Chat/View/ChatView.swift similarity index 94% rename from VoiceAssistant/Chat/View/ChatView.swift rename to VoiceAgent/Chat/View/ChatView.swift index 07b3de8..1b53700 100644 --- a/VoiceAssistant/Chat/View/ChatView.swift +++ b/VoiceAgent/Chat/View/ChatView.swift @@ -17,7 +17,7 @@ struct ChatView: View { } .upsideDown() .listStyle(.plain) - .scrollIndicators(.hidden) + .scrollIndicators(.never) .scrollContentBackground(.hidden) .animation(.default, value: viewModel.messages) } @@ -43,7 +43,7 @@ struct ChatView: View { HStack { Spacer(minLength: 4 * .grid) Text(text.trimmingCharacters(in: .whitespacesAndNewlines)) - .font(.system(size: 15)) + .font(.system(size: 17)) .padding(.horizontal, 4 * .grid) .padding(.vertical, 2 * .grid) .foregroundStyle(.fg1) @@ -58,7 +58,7 @@ struct ChatView: View { private func agentTranscript(_ text: String) -> some View { HStack { Text(text.trimmingCharacters(in: .whitespacesAndNewlines)) - .font(.system(size: 20)) + .font(.system(size: 17)) .padding(.vertical, 2 * .grid) Spacer(minLength: 4 * .grid) } diff --git a/VoiceAssistant/ControlBar/ControlBar.swift b/VoiceAgent/ControlBar/ControlBar.swift similarity index 97% rename from VoiceAssistant/ControlBar/ControlBar.swift rename to VoiceAgent/ControlBar/ControlBar.swift index d905430..9ba3d21 100644 --- a/VoiceAssistant/ControlBar/ControlBar.swift +++ b/VoiceAgent/ControlBar/ControlBar.swift @@ -117,7 +117,7 @@ struct ControlBar: View { Spacer() } .frame(width: Constants.buttonWidth) - .disabled(viewModel.connectionState != .connected) + .disabled(viewModel.agent == nil) } @ViewBuilder @@ -134,7 +134,7 @@ struct ControlBar: View { borderColor: .separator1 ) ) - .disabled(viewModel.connectionState != .connected) + .disabled(viewModel.agent == nil) } @ViewBuilder @@ -151,6 +151,7 @@ struct ControlBar: View { borderColor: .separator1 ) ) + .disabled(viewModel.agent == nil) } @ViewBuilder diff --git a/VoiceAssistant/ControlBar/Devices/AudioDeviceSelector.swift b/VoiceAgent/ControlBar/Devices/AudioDeviceSelector.swift similarity index 100% rename from VoiceAssistant/ControlBar/Devices/AudioDeviceSelector.swift rename to VoiceAgent/ControlBar/Devices/AudioDeviceSelector.swift diff --git a/VoiceAssistant/ControlBar/Devices/VideoDeviceSelector.swift b/VoiceAgent/ControlBar/Devices/VideoDeviceSelector.swift similarity index 100% rename from VoiceAssistant/ControlBar/Devices/VideoDeviceSelector.swift rename to VoiceAgent/ControlBar/Devices/VideoDeviceSelector.swift diff --git a/VoiceAssistant/DI/Dependencies.swift b/VoiceAgent/DI/Dependencies.swift similarity index 100% rename from VoiceAssistant/DI/Dependencies.swift rename to VoiceAgent/DI/Dependencies.swift diff --git a/VoiceAssistant/Error/ErrorView.swift b/VoiceAgent/Error/ErrorView.swift similarity index 100% rename from VoiceAssistant/Error/ErrorView.swift rename to VoiceAgent/Error/ErrorView.swift diff --git a/VoiceAssistant/Error/WarningView.swift b/VoiceAgent/Error/WarningView.swift similarity index 100% rename from VoiceAssistant/Error/WarningView.swift rename to VoiceAgent/Error/WarningView.swift diff --git a/VoiceAssistant/Helpers/AsyncButton.swift b/VoiceAgent/Helpers/AsyncButton.swift similarity index 100% rename from VoiceAssistant/Helpers/AsyncButton.swift rename to VoiceAgent/Helpers/AsyncButton.swift diff --git a/VoiceAssistant/Helpers/Environment.swift b/VoiceAgent/Helpers/Environment.swift similarity index 100% rename from VoiceAssistant/Helpers/Environment.swift rename to VoiceAgent/Helpers/Environment.swift diff --git a/VoiceAssistant/Helpers/ObservableObject+.swift b/VoiceAgent/Helpers/ObservableObject+.swift similarity index 100% rename from VoiceAssistant/Helpers/ObservableObject+.swift rename to VoiceAgent/Helpers/ObservableObject+.swift diff --git a/VoiceAssistant/Helpers/Spinner.swift b/VoiceAgent/Helpers/Spinner.swift similarity index 100% rename from VoiceAssistant/Helpers/Spinner.swift rename to VoiceAgent/Helpers/Spinner.swift diff --git a/VoiceAssistant/Helpers/Style.swift b/VoiceAgent/Helpers/Style.swift similarity index 100% rename from VoiceAssistant/Helpers/Style.swift rename to VoiceAgent/Helpers/Style.swift diff --git a/VoiceAssistant/Helpers/VideoTrack+.swift b/VoiceAgent/Helpers/VideoTrack+.swift similarity index 100% rename from VoiceAssistant/Helpers/VideoTrack+.swift rename to VoiceAgent/Helpers/VideoTrack+.swift diff --git a/VoiceAssistant/Helpers/View+.swift b/VoiceAgent/Helpers/View+.swift similarity index 85% rename from VoiceAssistant/Helpers/View+.swift rename to VoiceAgent/Helpers/View+.swift index a48f3f1..c2cc263 100644 --- a/VoiceAssistant/Helpers/View+.swift +++ b/VoiceAgent/Helpers/View+.swift @@ -38,10 +38,10 @@ struct Shimerring: ViewModifier { .black, .black.opacity(0.4), ], - startPoint: isShimmering ? UnitPoint(x: -1, y: 0) : UnitPoint(x: 1, y: 0), - endPoint: isShimmering ? UnitPoint(x: 0, y: 0) : UnitPoint(x: 2, y: 0) + startPoint: isShimmering ? UnitPoint(x: 1, y: 0) : UnitPoint(x: -1, y: 0), + endPoint: isShimmering ? UnitPoint(x: 2, y: 0) : UnitPoint(x: 0, y: 0) ) - .animation(.linear(duration: 5).repeatForever(autoreverses: true), value: isShimmering) + .animation(.linear(duration: 2).repeatForever(autoreverses: false), value: isShimmering) ) .onAppear { isShimmering = true diff --git a/VoiceAssistant/Info.plist b/VoiceAgent/Info.plist similarity index 100% rename from VoiceAssistant/Info.plist rename to VoiceAgent/Info.plist diff --git a/VoiceAssistant/Interactions/TextInteractionView.swift b/VoiceAgent/Interactions/TextInteractionView.swift similarity index 85% rename from VoiceAssistant/Interactions/TextInteractionView.swift rename to VoiceAgent/Interactions/TextInteractionView.swift index 08918ff..a4a285c 100644 --- a/VoiceAssistant/Interactions/TextInteractionView.swift +++ b/VoiceAgent/Interactions/TextInteractionView.swift @@ -9,6 +9,7 @@ import SwiftUI /// /// Additionally, the view shows a complete chat view with text input capabilities. struct TextInteractionView: View { + @Environment(AppViewModel.self) private var viewModel @FocusState.Binding var keyboardFocus: Bool var body: some View { @@ -36,11 +37,12 @@ struct TextInteractionView: View { HStack { Spacer() AgentParticipantView() + .frame(maxWidth: 25 * .grid) ScreenShareView() LocalParticipantView() Spacer() } - .frame(height: 50 * .grid) + .frame(height: viewModel.isCameraEnabled || viewModel.isScreenShareEnabled ? 50 * .grid : 25 * .grid) .safeAreaPadding() } } diff --git a/VoiceAssistant/Interactions/VisionInteractionView.swift b/VoiceAgent/Interactions/VisionInteractionView.swift similarity index 100% rename from VoiceAssistant/Interactions/VisionInteractionView.swift rename to VoiceAgent/Interactions/VisionInteractionView.swift diff --git a/VoiceAssistant/Interactions/VoiceInteractionView.swift b/VoiceAgent/Interactions/VoiceInteractionView.swift similarity index 100% rename from VoiceAssistant/Interactions/VoiceInteractionView.swift rename to VoiceAgent/Interactions/VoiceInteractionView.swift diff --git a/VoiceAssistant/Localizable.xcstrings b/VoiceAgent/Localizable.xcstrings similarity index 92% rename from VoiceAssistant/Localizable.xcstrings rename to VoiceAgent/Localizable.xcstrings index 129a3c7..a83f0bc 100644 --- a/VoiceAssistant/Localizable.xcstrings +++ b/VoiceAgent/Localizable.xcstrings @@ -51,7 +51,7 @@ "en" : { "stringUnit" : { "state" : "translated", - "value" : "Start a call to chat with your voice agent. Need help getting set up? Check out the [Voice AI quickstart](https://docs.livekit.io/agents/start/voice-ai/)." + "value" : "Need help getting set up? Check out the [Voice AI quickstart](https://docs.livekit.io/agents/start/voice-ai/)." } } } diff --git a/VoiceAssistant/Participant/AgentListeningView.swift b/VoiceAgent/Participant/AgentListeningView.swift similarity index 100% rename from VoiceAssistant/Participant/AgentListeningView.swift rename to VoiceAgent/Participant/AgentListeningView.swift diff --git a/VoiceAssistant/Participant/AgentParticipantView.swift b/VoiceAgent/Participant/AgentParticipantView.swift similarity index 95% rename from VoiceAssistant/Participant/AgentParticipantView.swift rename to VoiceAgent/Participant/AgentParticipantView.swift index c6b767f..2c5e43f 100644 --- a/VoiceAssistant/Participant/AgentParticipantView.swift +++ b/VoiceAgent/Participant/AgentParticipantView.swift @@ -37,7 +37,7 @@ struct AgentParticipantView: View { barMinOpacity: 0.1) .frame(maxWidth: 75 * .grid, maxHeight: 48 * .grid) .transition(.opacity) - } else { + } else if viewModel.isInteractive { BarAudioVisualizer(audioTrack: nil, agentState: .listening, barCount: 1, @@ -46,7 +46,7 @@ struct AgentParticipantView: View { .transition(.opacity) } } - .animation(.default, value: viewModel.agentAudioTrack?.id) + .animation(.snappy, value: viewModel.agentAudioTrack?.id) .matchedGeometryEffect(id: "agent", in: namespace!) } } diff --git a/VoiceAssistant/Participant/LocalParticipantView.swift b/VoiceAgent/Participant/LocalParticipantView.swift similarity index 96% rename from VoiceAssistant/Participant/LocalParticipantView.swift rename to VoiceAgent/Participant/LocalParticipantView.swift index 712aac0..8e4c4d0 100644 --- a/VoiceAssistant/Participant/LocalParticipantView.swift +++ b/VoiceAgent/Participant/LocalParticipantView.swift @@ -16,7 +16,7 @@ struct LocalParticipantView: View { if viewModel.canSwitchCamera { AsyncButton(action: viewModel.switchCamera) { Image(systemName: "arrow.trianglehead.2.clockwise.rotate.90") - .padding(8) + .padding(2 * .grid) .foregroundStyle(.fg0) .background(.bg1.opacity(0.8)) .clipShape(Circle()) diff --git a/VoiceAssistant/Participant/ScreenShareView.swift b/VoiceAgent/Participant/ScreenShareView.swift similarity index 100% rename from VoiceAssistant/Participant/ScreenShareView.swift rename to VoiceAgent/Participant/ScreenShareView.swift diff --git a/VoiceAssistant/Preview Content/Preview Assets.xcassets/Contents.json b/VoiceAgent/Preview Content/Preview Assets.xcassets/Contents.json similarity index 100% rename from VoiceAssistant/Preview Content/Preview Assets.xcassets/Contents.json rename to VoiceAgent/Preview Content/Preview Assets.xcassets/Contents.json diff --git a/VoiceAssistant/Start/StartView.swift b/VoiceAgent/Start/StartView.swift similarity index 64% rename from VoiceAssistant/Start/StartView.swift rename to VoiceAgent/Start/StartView.swift index 8bbe811..d5d7ebe 100644 --- a/VoiceAssistant/Start/StartView.swift +++ b/VoiceAgent/Start/StartView.swift @@ -8,17 +8,13 @@ struct StartView: View { @Namespace private var button var body: some View { - VStack(spacing: 4 * .grid) { - text() - Spacer().frame(height: 4 * .grid) + VStack(spacing: 8 * .grid) { + bars() connectButton() - #if targetEnvironment(simulator) - Spacer().frame(height: 4 * .grid) - simulator() - #endif } .padding(.horizontal, horizontalSizeClass == .regular ? 32 * .grid : 16 * .grid) .frame(maxWidth: .infinity, maxHeight: .infinity) + .safeAreaInset(edge: .bottom, content: tip) #if os(visionOS) .glassBackgroundEffect() .frame(maxWidth: 175 * .grid) @@ -26,13 +22,34 @@ struct StartView: View { } @ViewBuilder - private func text() -> some View { - Image(systemName: "apple.terminal") - .font(.system(size: 56, weight: .thin)) - Text("connect.tip") - .font(.system(size: 17)) - .tint(.fg2) // for markdown links - .multilineTextAlignment(.center) + private func bars() -> some View { + HStack(spacing: .grid) { + ForEach(0 ..< 5, id: \.self) { index in + Rectangle() + .fill(.fg0) + .frame(width: 2 * .grid, height: barHeight(index)) + } + } + } + + private func barHeight(_ index: Int) -> CGFloat { + let heights: [CGFloat] = [2, 8, 12, 8, 2].map { $0 * .grid } + return heights[index] + } + + @ViewBuilder + private func tip() -> some View { + VStack(spacing: 2 * .grid) { + #if targetEnvironment(simulator) + Text("connect.simulator") + .foregroundStyle(.fgModerate) + #endif + Text("connect.tip") + .foregroundStyle(.fg3) + } + .font(.system(size: 12)) + .multilineTextAlignment(.center) + .padding(.horizontal, horizontalSizeClass == .regular ? 32 * .grid : 16 * .grid) } @ViewBuilder @@ -63,14 +80,6 @@ struct StartView: View { .buttonStyle(ProminentButtonStyle()) #endif } - - @ViewBuilder - private func simulator() -> some View { - Text("connect.simulator") - .font(.system(size: 17)) - .foregroundStyle(.fgModerate) - .multilineTextAlignment(.center) - } } #Preview { diff --git a/VoiceAssistant/VoiceAssistant.entitlements b/VoiceAgent/VoiceAgent.entitlements similarity index 100% rename from VoiceAssistant/VoiceAssistant.entitlements rename to VoiceAgent/VoiceAgent.entitlements diff --git a/VoiceAssistant/VoiceAssistant.xcconfig b/VoiceAgent/VoiceAgent.xcconfig similarity index 100% rename from VoiceAssistant/VoiceAssistant.xcconfig rename to VoiceAgent/VoiceAgent.xcconfig diff --git a/VoiceAssistant/VoiceAssistant.xctestplan b/VoiceAgent/VoiceAgent.xctestplan similarity index 67% rename from VoiceAssistant/VoiceAssistant.xctestplan rename to VoiceAgent/VoiceAgent.xctestplan index bff04ac..9df3f9b 100644 --- a/VoiceAssistant/VoiceAssistant.xctestplan +++ b/VoiceAgent/VoiceAgent.xctestplan @@ -10,17 +10,17 @@ ], "defaultOptions" : { "targetForVariableExpansion" : { - "containerPath" : "container:VoiceAssistant.xcodeproj", + "containerPath" : "container:VoiceAgent.xcodeproj", "identifier" : "B5B5E3B12D124AE00099C9BE", - "name" : "VoiceAssistant" + "name" : "VoiceAgent" } }, "testTargets" : [ { "target" : { - "containerPath" : "container:VoiceAssistant.xcodeproj", + "containerPath" : "container:VoiceAgent.xcodeproj", "identifier" : "ACC280262DEDDA1D0023C137", - "name" : "VoiceAssistantTests" + "name" : "VoiceAgentTests" } } ], diff --git a/VoiceAssistant/VoiceAssistantApp.swift b/VoiceAgent/VoiceAgentApp.swift similarity index 96% rename from VoiceAssistant/VoiceAssistantApp.swift rename to VoiceAgent/VoiceAgentApp.swift index d612dfd..54a5ed1 100644 --- a/VoiceAssistant/VoiceAssistantApp.swift +++ b/VoiceAgent/VoiceAgentApp.swift @@ -2,7 +2,7 @@ import LiveKit import SwiftUI @main -struct VoiceAssistantApp: App { +struct VoiceAgentApp: App { // Create the root view model private let viewModel = AppViewModel() diff --git a/VoiceAssistantTests/ChatViewModelTests.swift b/VoiceAgentTests/ChatViewModelTests.swift similarity index 98% rename from VoiceAssistantTests/ChatViewModelTests.swift rename to VoiceAgentTests/ChatViewModelTests.swift index a74b4a7..a7f3ad7 100644 --- a/VoiceAssistantTests/ChatViewModelTests.swift +++ b/VoiceAgentTests/ChatViewModelTests.swift @@ -1,5 +1,5 @@ import Testing -@testable import VoiceAssistant +@testable import VoiceAgent @MainActor struct ChatViewModelTests { diff --git a/taskfile.yaml b/taskfile.yaml index bcbe63a..277afef 100644 --- a/taskfile.yaml +++ b/taskfile.yaml @@ -2,13 +2,13 @@ version: "3" output: interleaved vars: - env_file: "VoiceAssistant/.env.xcconfig" - env_example: "VoiceAssistant/.env.example.xcconfig" + env_file: "VoiceAgent/.env.xcconfig" + env_example: "VoiceAgent/.env.example.xcconfig" tasks: post_create: desc: "Runs after this template is instantiated as a Sandbox or Bootstrap" cmds: - - echo -e "\nYour Swift voice assistant is ready to go!\n" - - echo -e "To give it a try, open the VoiceAssistant.xcodeproj file in Xcode and run the app." + - echo -e "\nYour Swift Voice Agent is ready to go!\n" + - echo -e "To give it a try, open the VoiceAgent.xcodeproj file in Xcode and run the app." - echo -e "\nFor more help view your project's README.md file or join our Slack community at https://livekit.io/join-slack." \ No newline at end of file