diff --git a/.github/workflows/ios-browserstack.yml b/.github/workflows/ios-browserstack.yml index 58c44f87..c239eaa9 100644 --- a/.github/workflows/ios-browserstack.yml +++ b/.github/workflows/ios-browserstack.yml @@ -35,18 +35,12 @@ jobs: - run: pip3 install requests - - name: Install Cocoapods - run: gem install cocoapods - - name: Make build dir run: mkdir ddp - name: Copy test_resources run: ./copy_test_resources.sh - - name: Run Cocoapods - run: pod install - - name: Inject AccessKey run: sed -i '.bak' 's:{TESTING_ACCESS_KEY_HERE}:${{secrets.PV_VALID_ACCESS_KEY}}:' EagleAppTestUITests/BaseTest.swift @@ -54,7 +48,7 @@ jobs: - name: XCode Build run: xcrun xcodebuild build-for-testing -configuration Debug - -workspace EagleAppTest.xcworkspace + -project EagleAppTest.xcodeproj -sdk iphoneos -scheme EagleAppTest -derivedDataPath ddp diff --git a/.github/workflows/ios-demos.yml b/.github/workflows/ios-demos.yml index 580a0740..94b29795 100644 --- a/.github/workflows/ios-demos.yml +++ b/.github/workflows/ios-demos.yml @@ -25,24 +25,10 @@ jobs: - name: Checkout uses: actions/checkout@v3 - - name: Set up Node.js LTS - uses: actions/setup-node@v3 - with: - node-version: lts/* - - - name: Install Cocoapods - run: gem install cocoapods - - - name: Make build dir - run: mkdir ddp - - - name: Run Cocoapods - run: pod install - - name: Build run: xcrun xcodebuild build -configuration Debug - -workspace EagleDemo.xcworkspace + -project EagleDemo.xcodeproj -sdk iphoneos -scheme EagleDemo -derivedDataPath ddp diff --git a/.github/workflows/ios-perf.yml b/.github/workflows/ios-perf.yml index d5e0cb4a..f3aee616 100644 --- a/.github/workflows/ios-perf.yml +++ b/.github/workflows/ios-perf.yml @@ -43,18 +43,12 @@ jobs: - run: pip3 install requests - - name: Install Cocoapods - run: gem install cocoapods - - name: Make build dir run: mkdir ddp - name: Copy test_resources run: ./copy_test_resources.sh - - name: Run Cocoapods - run: pod install - - name: Inject AccessKey run: sed -i '.bak' 's:{TESTING_ACCESS_KEY_HERE}:${{secrets.PV_VALID_ACCESS_KEY}}:' PerformanceTest/PerformanceTest.swift @@ -76,7 +70,7 @@ jobs: - name: XCode Build run: xcrun xcodebuild build-for-testing -configuration Debug - -workspace EagleAppTest.xcworkspace + -project EagleAppTest.xcodeproj -sdk iphoneos -scheme PerformanceTest -derivedDataPath ddp diff --git a/.gitignore b/.gitignore index 3d725761..19b7d62c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .DS_Store -.idea \ No newline at end of file +.idea +.build +Package.resolved +.swiftpm \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 6fecd5fe..b3e6a2dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "demo/c/pvrecorder"] path = demo/c/pvrecorder - url = ../../Picovoice/pvrecorder.git + url = https://github.com/Picovoice/pvrecorder.git [submodule "demo/c/dr_libs"] path = demo/c/dr_libs - url = ../../mackron/dr_libs.git + url = https://github.com/mackron/dr_libs.git diff --git a/Package.swift b/Package.swift new file mode 100644 index 00000000..4af73480 --- /dev/null +++ b/Package.swift @@ -0,0 +1,39 @@ +// swift-tools-version:5.3 +import PackageDescription +let package = Package( + name: "Eagle-iOS", + platforms: [ + .iOS(.v13) + ], + products: [ + .library( + name: "Eagle", + targets: ["Eagle"] + ) + ], + targets: [ + .binaryTarget( + name: "PvEagle", + path: "lib/ios/PvEagle.xcframework" + ), + .target( + name: "Eagle", + dependencies: ["PvEagle"], + path: ".", + exclude: [ + "binding/ios/EagleAppTest", + "demo" + ], + sources: [ + "binding/ios/Eagle.swift", + "binding/ios/EagleBase.swift", + "binding/ios/EagleErrors.swift", + "binding/ios/EagleProfile.swift", + "binding/ios/EagleProfiler.swift" + ], + resources: [ + .copy("lib/common/eagle_params.pv") + ] + ) + ] +) diff --git a/binding/ios/Eagle-iOS.podspec b/binding/ios/Eagle-iOS.podspec index 97968e37..f9dbfb67 100644 --- a/binding/ios/Eagle-iOS.podspec +++ b/binding/ios/Eagle-iOS.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'Eagle-iOS' s.module_name = 'Eagle' - s.version = '1.0.1' + s.version = '1.0.2' s.license = {:type => 'Apache 2.0'} s.summary = 'iOS binding for Picovoice\'s Eagle speaker recognition engine' s.description = @@ -11,11 +11,15 @@ Pod::Spec.new do |s| DESC s.homepage = 'https://github.com/Picovoice/eagle/tree/master/binding/ios' s.author = { 'Picovoice' => 'hello@picovoice.ai' } - s.source = { :git => "https://github.com/Picovoice/eagle.git", :tag => "Eagle-iOS-v1.0.1" } + s.source = { :git => "https://github.com/Picovoice/eagle.git", :tag => s.version.to_s } s.ios.deployment_target = '13.0' s.swift_version = '5.0' s.vendored_frameworks = 'lib/ios/PvEagle.xcframework' - s.resources = 'lib/common/eagle_params.pv' + s.resource_bundles = { + 'EagleResources' => [ + 'lib/common/eagle_params.pv' + ] + } s.source_files = 'binding/ios/*.{swift}' s.exclude_files = 'binding/ios/EagleTestApp/**' end diff --git a/binding/ios/Eagle.swift b/binding/ios/Eagle.swift index 6a6fa94e..7d7273ac 100644 --- a/binding/ios/Eagle.swift +++ b/binding/ios/Eagle.swift @@ -1,5 +1,5 @@ // -// Copyright 2023 Picovoice Inc. +// Copyright 2023-2024 Picovoice Inc. // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" // file accompanying this source. // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on @@ -37,11 +37,9 @@ public class Eagle: EagleBase { var modelPathArg = modelPath if modelPath == nil { - let bundle = Bundle(for: type(of: self)) - - modelPathArg = bundle.path(forResource: "eagle_params", ofType: "pv") + modelPathArg = EagleBase.resourceBundle.path(forResource: "eagle_params", ofType: "pv") if modelPathArg == nil { - throw EagleIOError("Could not retrieve default model from app bundle") + throw EagleIOError("Could not find default model file in app bundle.") } } diff --git a/binding/ios/EagleAppTest/EagleAppTest.xcodeproj/project.pbxproj b/binding/ios/EagleAppTest/EagleAppTest.xcodeproj/project.pbxproj index 5449c7f9..e816b3ec 100644 --- a/binding/ios/EagleAppTest/EagleAppTest.xcodeproj/project.pbxproj +++ b/binding/ios/EagleAppTest/EagleAppTest.xcodeproj/project.pbxproj @@ -7,7 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 0C9CABF209E5A01CD81A64DF /* libPods-EagleAppTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C58FC7EC00CCF3F459941BE /* libPods-EagleAppTest.a */; }; 1E0064C827CEEA65006FF6E9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0064C727CEEA65006FF6E9 /* AppDelegate.swift */; }; 1E0064CC27CEEA65006FF6E9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0064CB27CEEA65006FF6E9 /* ViewController.swift */; }; 1E0064CF27CEEA65006FF6E9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1E0064CD27CEEA65006FF6E9 /* Main.storyboard */; }; @@ -15,12 +14,13 @@ 1E0064D427CEEA66006FF6E9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1E0064D227CEEA66006FF6E9 /* LaunchScreen.storyboard */; }; 1E00654627CFF260006FF6E9 /* EagleAppTestUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0064E827CEEA66006FF6E9 /* EagleAppTestUITests.swift */; }; 1E5B7B042800D9BE00F8BDDB /* PerformanceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E5B7B032800D9BE00F8BDDB /* PerformanceTest.swift */; }; - 36696536961DE78B3E1C06FA /* BuildFile in Frameworks */ = {isa = PBXBuildFile; }; - B5578FCC2C91E2880B64023D /* libPods-PerformanceTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0266BA901685246D8B0A5A8E /* libPods-PerformanceTest.a */; }; + 36696536961DE78B3E1C06FA /* (null) in Frameworks */ = {isa = PBXBuildFile; }; C757D8CD28A1C77100F391C8 /* BaseTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C757D8CB28A1C77100F391C8 /* BaseTest.swift */; }; C790A6FB2A2012C40074FF7A /* audio_samples in Resources */ = {isa = PBXBuildFile; fileRef = C790A6FA2A2012C40074FF7A /* audio_samples */; }; C790A6FC2A2012C40074FF7A /* audio_samples in Resources */ = {isa = PBXBuildFile; fileRef = C790A6FA2A2012C40074FF7A /* audio_samples */; }; - C9A7477B92B84DFDA3D46BCC /* libPods-EagleAppTestUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E0875EEE3C08E7ABFB3CE195 /* libPods-EagleAppTestUITests.a */; }; + E173C8FE2CFE43C900A0903C /* Eagle in Frameworks */ = {isa = PBXBuildFile; productRef = E173C8FD2CFE43C900A0903C /* Eagle */; }; + E173C9002CFE43CF00A0903C /* Eagle in Frameworks */ = {isa = PBXBuildFile; productRef = E173C8FF2CFE43CF00A0903C /* Eagle */; }; + E173C9022CFE43D400A0903C /* Eagle in Frameworks */ = {isa = PBXBuildFile; productRef = E173C9012CFE43D400A0903C /* Eagle */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -41,8 +41,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ - 0139DD3D81100CAF5527B67E /* Pods-EagleAppTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EagleAppTest.release.xcconfig"; path = "Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest.release.xcconfig"; sourceTree = ""; }; - 0266BA901685246D8B0A5A8E /* libPods-PerformanceTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PerformanceTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 1E0064C427CEEA65006FF6E9 /* EagleAppTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EagleAppTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1E0064C727CEEA65006FF6E9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 1E0064CB27CEEA65006FF6E9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -56,15 +54,8 @@ 1E5B7B012800D98500F8BDDB /* PerformanceTest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PerformanceTest.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 1E5B7B032800D9BE00F8BDDB /* PerformanceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTest.swift; sourceTree = ""; }; 1E5B7B052800D9D700F8BDDB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 5C58FC7EC00CCF3F459941BE /* libPods-EagleAppTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-EagleAppTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 6EB8824CCA02EB6A5E079227 /* Pods-PerformanceTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PerformanceTest.release.xcconfig"; path = "Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest.release.xcconfig"; sourceTree = ""; }; - 9082B0B6F167EBBD33004F85 /* Pods-PerformanceTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PerformanceTest.debug.xcconfig"; path = "Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest.debug.xcconfig"; sourceTree = ""; }; - A92C05187F9FBEC390A20E53 /* Pods-EagleAppTestUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EagleAppTestUITests.release.xcconfig"; path = "Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests.release.xcconfig"; sourceTree = ""; }; C757D8CB28A1C77100F391C8 /* BaseTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTest.swift; sourceTree = ""; }; C790A6FA2A2012C40074FF7A /* audio_samples */ = {isa = PBXFileReference; lastKnownFileType = folder; name = audio_samples; path = ../../../../resources/audio_samples; sourceTree = ""; }; - E0875EEE3C08E7ABFB3CE195 /* libPods-EagleAppTestUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-EagleAppTestUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - E0F3D3494A166F1FF0176680 /* Pods-EagleAppTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EagleAppTest.debug.xcconfig"; path = "Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest.debug.xcconfig"; sourceTree = ""; }; - FA141C9AFB42E164A6D11CD6 /* Pods-EagleAppTestUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EagleAppTestUITests.debug.xcconfig"; path = "Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -72,7 +63,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 0C9CABF209E5A01CD81A64DF /* libPods-EagleAppTest.a in Frameworks */, + E173C8FE2CFE43C900A0903C /* Eagle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,8 +71,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 36696536961DE78B3E1C06FA /* BuildFile in Frameworks */, - C9A7477B92B84DFDA3D46BCC /* libPods-EagleAppTestUITests.a in Frameworks */, + 36696536961DE78B3E1C06FA /* (null) in Frameworks */, + E173C9002CFE43CF00A0903C /* Eagle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -89,7 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B5578FCC2C91E2880B64023D /* libPods-PerformanceTest.a in Frameworks */, + E173C9022CFE43D400A0903C /* Eagle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -102,9 +93,8 @@ 1E5B7B022800D9A500F8BDDB /* PerformanceTest */, 1E0064C627CEEA65006FF6E9 /* EagleAppTest */, 1E0064E727CEEA66006FF6E9 /* EagleAppTestUITests */, + E173C8FC2CFE43C900A0903C /* Frameworks */, 1E0064C527CEEA65006FF6E9 /* Products */, - 5F0339EBC9CCA36F768D5A82 /* Pods */, - F9945CFAF9D610B157AB37A1 /* Frameworks */, ); sourceTree = ""; }; @@ -151,26 +141,9 @@ path = PerformanceTest; sourceTree = ""; }; - 5F0339EBC9CCA36F768D5A82 /* Pods */ = { + E173C8FC2CFE43C900A0903C /* Frameworks */ = { isa = PBXGroup; children = ( - E0F3D3494A166F1FF0176680 /* Pods-EagleAppTest.debug.xcconfig */, - 0139DD3D81100CAF5527B67E /* Pods-EagleAppTest.release.xcconfig */, - FA141C9AFB42E164A6D11CD6 /* Pods-EagleAppTestUITests.debug.xcconfig */, - A92C05187F9FBEC390A20E53 /* Pods-EagleAppTestUITests.release.xcconfig */, - 9082B0B6F167EBBD33004F85 /* Pods-PerformanceTest.debug.xcconfig */, - 6EB8824CCA02EB6A5E079227 /* Pods-PerformanceTest.release.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - F9945CFAF9D610B157AB37A1 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 5C58FC7EC00CCF3F459941BE /* libPods-EagleAppTest.a */, - E0875EEE3C08E7ABFB3CE195 /* libPods-EagleAppTestUITests.a */, - 0266BA901685246D8B0A5A8E /* libPods-PerformanceTest.a */, ); name = Frameworks; sourceTree = ""; @@ -182,12 +155,9 @@ isa = PBXNativeTarget; buildConfigurationList = 1E0064EE27CEEA66006FF6E9 /* Build configuration list for PBXNativeTarget "EagleAppTest" */; buildPhases = ( - 6649FEB82A39C11FBC0A2460 /* [CP] Check Pods Manifest.lock */, 1E0064C027CEEA65006FF6E9 /* Sources */, 1E0064C127CEEA65006FF6E9 /* Frameworks */, 1E0064C227CEEA65006FF6E9 /* Resources */, - 03C134DA7D12274947FEBE28 /* [CP] Embed Pods Frameworks */, - E75A2A1845803BE9AC414999 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -202,12 +172,9 @@ isa = PBXNativeTarget; buildConfigurationList = 1E00650827CFEC95006FF6E9 /* Build configuration list for PBXNativeTarget "EagleAppTestUITests" */; buildPhases = ( - 93A24FE350D0E3A0F1A38251 /* [CP] Check Pods Manifest.lock */, 1E0064FA27CFEC95006FF6E9 /* Sources */, 1E0064FB27CFEC95006FF6E9 /* Frameworks */, 1E0064FC27CFEC95006FF6E9 /* Resources */, - AB6D27F280DE3E731D48F34B /* [CP] Embed Pods Frameworks */, - 583BF0730F8A63CF3A3E1AAE /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -223,12 +190,9 @@ isa = PBXNativeTarget; buildConfigurationList = 1E5B7AFE2800D98500F8BDDB /* Build configuration list for PBXNativeTarget "PerformanceTest" */; buildPhases = ( - 9CF96D89E292F3CADD25024E /* [CP] Check Pods Manifest.lock */, 1E5B7AF62800D98500F8BDDB /* Sources */, 1E5B7AF82800D98500F8BDDB /* Frameworks */, 1E5B7AFA2800D98500F8BDDB /* Resources */, - 499664F25C76562B3D346CFF /* [CP] Embed Pods Frameworks */, - 46B8E788C8C12F7F6B2FFDA3 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -272,6 +236,9 @@ Base, ); mainGroup = 1E0064BB27CEEA65006FF6E9; + packageReferences = ( + E1F352FC2D00E9790069B0E6 /* XCRemoteSwiftPackageReference "eagle" */, + ); productRefGroup = 1E0064C527CEEA65006FF6E9 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -312,177 +279,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 03C134DA7D12274947FEBE28 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 46B8E788C8C12F7F6B2FFDA3 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 499664F25C76562B3D346CFF /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PerformanceTest/Pods-PerformanceTest-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - 583BF0730F8A63CF3A3E1AAE /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 6649FEB82A39C11FBC0A2460 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-EagleAppTest-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 93A24FE350D0E3A0F1A38251 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-EagleAppTestUITests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 9CF96D89E292F3CADD25024E /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PerformanceTest-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - AB6D27F280DE3E731D48F34B /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-EagleAppTestUITests/Pods-EagleAppTestUITests-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - E75A2A1845803BE9AC414999 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-EagleAppTest/Pods-EagleAppTest-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 1E0064C027CEEA65006FF6E9 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -661,7 +457,6 @@ }; 1E0064EF27CEEA66006FF6E9 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E0F3D3494A166F1FF0176680 /* Pods-EagleAppTest.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -691,7 +486,6 @@ }; 1E0064F027CEEA66006FF6E9 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 0139DD3D81100CAF5527B67E /* Pods-EagleAppTest.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -721,7 +515,6 @@ }; 1E00650627CFEC95006FF6E9 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FA141C9AFB42E164A6D11CD6 /* Pods-EagleAppTestUITests.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -746,7 +539,6 @@ }; 1E00650727CFEC95006FF6E9 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A92C05187F9FBEC390A20E53 /* Pods-EagleAppTestUITests.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -770,7 +562,6 @@ }; 1E5B7AFF2800D98500F8BDDB /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 9082B0B6F167EBBD33004F85 /* Pods-PerformanceTest.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; @@ -797,7 +588,6 @@ }; 1E5B7B002800D98500F8BDDB /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 6EB8824CCA02EB6A5E079227 /* Pods-PerformanceTest.release.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; @@ -860,6 +650,42 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCLocalSwiftPackageReference section */ + E173C8FB2CFE40F000A0903C /* XCLocalSwiftPackageReference "../../../../eagle" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = ../../../../eagle; + }; +/* End XCLocalSwiftPackageReference section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E1F352FC2D00E9790069B0E6 /* XCRemoteSwiftPackageReference "eagle" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Picovoice/eagle"; + requirement = { + kind = exactVersion; + version = 1.0.2; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E173C8FD2CFE43C900A0903C /* Eagle */ = { + isa = XCSwiftPackageProductDependency; + package = E173C8FB2CFE40F000A0903C /* XCLocalSwiftPackageReference "../../../../eagle" */; + productName = Eagle; + }; + E173C8FF2CFE43CF00A0903C /* Eagle */ = { + isa = XCSwiftPackageProductDependency; + package = E173C8FB2CFE40F000A0903C /* XCLocalSwiftPackageReference "../../../../eagle" */; + productName = Eagle; + }; + E173C9012CFE43D400A0903C /* Eagle */ = { + isa = XCSwiftPackageProductDependency; + package = E173C8FB2CFE40F000A0903C /* XCLocalSwiftPackageReference "../../../../eagle" */; + productName = Eagle; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 1E0064BC27CEEA65006FF6E9 /* Project object */; } diff --git a/binding/ios/EagleAppTest/EagleAppTest.xcworkspace/contents.xcworkspacedata b/binding/ios/EagleAppTest/EagleAppTest.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 9a2caa01..00000000 --- a/binding/ios/EagleAppTest/EagleAppTest.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/binding/ios/EagleAppTest/EagleAppTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/binding/ios/EagleAppTest/EagleAppTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/binding/ios/EagleAppTest/EagleAppTest.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/binding/ios/EagleAppTest/Podfile b/binding/ios/EagleAppTest/Podfile deleted file mode 100644 index 35eeddbb..00000000 --- a/binding/ios/EagleAppTest/Podfile +++ /dev/null @@ -1,14 +0,0 @@ -source 'https://cdn.cocoapods.org/' -platform :ios, '13.0' - -target 'EagleAppTest' do - pod 'Eagle-iOS', '~> 1.0.1' -end - -target 'EagleAppTestUITests' do - pod 'Eagle-iOS', '~> 1.0.1' -end - -target 'PerformanceTest' do - pod 'Eagle-iOS', '~> 1.0.1' -end diff --git a/binding/ios/EagleAppTest/Podfile.lock b/binding/ios/EagleAppTest/Podfile.lock deleted file mode 100644 index 4f51c678..00000000 --- a/binding/ios/EagleAppTest/Podfile.lock +++ /dev/null @@ -1,16 +0,0 @@ -PODS: - - Eagle-iOS (1.0.1) - -DEPENDENCIES: - - Eagle-iOS (~> 1.0.1) - -SPEC REPOS: - trunk: - - Eagle-iOS - -SPEC CHECKSUMS: - Eagle-iOS: 998181609f97849b323023c3ed3d3fbb369ece2a - -PODFILE CHECKSUM: 32252195e8a4e7b7e29caf8a647533d7585f75e0 - -COCOAPODS: 1.16.2 diff --git a/binding/ios/EagleBase.swift b/binding/ios/EagleBase.swift index 330659f2..c99d784f 100644 --- a/binding/ios/EagleBase.swift +++ b/binding/ios/EagleBase.swift @@ -1,8 +1,34 @@ +import Foundation import PvEagle /// Base class providing shared utilities and functions for Eagle and EagleProfiler public class EagleBase { +#if SWIFT_PACKAGE + + static let resourceBundle = Bundle.module + +#else + + static let resourceBundle: Bundle = { + let myBundle = Bundle(for: EagleBase.self) + + guard let resourceBundleURL = myBundle.url( + forResource: "EagleResources", withExtension: "bundle") + else { + fatalError("EagleResources.bundle not found") + } + + guard let resourceBundle = Bundle(url: resourceBundleURL) + else { + fatalError("Could not open EagleResources.bundle") + } + + return resourceBundle + }() + +#endif + /// Required audio sample rate for PCM data public static let sampleRate = Int(pv_sample_rate()) diff --git a/binding/ios/EagleErrors.swift b/binding/ios/EagleErrors.swift index b0332ec2..3deab627 100644 --- a/binding/ios/EagleErrors.swift +++ b/binding/ios/EagleErrors.swift @@ -1,5 +1,5 @@ // -// Copyright 2023 Picovoice Inc. +// Copyright 2023-2024 Picovoice Inc. // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" // file accompanying this source. // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on @@ -7,6 +7,8 @@ // specific language governing permissions and limitations under the License. // +import Foundation + public class EagleError: LocalizedError { private let message: String private let messageStack: [String] diff --git a/binding/ios/EagleProfiler.swift b/binding/ios/EagleProfiler.swift index 7b797fce..88883b68 100644 --- a/binding/ios/EagleProfiler.swift +++ b/binding/ios/EagleProfiler.swift @@ -1,5 +1,5 @@ // -// Copyright 2023 Picovoice Inc. +// Copyright 2023-2024 Picovoice Inc. // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" // file accompanying this source. // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on @@ -39,11 +39,9 @@ public class EagleProfiler: EagleBase { var modelPathArg = modelPath if modelPath == nil { - let bundle = Bundle(for: type(of: self)) - - modelPathArg = bundle.path(forResource: "eagle_params", ofType: "pv") + modelPathArg = EagleBase.resourceBundle.path(forResource: "eagle_params", ofType: "pv") if modelPathArg == nil { - throw EagleIOError("Could not retrieve default model from app bundle") + throw EagleIOError("Could not find default model file in app bundle.") } } diff --git a/binding/ios/README.md b/binding/ios/README.md index aec3586a..c91584f3 100644 --- a/binding/ios/README.md +++ b/binding/ios/README.md @@ -19,9 +19,15 @@ Eagle is an on-device speaker recognition engine. Eagle is: ## Installation -The Eagle iOS binding is available via [Cocoapods](https://cocoapods.org/pods/Eagle-iOS). To import it into your iOS project, add the following line to your Podfile: +The Eagle iOS binding is available via [Swift Package Manager](https://www.swift.org/documentation/package-manager/) or [CocoaPods](https://cocoapods.org/pods/Eagle-iOS). +To import the package using SPM, open up your project's Package Dependencies in XCode and add: +``` +https://github.com/Picovoice/eagle.git +``` +To import it into your iOS project using CocoaPods, add the following line to your Podfile: + ```ruby pod 'Eagle-iOS' ``` @@ -122,4 +128,4 @@ eagle.delete() ## Running Unit Tests -Copy your `AccessKey` into the `accessKey` variable in [`EagleAppTestUITests.swift`](EagleAppTest/EagleAppTestUITests/EagleAppTestUITests.swift). Open `EagleAppTest.xcworkspace` with XCode and run the tests with `Product > Test`. +Copy your `AccessKey` into the `accessKey` variable in [`EagleAppTestUITests.swift`](EagleAppTest/EagleAppTestUITests/EagleAppTestUITests.swift). Open [`EagleAppTest.xcodeproj`](EagleAppTest/EagleAppTest.xcodeproj) with XCode and run the tests with `Product > Test`. diff --git a/demo/ios/EagleDemo/EagleDemo.xcodeproj/project.pbxproj b/demo/ios/EagleDemo/EagleDemo.xcodeproj/project.pbxproj index bd3beabd..d67f36d2 100644 --- a/demo/ios/EagleDemo/EagleDemo.xcodeproj/project.pbxproj +++ b/demo/ios/EagleDemo/EagleDemo.xcodeproj/project.pbxproj @@ -10,8 +10,10 @@ 02447A8D26D94F75004A6CE9 /* EagleDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02447A8C26D94F75004A6CE9 /* EagleDemoApp.swift */; }; 02447A8F26D94F75004A6CE9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02447A8E26D94F75004A6CE9 /* ContentView.swift */; }; 02447A9126D94F7C004A6CE9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02447A9026D94F7C004A6CE9 /* Assets.xcassets */; }; - 34A93097536D8E96854DFF7C /* libPods-EagleDemo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 283588A68587C97CF5AD71FD /* libPods-EagleDemo.a */; }; C76A41602703B17000DB57D6 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C76A415F2703B17000DB57D6 /* ViewModel.swift */; }; + E19F4D222CF51CB20097F2C5 /* Eagle in Frameworks */ = {isa = PBXBuildFile; productRef = E19F4D212CF51CB20097F2C5 /* Eagle */; }; + E19F4D252CF51CC40097F2C5 /* ios_voice_processor in Frameworks */ = {isa = PBXBuildFile; productRef = E19F4D242CF51CC40097F2C5 /* ios_voice_processor */; }; + E1F352FB2D00E8E30069B0E6 /* Eagle in Frameworks */ = {isa = PBXBuildFile; productRef = E1F352FA2D00E8E30069B0E6 /* Eagle */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -20,10 +22,7 @@ 02447A8E26D94F75004A6CE9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 02447A9026D94F7C004A6CE9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 02447A9526D94F7C004A6CE9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 283588A68587C97CF5AD71FD /* libPods-EagleDemo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-EagleDemo.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 43F120214085720BE256298F /* Pods-EagleDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EagleDemo.release.xcconfig"; path = "Target Support Files/Pods-EagleDemo/Pods-EagleDemo.release.xcconfig"; sourceTree = ""; }; C76A415F2703B17000DB57D6 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; - E99243D0A19EBD6589E2BFD9 /* Pods-EagleDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-EagleDemo.debug.xcconfig"; path = "Target Support Files/Pods-EagleDemo/Pods-EagleDemo.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -31,7 +30,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 34A93097536D8E96854DFF7C /* libPods-EagleDemo.a in Frameworks */, + E1F352FB2D00E8E30069B0E6 /* Eagle in Frameworks */, + E19F4D252CF51CC40097F2C5 /* ios_voice_processor in Frameworks */, + E19F4D222CF51CB20097F2C5 /* Eagle in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -43,8 +44,6 @@ children = ( 02447A8B26D94F75004A6CE9 /* EagleDemo */, 02447A8A26D94F75004A6CE9 /* Products */, - E50F2B7FDAD796B5FEA16357 /* Frameworks */, - C5C197030F9F54593E7ED0A3 /* Pods */, ); sourceTree = ""; }; @@ -68,23 +67,6 @@ path = EagleDemo; sourceTree = ""; }; - C5C197030F9F54593E7ED0A3 /* Pods */ = { - isa = PBXGroup; - children = ( - E99243D0A19EBD6589E2BFD9 /* Pods-EagleDemo.debug.xcconfig */, - 43F120214085720BE256298F /* Pods-EagleDemo.release.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - E50F2B7FDAD796B5FEA16357 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 283588A68587C97CF5AD71FD /* libPods-EagleDemo.a */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -92,12 +74,9 @@ isa = PBXNativeTarget; buildConfigurationList = 02447AAE26D94F7D004A6CE9 /* Build configuration list for PBXNativeTarget "EagleDemo" */; buildPhases = ( - D357EEA0DF40D8BF4377767F /* [CP] Check Pods Manifest.lock */, 02447A8526D94F75004A6CE9 /* Sources */, 02447A8626D94F75004A6CE9 /* Frameworks */, 02447A8726D94F75004A6CE9 /* Resources */, - 1088AA63DC0E9AE1413D3A47 /* [CP] Embed Pods Frameworks */, - 01B200502015B72FD61A637A /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -131,6 +110,10 @@ Base, ); mainGroup = 02447A8026D94F74004A6CE9; + packageReferences = ( + E19F4D232CF51CC40097F2C5 /* XCRemoteSwiftPackageReference "ios-voice-processor" */, + E1F352F92D00E8E30069B0E6 /* XCRemoteSwiftPackageReference "eagle" */, + ); productRefGroup = 02447A8A26D94F75004A6CE9 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -151,65 +134,6 @@ }; /* End PBXResourcesBuildPhase section */ -/* Begin PBXShellScriptBuildPhase section */ - 01B200502015B72FD61A637A /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleDemo/Pods-EagleDemo-resources-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Copy Pods Resources"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleDemo/Pods-EagleDemo-resources-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-EagleDemo/Pods-EagleDemo-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - 1088AA63DC0E9AE1413D3A47 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleDemo/Pods-EagleDemo-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-EagleDemo/Pods-EagleDemo-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-EagleDemo/Pods-EagleDemo-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - D357EEA0DF40D8BF4377767F /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-EagleDemo-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 02447A8526D94F75004A6CE9 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -342,7 +266,6 @@ }; 02447AAF26D94F7D004A6CE9 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E99243D0A19EBD6589E2BFD9 /* Pods-EagleDemo.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -365,7 +288,6 @@ }; 02447AB026D94F7D004A6CE9 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 43F120214085720BE256298F /* Pods-EagleDemo.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -408,6 +330,42 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E19F4D232CF51CC40097F2C5 /* XCRemoteSwiftPackageReference "ios-voice-processor" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Picovoice/ios-voice-processor"; + requirement = { + kind = exactVersion; + version = 1.2.0; + }; + }; + E1F352F92D00E8E30069B0E6 /* XCRemoteSwiftPackageReference "eagle" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Picovoice/eagle"; + requirement = { + kind = exactVersion; + version = 1.0.2; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E19F4D212CF51CB20097F2C5 /* Eagle */ = { + isa = XCSwiftPackageProductDependency; + productName = Eagle; + }; + E19F4D242CF51CC40097F2C5 /* ios_voice_processor */ = { + isa = XCSwiftPackageProductDependency; + package = E19F4D232CF51CC40097F2C5 /* XCRemoteSwiftPackageReference "ios-voice-processor" */; + productName = ios_voice_processor; + }; + E1F352FA2D00E8E30069B0E6 /* Eagle */ = { + isa = XCSwiftPackageProductDependency; + package = E1F352F92D00E8E30069B0E6 /* XCRemoteSwiftPackageReference "eagle" */; + productName = Eagle; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 02447A8126D94F74004A6CE9 /* Project object */; } diff --git a/demo/ios/EagleDemo/EagleDemo.xcworkspace/contents.xcworkspacedata b/demo/ios/EagleDemo/EagleDemo.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 8f1aa7b2..00000000 --- a/demo/ios/EagleDemo/EagleDemo.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/demo/ios/EagleDemo/EagleDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/demo/ios/EagleDemo/EagleDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d98100..00000000 --- a/demo/ios/EagleDemo/EagleDemo.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/demo/ios/EagleDemo/EagleDemo/ContentView.swift b/demo/ios/EagleDemo/EagleDemo/ContentView.swift index 91217515..cff17cfa 100644 --- a/demo/ios/EagleDemo/EagleDemo/ContentView.swift +++ b/demo/ios/EagleDemo/EagleDemo/ContentView.swift @@ -105,7 +105,7 @@ struct ContentView: View { Button( action: { if viewModel.state == UIState.TESTING { - viewModel.stopTest() + try? viewModel.stopTest() } else { try? viewModel.test() } diff --git a/demo/ios/EagleDemo/EagleDemo/ViewModel.swift b/demo/ios/EagleDemo/EagleDemo/ViewModel.swift index bf6c8cee..f588994e 100644 --- a/demo/ios/EagleDemo/EagleDemo/ViewModel.swift +++ b/demo/ios/EagleDemo/EagleDemo/ViewModel.swift @@ -1,5 +1,5 @@ // -// Copyright 2023 Picovoice Inc. +// Copyright 2023-2024 Picovoice Inc. // You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE" // file accompanying this source. // Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on @@ -23,6 +23,11 @@ class ViewModel: ObservableObject { private let accessKey = "{YOUR_ACCESS_KEY_HERE}" + private var frameListener: VoiceProcessorFrameListener? + private var testListener: VoiceProcessorFrameListener? + private var errorListener: VoiceProcessorErrorListener? + private var testErrorListener: VoiceProcessorErrorListener? + private var eagleProfiler: EagleProfiler! private var eagle: Eagle! @@ -44,7 +49,7 @@ class ViewModel: ObservableObject { try? stopEnroll() } if self.state == UIState.TESTING { - stopTest() + try? stopTest() } self.profiles = [] self.scores = [] @@ -84,33 +89,7 @@ class ViewModel: ObservableObject { } catch is EagleActivationThrottledError { errorMessage = "AccessKey is throttled" } catch { - errorMessage = "\(error)" - } - } - - private func enrollAudioCallback(pcm: [Int16]) { - enrollPcmBuffer.append(contentsOf: pcm) - do { - let minEnrollSamples = try eagleProfiler.minEnrollSamples() - if enrollPcmBuffer.count >= minEnrollSamples { - let enrollFrame = Array(enrollPcmBuffer[0..= 100.0 { - try stopEnroll(export: true) - } - - DispatchQueue.main.async { - self.enrollPercentage = percentage - self.setEnrollFeedback(feedback: feedback) - } - - try appendToDumpFile(pcm: enrollFrame) - } - } catch { - self.errorMessage = "Failed to process pcm frames for enrollment." - try? self.stopEnroll() + errorMessage = "\(error.localizedDescription)" } } @@ -118,22 +97,65 @@ class ViewModel: ObservableObject { state = UIState.ENROLLING initProfiler() - guard try VoiceProcessor.shared.hasPermissions() else { - print("Permissions denied.") - return - } + self.errorListener = VoiceProcessorErrorListener({ error in + self.errorMessage = "\(error)" + }) + self.frameListener = VoiceProcessorFrameListener({ pcm in + guard let eagleProfiler = self.eagleProfiler else { + return + } + self.enrollPcmBuffer.append(contentsOf: pcm) + do { + let minEnrollSamples = try eagleProfiler.minEnrollSamples() + if self.enrollPcmBuffer.count >= minEnrollSamples { + let enrollFrame = Array(self.enrollPcmBuffer[0..= 100.0 { + try self.stopEnroll(export: true) + } + + DispatchQueue.main.async { + self.enrollPercentage = percentage + self.setEnrollFeedback(feedback: feedback) + } + + try self.appendToDumpFile(pcm: enrollFrame) + } + } catch { + self.errorMessage = "Failed to process pcm frames for enrollment." + try? self.stopEnroll() + } + }) + + VoiceProcessor.instance.addErrorListener(self.errorListener!) + VoiceProcessor.instance.addFrameListener(self.frameListener!) enrollPercentage = 0.0 enrollFeedback = "" - try VoiceProcessor.shared.start( - frameLength: UInt32(512), - sampleRate: UInt32(EagleProfiler.sampleRate), - audioCallback: self.enrollAudioCallback) + do { + try VoiceProcessor.instance.start( + frameLength: UInt32(512), + sampleRate: UInt32(EagleProfiler.sampleRate)) + } catch { + errorMessage = "\(error)" + } } private func stopEnroll(export: Bool = false) throws { - VoiceProcessor.shared.stop() + VoiceProcessor.instance.removeErrorListener(errorListener!) + VoiceProcessor.instance.removeFrameListener(frameListener!) + + if VoiceProcessor.instance.numFrameListeners == 0 { + do { + try VoiceProcessor.instance.stop() + } catch { + throw EagleError(error.localizedDescription) + } + } + if export == true { let newProfile = try eagleProfiler.export() DispatchQueue.main.async { @@ -160,6 +182,24 @@ class ViewModel: ObservableObject { try eagle = Eagle(accessKey: accessKey, speakerProfiles: profiles) statusText = "" + self.testErrorListener = VoiceProcessorErrorListener({ error in + self.errorMessage = "\(error)" + }) + self.testListener = VoiceProcessorFrameListener({ pcm in + do { + let profileScores = try self.eagle.process(pcm: pcm) + + DispatchQueue.main.async { + self.scores = profileScores + } + + try self.appendToDumpFile(pcm: pcm) + } catch { + self.errorMessage = "Failed to process pcm frames for enrollment." + try? self.stopTest() + } + }) + try createDumpFile(filename: "test_dump.pcm") } catch let error as EagleInvalidArgumentError { errorMessage = "\(error.localizedDescription)" @@ -176,40 +216,34 @@ class ViewModel: ObservableObject { } } - private func testAudioCallback(pcm: [Int16]) { - do { - let profileScores = try eagle.process(pcm: pcm) + public func test() throws { + state = UIState.TESTING + initEagle() - DispatchQueue.main.async { - self.scores = profileScores - } + VoiceProcessor.instance.addErrorListener(testErrorListener!) + VoiceProcessor.instance.addFrameListener(testListener!) - try appendToDumpFile(pcm: pcm) + do { + try VoiceProcessor.instance.start( + frameLength: UInt32(Eagle.frameLength), + sampleRate: UInt32(Eagle.sampleRate)) } catch { - self.errorMessage = "Failed to process pcm frames for enrollment." - self.stopTest() + throw EagleError(error.localizedDescription) } } - public func test() throws { - state = UIState.TESTING - initEagle() + public func stopTest() throws { + VoiceProcessor.instance.removeErrorListener(testErrorListener!) + VoiceProcessor.instance.removeFrameListener(testListener!) - guard try VoiceProcessor.shared.hasPermissions() else { - print("Permissions denied.") - self.errorMessage = "App does not have microphone permissions" - return + if VoiceProcessor.instance.numFrameListeners == 0 { + do { + try VoiceProcessor.instance.stop() + } catch { + throw EagleError(error.localizedDescription) + } } - try VoiceProcessor.shared.start( - frameLength: UInt32(Eagle.frameLength), - sampleRate: UInt32(Eagle.sampleRate), - audioCallback: self.testAudioCallback) - } - - public func stopTest() { - VoiceProcessor.shared.stop() - if eagle != nil { eagle.delete() eagle = nil diff --git a/demo/ios/EagleDemo/Podfile b/demo/ios/EagleDemo/Podfile deleted file mode 100644 index 695e9da5..00000000 --- a/demo/ios/EagleDemo/Podfile +++ /dev/null @@ -1,7 +0,0 @@ -source 'https://cdn.cocoapods.org/' -platform :ios, '14.0' - -target 'EagleDemo' do - pod 'Eagle-iOS', '~> 1.0.1' - pod 'ios-voice-processor', '~> 1.0.3' -end diff --git a/demo/ios/EagleDemo/Podfile.lock b/demo/ios/EagleDemo/Podfile.lock deleted file mode 100644 index fd69ee76..00000000 --- a/demo/ios/EagleDemo/Podfile.lock +++ /dev/null @@ -1,20 +0,0 @@ -PODS: - - Eagle-iOS (1.0.1) - - ios-voice-processor (1.0.3) - -DEPENDENCIES: - - Eagle-iOS (~> 1.0.1) - - ios-voice-processor (~> 1.0.3) - -SPEC REPOS: - trunk: - - Eagle-iOS - - ios-voice-processor - -SPEC CHECKSUMS: - Eagle-iOS: 998181609f97849b323023c3ed3d3fbb369ece2a - ios-voice-processor: 65b25a8db69ea25ffba0eeef37bae71a982f34cc - -PODFILE CHECKSUM: 6fdf5a2d8316e8b741e0a468bda00174e72c4499 - -COCOAPODS: 1.16.2 diff --git a/demo/ios/README.md b/demo/ios/README.md index c764b003..c440831b 100644 --- a/demo/ios/README.md +++ b/demo/ios/README.md @@ -8,16 +8,12 @@ Signup or Login to [Picovoice Console](https://console.picovoice.ai/) to get you ## Setup -1. Before building the demo app, run the following from this directory to install the Eagle CocoaPod and other dependencies: -```console -pod install -``` - -2. Replace `"YOUR_ACCESS_KEY_HERE"` inside [`ViewModel.swift`](EagleDemo/EagleDemo/ViewModel.swift) with +Replace `{YOUR_ACCESS_KEY_HERE}` inside [`ViewModel.swift`](EagleDemo/EagleDemo/ViewModel.swift) with your AccessKey obtained from [Picovoice Console](https://console.picovoice.ai/). ## Usage -Open the Eag;eDemo Xcode project (.xcworkspace) and build. Launch the demo on a simulator or a physical iOS device. + +Open the EagleDemo Xcode project ([.xcodeproj](EagleDemo/EagleDemo.xcodeproj/)) and build. Launch the demo on a simulator or a physical iOS device. 1. Press the enroll button. 2. Start talking. Continue talking until the enrollment process reaches 100%. diff --git a/lib/ios/PvEagle.xcframework/Info.plist b/lib/ios/PvEagle.xcframework/Info.plist index 312fed1c..b4e9a2f5 100644 --- a/lib/ios/PvEagle.xcframework/Info.plist +++ b/lib/ios/PvEagle.xcframework/Info.plist @@ -5,31 +5,35 @@ AvailableLibraries + BinaryPath + PvEagle.framework/PvEagle LibraryIdentifier - ios-arm64_x86_64-simulator + ios-arm64 LibraryPath PvEagle.framework SupportedArchitectures arm64 - x86_64 SupportedPlatform ios - SupportedPlatformVariant - simulator + BinaryPath + PvEagle.framework/PvEagle LibraryIdentifier - ios-arm64 + ios-arm64_x86_64-simulator LibraryPath PvEagle.framework SupportedArchitectures arm64 + x86_64 SupportedPlatform ios + SupportedPlatformVariant + simulator CFBundlePackageType diff --git a/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/Info.plist b/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/Info.plist index 2f3ae59c..17ba73b0 100644 Binary files a/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/Info.plist and b/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/Info.plist differ diff --git a/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/PvEagle b/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/PvEagle index ec29f1e7..15d71c67 100755 Binary files a/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/PvEagle and b/lib/ios/PvEagle.xcframework/ios-arm64/PvEagle.framework/PvEagle differ diff --git a/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/Info.plist b/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/Info.plist index 6ea8523b..7f009012 100644 Binary files a/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/Info.plist and b/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/Info.plist differ diff --git a/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/PvEagle b/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/PvEagle index 3bb4a961..4d915140 100755 Binary files a/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/PvEagle and b/lib/ios/PvEagle.xcframework/ios-arm64_x86_64-simulator/PvEagle.framework/PvEagle differ diff --git a/resources/.lint/spell-check/dict.txt b/resources/.lint/spell-check/dict.txt index 2609cc7f..9df15f00 100644 --- a/resources/.lint/spell-check/dict.txt +++ b/resources/.lint/spell-check/dict.txt @@ -30,6 +30,7 @@ wavefile wchars wfopen xcframework +xcodeproj xcuitest xcworkspace malloc