diff --git a/.gitignore b/.gitignore index ebff283b..a54ac8ba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore +.DS_Store ## Build generated build/ diff --git a/Podfile b/Podfile index 76035f67..3c1c92b7 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,15 @@ source 'https://github.com/twilio/cocoapod-specs' -target 'VideoQuickStart' do - pod 'TwilioVideo', '1.0.0-beta1' +workspace 'VideoQuickStart' + +abstract_target 'TwilioVideo' do + pod 'TwilioVideo', '1.0.0-beta2' + + target 'VideoQuickStart' do + project 'VideoQuickStart.xcproject' + end + + target 'VideoCallKitQuickStart' do + project 'VideoCallKitQuickStart.xcproject' + end end diff --git a/VideoCallKitQuickStart.xcodeproj/project.pbxproj b/VideoCallKitQuickStart.xcodeproj/project.pbxproj new file mode 100644 index 00000000..d7dd1f3c --- /dev/null +++ b/VideoCallKitQuickStart.xcodeproj/project.pbxproj @@ -0,0 +1,409 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 24166E171DD3AE910004C634 /* ViewController+SimulateIncomingCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24166E161DD3AE910004C634 /* ViewController+SimulateIncomingCall.swift */; }; + 24166E191DD3B0970004C634 /* ViewController+CallKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24166E181DD3B0970004C634 /* ViewController+CallKit.swift */; }; + 304A80C11C23350000CB8C03 /* libc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80C01C23350000CB8C03 /* libc++.tbd */; }; + 304A80C31C23350500CB8C03 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80C21C23350500CB8C03 /* AudioToolbox.framework */; }; + 304A80C51C23350A00CB8C03 /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80C41C23350A00CB8C03 /* VideoToolbox.framework */; }; + 304A80C71C23351000CB8C03 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80C61C23351000CB8C03 /* AVFoundation.framework */; }; + 304A80C91C23351600CB8C03 /* CoreTelephony.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80C81C23351600CB8C03 /* CoreTelephony.framework */; }; + 304A80CB1C23351B00CB8C03 /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80CA1C23351B00CB8C03 /* GLKit.framework */; }; + 304A80CD1C23352100CB8C03 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80CC1C23352100CB8C03 /* CoreMedia.framework */; }; + 304A80CF1C23352700CB8C03 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 304A80CE1C23352700CB8C03 /* SystemConfiguration.framework */; }; + 4BAA74EB1D8B5CBE00239F19 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BAA74EA1D8B5CBE00239F19 /* Utils.swift */; }; + 8B79D46A1C2252A100489AA5 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B79D4691C2252A100489AA5 /* AppDelegate.swift */; }; + 8B79D46C1C2252A100489AA5 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B79D46B1C2252A100489AA5 /* ViewController.swift */; }; + 8B79D46F1C2252A100489AA5 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B79D46D1C2252A100489AA5 /* Main.storyboard */; }; + 8B79D4711C2252A100489AA5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B79D4701C2252A100489AA5 /* Assets.xcassets */; }; + 8B79D4741C2252A100489AA5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8B79D4721C2252A100489AA5 /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 4BAA74E91D8A152A00239F19 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 24166E161DD3AE910004C634 /* ViewController+SimulateIncomingCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ViewController+SimulateIncomingCall.swift"; sourceTree = ""; }; + 24166E181DD3B0970004C634 /* ViewController+CallKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "ViewController+CallKit.swift"; sourceTree = ""; }; + 304A80C01C23350000CB8C03 /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "usr/lib/libc++.tbd"; sourceTree = SDKROOT; }; + 304A80C21C23350500CB8C03 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 304A80C41C23350A00CB8C03 /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; }; + 304A80C61C23351000CB8C03 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; + 304A80C81C23351600CB8C03 /* CoreTelephony.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreTelephony.framework; path = System/Library/Frameworks/CoreTelephony.framework; sourceTree = SDKROOT; }; + 304A80CA1C23351B00CB8C03 /* GLKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GLKit.framework; path = System/Library/Frameworks/GLKit.framework; sourceTree = SDKROOT; }; + 304A80CC1C23352100CB8C03 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; + 304A80CE1C23352700CB8C03 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + 4BAA74EA1D8B5CBE00239F19 /* Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; + 8B79D4661C2252A100489AA5 /* VideoCallKitQuickStart.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VideoCallKitQuickStart.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 8B79D4691C2252A100489AA5 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 8B79D46B1C2252A100489AA5 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 8B79D46E1C2252A100489AA5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 8B79D4701C2252A100489AA5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 8B79D4731C2252A100489AA5 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 8B79D4751C2252A100489AA5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8B79D4631C2252A100489AA5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 304A80CF1C23352700CB8C03 /* SystemConfiguration.framework in Frameworks */, + 304A80CD1C23352100CB8C03 /* CoreMedia.framework in Frameworks */, + 304A80CB1C23351B00CB8C03 /* GLKit.framework in Frameworks */, + 304A80C91C23351600CB8C03 /* CoreTelephony.framework in Frameworks */, + 304A80C71C23351000CB8C03 /* AVFoundation.framework in Frameworks */, + 304A80C51C23350A00CB8C03 /* VideoToolbox.framework in Frameworks */, + 304A80C31C23350500CB8C03 /* AudioToolbox.framework in Frameworks */, + 304A80C11C23350000CB8C03 /* libc++.tbd in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5A82245B3CA8AD8931FA33E9 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 304A80CE1C23352700CB8C03 /* SystemConfiguration.framework */, + 304A80CC1C23352100CB8C03 /* CoreMedia.framework */, + 304A80CA1C23351B00CB8C03 /* GLKit.framework */, + 304A80C81C23351600CB8C03 /* CoreTelephony.framework */, + 304A80C61C23351000CB8C03 /* AVFoundation.framework */, + 304A80C41C23350A00CB8C03 /* VideoToolbox.framework */, + 304A80C21C23350500CB8C03 /* AudioToolbox.framework */, + 304A80C01C23350000CB8C03 /* libc++.tbd */, + ); + name = Frameworks; + sourceTree = ""; + }; + 8B79D45D1C2252A100489AA5 = { + isa = PBXGroup; + children = ( + 8B79D4681C2252A100489AA5 /* VideoCallKitQuickStart */, + 8B79D4671C2252A100489AA5 /* Products */, + 5A82245B3CA8AD8931FA33E9 /* Frameworks */, + ); + sourceTree = ""; + }; + 8B79D4671C2252A100489AA5 /* Products */ = { + isa = PBXGroup; + children = ( + 8B79D4661C2252A100489AA5 /* VideoCallKitQuickStart.app */, + ); + name = Products; + sourceTree = ""; + }; + 8B79D4681C2252A100489AA5 /* VideoCallKitQuickStart */ = { + isa = PBXGroup; + children = ( + 8B99217A1C230E7800DC3BCA /* Source */, + 8B9921791C230E6B00DC3BCA /* Storyboards */, + 8B99217B1C230E8E00DC3BCA /* Supporting Files */, + ); + path = VideoCallKitQuickStart; + sourceTree = ""; + }; + 8B9921791C230E6B00DC3BCA /* Storyboards */ = { + isa = PBXGroup; + children = ( + 8B79D4721C2252A100489AA5 /* LaunchScreen.storyboard */, + 8B79D46D1C2252A100489AA5 /* Main.storyboard */, + ); + name = Storyboards; + sourceTree = ""; + }; + 8B99217A1C230E7800DC3BCA /* Source */ = { + isa = PBXGroup; + children = ( + 8B79D4691C2252A100489AA5 /* AppDelegate.swift */, + 8B79D46B1C2252A100489AA5 /* ViewController.swift */, + 24166E181DD3B0970004C634 /* ViewController+CallKit.swift */, + 24166E161DD3AE910004C634 /* ViewController+SimulateIncomingCall.swift */, + 4BAA74EA1D8B5CBE00239F19 /* Utils.swift */, + ); + name = Source; + sourceTree = ""; + }; + 8B99217B1C230E8E00DC3BCA /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 8B79D4751C2252A100489AA5 /* Info.plist */, + 8B79D4701C2252A100489AA5 /* Assets.xcassets */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8B79D4651C2252A100489AA5 /* VideoCallKitQuickStart */ = { + isa = PBXNativeTarget; + buildConfigurationList = 8B79D4781C2252A100489AA5 /* Build configuration list for PBXNativeTarget "VideoCallKitQuickStart" */; + buildPhases = ( + 8B79D4621C2252A100489AA5 /* Sources */, + 8B79D4631C2252A100489AA5 /* Frameworks */, + 8B79D4641C2252A100489AA5 /* Resources */, + 4BAA74E91D8A152A00239F19 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VideoCallKitQuickStart; + productName = VideoCallKitQuickStart; + productReference = 8B79D4661C2252A100489AA5 /* VideoCallKitQuickStart.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 8B79D45E1C2252A100489AA5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0800; + ORGANIZATIONNAME = Twilio; + TargetAttributes = { + 8B79D4651C2252A100489AA5 = { + CreatedOnToolsVersion = 7.2; + DevelopmentTeam = SX5J6BN2KX; + LastSwiftMigration = 0800; + }; + }; + }; + buildConfigurationList = 8B79D4611C2252A100489AA5 /* Build configuration list for PBXProject "VideoCallKitQuickStart" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 8B79D45D1C2252A100489AA5; + productRefGroup = 8B79D4671C2252A100489AA5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8B79D4651C2252A100489AA5 /* VideoCallKitQuickStart */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8B79D4641C2252A100489AA5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8B79D4741C2252A100489AA5 /* LaunchScreen.storyboard in Resources */, + 8B79D4711C2252A100489AA5 /* Assets.xcassets in Resources */, + 8B79D46F1C2252A100489AA5 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8B79D4621C2252A100489AA5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8B79D46C1C2252A100489AA5 /* ViewController.swift in Sources */, + 24166E171DD3AE910004C634 /* ViewController+SimulateIncomingCall.swift in Sources */, + 8B79D46A1C2252A100489AA5 /* AppDelegate.swift in Sources */, + 4BAA74EB1D8B5CBE00239F19 /* Utils.swift in Sources */, + 24166E191DD3B0970004C634 /* ViewController+CallKit.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 8B79D46D1C2252A100489AA5 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8B79D46E1C2252A100489AA5 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 8B79D4721C2252A100489AA5 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 8B79D4731C2252A100489AA5 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 8B79D4761C2252A100489AA5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 8B79D4771C2252A100489AA5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 8B79D4791C2252A100489AA5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = SX5J6BN2KX; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = VideoCallKitQuickStart/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.twilio.VideoCallKitQuickStart; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Debug; + }; + 8B79D47A1C2252A100489AA5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = SX5J6BN2KX; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)", + ); + INFOPLIST_FILE = VideoCallKitQuickStart/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = "$(inherited)"; + PRODUCT_BUNDLE_IDENTIFIER = com.twilio.VideoCallKitQuickStart; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 3.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 8B79D4611C2252A100489AA5 /* Build configuration list for PBXProject "VideoCallKitQuickStart" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8B79D4761C2252A100489AA5 /* Debug */, + 8B79D4771C2252A100489AA5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 8B79D4781C2252A100489AA5 /* Build configuration list for PBXNativeTarget "VideoCallKitQuickStart" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 8B79D4791C2252A100489AA5 /* Debug */, + 8B79D47A1C2252A100489AA5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 8B79D45E1C2252A100489AA5 /* Project object */; +} diff --git a/VideoCallKitQuickStart.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/VideoCallKitQuickStart.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..8dd016bc --- /dev/null +++ b/VideoCallKitQuickStart.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/VideoCallKitQuickStart/AppDelegate.swift b/VideoCallKitQuickStart/AppDelegate.swift new file mode 100644 index 00000000..3efd7077 --- /dev/null +++ b/VideoCallKitQuickStart/AppDelegate.swift @@ -0,0 +1,42 @@ +// +// AppDelegate.swift +// VideoCallKitQuickStart +// +// Copyright © 2016 Twilio. All rights reserved. +// + +import UIKit +import Intents + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + + return true + } + + func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { + guard let viewController = window?.rootViewController as? ViewController, let interaction = userActivity.interaction else { + return false + } + + var personHandle: INPersonHandle? + + if let startVideoCallIntent = interaction.intent as? INStartVideoCallIntent { + personHandle = startVideoCallIntent.contacts?[0].personHandle + } else if let startAudioCallIntent = interaction.intent as? INStartAudioCallIntent { + personHandle = startAudioCallIntent.contacts?[0].personHandle + } + + if let personHandle = personHandle { + viewController.performStartCallAction(uuid: UUID(), roomName: personHandle.value) + } + + return true + } +} diff --git a/VideoCallKitQuickStart/Assets.xcassets/AppIcon.appiconset/Contents.json b/VideoCallKitQuickStart/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..1d060ed2 --- /dev/null +++ b/VideoCallKitQuickStart/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,93 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/VideoCallKitQuickStart/Assets.xcassets/Contents.json b/VideoCallKitQuickStart/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/VideoCallKitQuickStart/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/VideoCallKitQuickStart/Base.lproj/LaunchScreen.storyboard b/VideoCallKitQuickStart/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..8377a78b --- /dev/null +++ b/VideoCallKitQuickStart/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VideoCallKitQuickStart/Base.lproj/Main.storyboard b/VideoCallKitQuickStart/Base.lproj/Main.storyboard new file mode 100644 index 00000000..026b7213 --- /dev/null +++ b/VideoCallKitQuickStart/Base.lproj/Main.storyboard @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VideoCallKitQuickStart/Info.plist b/VideoCallKitQuickStart/Info.plist new file mode 100644 index 00000000..1b79af9a --- /dev/null +++ b/VideoCallKitQuickStart/Info.plist @@ -0,0 +1,61 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSCameraUsageDescription + ${PRODUCT_NAME} uses your camera to capture video which is shared with other room participants. + NSMicrophoneUsageDescription + ${PRODUCT_NAME} uses your microphone to capture audio which is shared with other room participants. + UIBackgroundModes + + audio + voip + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/VideoCallKitQuickStart/Utils.swift b/VideoCallKitQuickStart/Utils.swift new file mode 100644 index 00000000..365d056e --- /dev/null +++ b/VideoCallKitQuickStart/Utils.swift @@ -0,0 +1,40 @@ +// +// Utils.swift +// VideoCallKitQuickStart +// +// Copyright © 2016 Twilio. All rights reserved. +// + +import Foundation + +// Helper to determine if we're running on simulator or device +struct PlatformUtils { + static let isSimulator: Bool = { + var isSim = false + #if arch(i386) || arch(x86_64) + isSim = true + #endif + return isSim + }() +} + +struct TokenUtils { + static func fetchToken(url : String) throws -> String { + var token: String = "TWILIO_ACCESS_TOKEN" + let requestURL: URL = URL(string: url)! + do { + let data = try Data(contentsOf: requestURL) + do { + let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String:AnyObject] + token = json["token"] as! String + } catch let error as NSError{ + print ("Error with json, error = \(error)") + throw error + } + } catch let error as NSError { + print ("Invalid token url, error = \(error)") + throw error + } + return token + } +} diff --git a/VideoCallKitQuickStart/ViewController+CallKit.swift b/VideoCallKitQuickStart/ViewController+CallKit.swift new file mode 100644 index 00000000..9a8fb51c --- /dev/null +++ b/VideoCallKitQuickStart/ViewController+CallKit.swift @@ -0,0 +1,217 @@ +// +// ViewController+CallKit.swift +// VideoCallKitQuickStart +// +// Copyright © 2016 Twilio. All rights reserved. +// + +import UIKit + +import TwilioVideo +import CallKit + +extension ViewController : CXProviderDelegate { + + func providerDidReset(_ provider: CXProvider) { + logMessage(messageText: "providerDidReset:") + + localMedia?.audioController.stopAudio() + } + + func providerDidBegin(_ provider: CXProvider) { + logMessage(messageText: "providerDidBegin") + } + + func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { + logMessage(messageText: "provider:didActivateAudioSession:") + + localMedia?.audioController.startAudio() + } + + func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { + logMessage(messageText: "provider:didDeactivateAudioSession:") + } + + func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) { + logMessage(messageText: "provider:timedOutPerformingAction:") + } + + func provider(_ provider: CXProvider, perform action: CXStartCallAction) { + logMessage(messageText: "provider:performStartCallAction:") + + localMedia?.audioController.configureAudioSession(.videoChatSpeaker) + + callKitProvider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil) + + performRoomConnect(uuid: action.callUUID, roomName: action.handle.value) + + // Hang on to the action, as we will either fulfill it after we succesfully connect to the room, or fail + // it if there is an error connecting. + pendingAction = action + } + + func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { + logMessage(messageText: "provider:performAnswerCallAction:") + + // NOTE: When using CallKit with VoIP pushes, the workaround from https://forums.developer.apple.com/message/169511 + // suggests configuring audio in the completion block of the `reportNewIncomingCallWithUUID:update:completion:` + // method instead of in `provider:performAnswerCallAction:` per the Speakerbox example. + // localMedia?.audioController.configureAudioSession() + + performRoomConnect(uuid: action.callUUID, roomName: self.roomTextField.text) + + // Hang on to the action, as we will either fulfill it after we succesfully connect to the room, or fail + // it if there is an error connecting. + pendingAction = action + } + + func provider(_ provider: CXProvider, perform action: CXEndCallAction) { + NSLog("provider:performEndCallAction:") + + localMedia?.audioController.stopAudio() + room?.disconnect() + + action.fulfill() + } + + func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) { + NSLog("provier:performSetMutedCallAction:") + toggleMic(sender: self) + action.fulfill() + } + + func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) { + NSLog("provier:performSetHeldCallAction:") + + let cxObserver = callKitCallController.callObserver + let calls = cxObserver.calls + + guard let call = calls.first(where:{$0.uuid == action.callUUID}) else { + action.fail() + return + } + + if call.isOnHold { + holdCall(onHold: false) + } else { + holdCall(onHold: true) + } + + action.fulfill() + } +} + +// MARK: Call Kit Actions +extension ViewController { + + func performStartCallAction(uuid: UUID, roomName: String?) { + let callHandle = CXHandle(type: .generic, value: roomName ?? "") + let startCallAction = CXStartCallAction(call: uuid, handle: callHandle) + let transaction = CXTransaction(action: startCallAction) + + callKitCallController.request(transaction) { error in + if let error = error { + NSLog("StartCallAction transaction request failed: \(error.localizedDescription)") + return + } + + NSLog("StartCallAction transaction request successful") + + let callUpdate = CXCallUpdate() + callUpdate.remoteHandle = callHandle + callUpdate.supportsDTMF = false + callUpdate.supportsHolding = true + callUpdate.supportsGrouping = false + callUpdate.supportsUngrouping = false + callUpdate.hasVideo = true + + self.callKitProvider.reportCall(with: uuid, updated: callUpdate) + } + } + + func reportIncomingCall(uuid: UUID, roomName: String?, completion: ((NSError?) -> Void)? = nil) { + let callHandle = CXHandle(type: .generic, value: roomName ?? "") + + let callUpdate = CXCallUpdate() + callUpdate.remoteHandle = callHandle + callUpdate.supportsDTMF = false + callUpdate.supportsHolding = true + callUpdate.supportsGrouping = false + callUpdate.supportsUngrouping = false + callUpdate.hasVideo = true + + callKitProvider.reportNewIncomingCall(with: uuid, update: callUpdate) { error in + if error == nil { + NSLog("Incoming call successfully reported.") + + // NOTE: CallKit with VoIP push workaround per https://forums.developer.apple.com/message/169511 + self.localMedia?.audioController.configureAudioSession(.videoChatSpeaker) + } else { + NSLog("Failed to report incoming call successfully: \(error?.localizedDescription).") + } + + completion?(error as? NSError) + } + } + + func performEndCallAction(uuid: UUID) { + let endCallAction = CXEndCallAction(call: uuid) + let transaction = CXTransaction(action: endCallAction) + + callKitCallController.request(transaction) { error in + if let error = error { + NSLog("EndCallAction transaction request failed: \(error.localizedDescription).") + return + } + + NSLog("EndCallAction transaction request successful") + } + } + + func performRoomConnect(uuid: UUID, roomName: String?) { + // Configure access token either from server or manually. + // If the default wasn't changed, try fetching from server. + if (accessToken == "TWILIO_ACCESS_TOKEN") { + do { + accessToken = try TokenUtils.fetchToken(url: tokenUrl) + } catch { + let message = "Failed to fetch access token" + logMessage(messageText: message) + return + } + } + + // Create a Client with the access token that we fetched (or hardcoded). + if (client == nil) { + client = TVIVideoClient(token: accessToken) + if (client == nil) { + logMessage(messageText: "Failed to create video client") + return + } + } + + // Prepare local media which we will share with Room Participants. + self.prepareLocalMedia() + + // Preparing the connect options + let connectOptions = TVIConnectOptions { (builder) in + + // Use the local media that we prepared earlier. + builder.localMedia = self.localMedia + + // The name of the Room where the Client will attempt to connect to. Please note that if you pass an empty + // Room `name`, the Client will create one for you. You can get the name or sid from any connected Room. + builder.name = roomName + + // The CallKit UUID to assoicate with this Room. + builder.uuid = uuid + } + + // Connect to the Room using the options we provided. + room = client?.connect(with: connectOptions, delegate: self) + + logMessage(messageText: "Attempting to connect to room \(roomName)") + + self.showRoomUI(inRoom: true) + } +} diff --git a/VideoCallKitQuickStart/ViewController+SimulateIncomingCall.swift b/VideoCallKitQuickStart/ViewController+SimulateIncomingCall.swift new file mode 100644 index 00000000..0067ef77 --- /dev/null +++ b/VideoCallKitQuickStart/ViewController+SimulateIncomingCall.swift @@ -0,0 +1,57 @@ +// +// ViewController+SimulateIncomingCall.swift +// VideoCallKitQuickStart +// +// Copyright © 2016 Twilio. All rights reserved. +// + +import UIKit + +// MARK: Simulate Incoming Call +extension ViewController { + + @IBAction func simulateIncomingCall(sender: AnyObject) { + + let alertController = UIAlertController(title: "Simulate Incoming Call", message: nil, preferredStyle: .alert) + + let okAction = UIAlertAction(title: "OK", style: .default, handler: { alert -> Void in + + let roomNameTextField = alertController.textFields![0] as UITextField + let delayTextField = alertController.textFields![1] as UITextField + + let roomName = roomNameTextField.text + self.roomTextField.text = roomName + + var delay = 5.0 + if let delayString = delayTextField.text, let delayFromString = Double(delayString) { + delay = delayFromString + } + + self.logMessage(messageText: "Simulating Incoming Call for room: \(roomName) after a \(delay) second delay") + + let backgroundTaskIdentifier = UIApplication.shared.beginBackgroundTask(expirationHandler: nil) + DispatchQueue.main.asyncAfter(wallDeadline: DispatchWallTime.now() + delay) { + self.reportIncomingCall(uuid: UUID(), roomName: self.roomTextField.text) { _ in + UIApplication.shared.endBackgroundTask(backgroundTaskIdentifier) + } + } + }) + + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: { + (action : UIAlertAction!) -> Void in + }) + + alertController.addTextField { (textField : UITextField!) -> Void in + textField.placeholder = "Room Name" + } + + alertController.addTextField { (textField : UITextField!) -> Void in + textField.placeholder = "Delay in seconds (defaults is 5)" + } + + alertController.addAction(okAction) + alertController.addAction(cancelAction) + + self.present(alertController, animated: true, completion: nil) + } +} diff --git a/VideoCallKitQuickStart/ViewController.swift b/VideoCallKitQuickStart/ViewController.swift new file mode 100644 index 00000000..afe58b1d --- /dev/null +++ b/VideoCallKitQuickStart/ViewController.swift @@ -0,0 +1,325 @@ +// +// ViewController.swift +// VideoCallKitQuickStart +// +// Copyright © 2016 Twilio. All rights reserved. +// + +import UIKit + +import TwilioVideo +import CallKit + +class ViewController: UIViewController { + + // MARK: View Controller Members + + // Configure access token manually for testing, if desired! Create one manually in the console + // at https://www.twilio.com/user/account/video/dev-tools/testing-tools + var accessToken = "TWILIO_ACCESS_TOKEN" + + // Configure remote URL to fetch token from + var tokenUrl = "http://localhost:8000/token.php" + + // Video SDK components + var client: TVIVideoClient? + var room: TVIRoom? + var localMedia: TVILocalMedia? + var camera: TVICameraCapturer? + var localVideoTrack: TVILocalVideoTrack? + var localAudioTrack: TVILocalAudioTrack? + var participant: TVIParticipant? + + // CallKit components + let callKitProvider:CXProvider + let callKitCallController:CXCallController + var pendingAction:CXCallAction? + + // MARK: UI Element Outlets and handles + @IBOutlet weak var remoteView: UIView! + @IBOutlet weak var previewView: UIView! + @IBOutlet weak var connectButton: UIButton! + @IBOutlet weak var simulateIncomingButton: UIButton! + @IBOutlet weak var disconnectButton: UIButton! + @IBOutlet weak var messageLabel: UILabel! + @IBOutlet weak var roomTextField: UITextField! + @IBOutlet weak var roomLine: UIView! + @IBOutlet weak var roomLabel: UILabel! + @IBOutlet weak var micButton: UIButton! + + required init?(coder aDecoder: NSCoder) { + let configuration = CXProviderConfiguration(localizedName: "CallKit Quickstart") + configuration.maximumCallGroups = 1 + configuration.maximumCallsPerCallGroup = 1 + configuration.supportsVideo = true + if let callKitIcon = UIImage(named: "iconMask80") { + configuration.iconTemplateImageData = UIImagePNGRepresentation(callKitIcon) + } + + callKitProvider = CXProvider(configuration: configuration) + callKitCallController = CXCallController() + + super.init(coder: aDecoder) + + callKitProvider.setDelegate(self, queue: nil) + } + + // MARK: UIViewController + override func viewDidLoad() { + super.viewDidLoad() + + // LocalMedia represents the collection of tracks that we are sending to other Participants from our VideoClient. + localMedia = TVILocalMedia() + + if PlatformUtils.isSimulator { + self.previewView.removeFromSuperview() + } else { + // Preview our local camera track in the local video preview view. + self.startPreview() + } + + // Disconnect and mic button will be displayed when the Client is connected to a Room. + self.disconnectButton.isHidden = true + self.micButton.isHidden = true + + self.roomTextField.delegate = self + + let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.dismissKeyboard)) + self.view.addGestureRecognizer(tap) + } + + // MARK: IBActions + @IBAction func connect(sender: AnyObject) { + performStartCallAction(uuid: UUID.init(), roomName: self.roomTextField.text) + self.dismissKeyboard() + } + + @IBAction func disconnect(sender: AnyObject) { + if let room = room, let uuid = room.uuid { + logMessage(messageText: "Attempting to disconnect from room \(room.name)") + performEndCallAction(uuid: uuid) + } + } + + @IBAction func toggleMic(sender: AnyObject) { + if (self.localAudioTrack != nil) { + self.localAudioTrack?.isEnabled = !(self.localAudioTrack?.isEnabled)! + + // Update the button title + if (self.localAudioTrack?.isEnabled == true) { + self.micButton.setTitle("Mute", for: .normal) + } else { + self.micButton.setTitle("Unmute", for: .normal) + } + } + } + + // MARK: Private + func startPreview() { + if PlatformUtils.isSimulator { + return + } + + // Preview our local camera track in the local video preview view. + camera = TVICameraCapturer() + localVideoTrack = localMedia?.addVideoTrack(true, capturer: camera!) + if (localVideoTrack == nil) { + logMessage(messageText: "Failed to add video track") + } else { + // Attach view to video track for local preview + localVideoTrack!.attach(self.previewView) + + logMessage(messageText: "Video track added to localMedia") + + // We will flip camera on tap. + let tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.flipCamera)) + self.previewView.addGestureRecognizer(tap) + } + } + + func flipCamera() { + if (self.camera?.source == .frontCamera) { + self.camera?.selectSource(.backCameraWide) + } else { + self.camera?.selectSource(.frontCamera) + } + } + + func prepareLocalMedia() { + + // We will offer local audio and video when we connect to room. + + // Adding local audio track to localMedia + if (localAudioTrack == nil) { + localAudioTrack = localMedia?.addAudioTrack(true) + } + + // Adding local video track to localMedia and starting local preview if it is not already started. + if (localMedia?.videoTracks.count == 0) { + self.startPreview() + } + } + + // Update our UI based upon if we are in a Room or not + func showRoomUI(inRoom: Bool) { + self.connectButton.isHidden = inRoom + self.simulateIncomingButton.isHidden = inRoom + self.roomTextField.isHidden = inRoom + self.roomLine.isHidden = inRoom + self.roomLabel.isHidden = inRoom + self.micButton.isHidden = !inRoom + self.disconnectButton.isHidden = !inRoom + UIApplication.shared.isIdleTimerDisabled = inRoom + } + + func dismissKeyboard() { + if (self.roomTextField.isFirstResponder) { + self.roomTextField.resignFirstResponder() + } + } + + func cleanupRemoteParticipant() { + if ((self.participant) != nil) { + if ((self.participant?.media.videoTracks.count)! > 0) { + self.participant?.media.videoTracks[0].detach(self.remoteView) + } + } + self.participant = nil + } + + func logMessage(messageText: String) { + NSLog(messageText) + messageLabel.text = messageText + } + + func holdCall(onHold: Bool) { + localAudioTrack?.isEnabled = !onHold + localVideoTrack?.isEnabled = !onHold + } +} + +// MARK: UITextFieldDelegate +extension ViewController : UITextFieldDelegate { + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + self.connect(sender: textField) + return true + } +} + +// MARK: TVIRoomDelegate +extension ViewController : TVIRoomDelegate { + func didConnect(to room: TVIRoom) { + + // At the moment, this example only supports rendering one Participant at a time. + + logMessage(messageText: "Connected to room \(room.name) as \(room.localParticipant?.identity)") + + if (room.participants.count > 0) { + self.participant = room.participants[0] + self.participant?.delegate = self + } + + let cxObserver = callKitCallController.callObserver + let calls = cxObserver.calls + + // Let the call provider know that the outgoing call has connected + if let uuid = room.uuid, let call = calls.first(where:{$0.uuid == uuid}) { + if call.isOutgoing { + callKitProvider.reportOutgoingCall(with: uuid, connectedAt: nil) + } + } + + // Mark the Start/Answer Call Action as being fullfilled + if let pendingAction = pendingAction { + pendingAction.fulfill() + } + + pendingAction = nil + } + + func room(_ room: TVIRoom, didDisconnectWithError error: Error?) { + logMessage(messageText: "Disconncted from room \(room.name), error = \(error)") + + self.cleanupRemoteParticipant() + self.room = nil + + self.showRoomUI(inRoom: false) + } + + func room(_ room: TVIRoom, didFailToConnectWithError error: Error) { + logMessage(messageText: "Failed to connect to room with error: \(error.localizedDescription)") + + // Mark the Start/Answer Call Action as having failed + if let pendingAction = pendingAction { + pendingAction.fail() + } + + pendingAction = nil + self.room = nil + + self.showRoomUI(inRoom: false) + } + + func room(_ room: TVIRoom, participantDidConnect participant: TVIParticipant) { + if (self.participant == nil) { + self.participant = participant + self.participant?.delegate = self + } + logMessage(messageText: "Room \(room.name), Participant \(participant.identity) connected") + } + + func room(_ room: TVIRoom, participantDidDisconnect participant: TVIParticipant) { + if (self.participant == participant) { + cleanupRemoteParticipant() + } + logMessage(messageText: "Room \(room.name), Participant \(participant.identity) disconnected") + } +} + +// MARK: TVIParticipantDelegate +extension ViewController : TVIParticipantDelegate { + func participant(_ participant: TVIParticipant, addedVideoTrack videoTrack: TVIVideoTrack) { + logMessage(messageText: "Participant \(participant.identity) added video track") + + if (self.participant == participant) { + videoTrack.attach(self.remoteView) + } + } + + func participant(_ participant: TVIParticipant, removedVideoTrack videoTrack: TVIVideoTrack) { + logMessage(messageText: "Participant \(participant.identity) removed video track") + + if (self.participant == participant) { + videoTrack.detach(self.remoteView) + } + } + + func participant(_ participant: TVIParticipant, addedAudioTrack audioTrack: TVIAudioTrack) { + logMessage(messageText: "Participant \(participant.identity) added audio track") + + } + + func participant(_ participant: TVIParticipant, removedAudioTrack audioTrack: TVIAudioTrack) { + logMessage(messageText: "Participant \(participant.identity) removed audio track") + } + + func participant(_ participant: TVIParticipant, enabledTrack track: TVITrack) { + var type = "" + if (track is TVIVideoTrack) { + type = "video" + } else { + type = "audio" + } + logMessage(messageText: "Participant \(participant.identity) enabled \(type) track") + } + + func participant(_ participant: TVIParticipant, disabledTrack track: TVITrack) { + var type = "" + if (track is TVIVideoTrack) { + type = "video" + } else { + type = "audio" + } + logMessage(messageText: "Participant \(participant.identity) disabled \(type) track") + } +}