diff --git a/Conference/Stories/ConferenceCall/ConferenceCallViewController.swift b/Conference/Stories/ConferenceCall/ConferenceCallViewController.swift
index dc3c07e..7bb1e86 100644
--- a/Conference/Stories/ConferenceCall/ConferenceCallViewController.swift
+++ b/Conference/Stories/ConferenceCall/ConferenceCallViewController.swift
@@ -27,21 +27,75 @@ final class ConferenceCallViewController: UIViewController, AudioDeviceSelecting
super.viewDidLoad()
muteButton.state = .initial(model: CallOptionButtonModels.mute)
- muteButton.touchUpHandler = muteHandler(_:)
+ muteButton.touchUpHandler = { [weak self] button in
+ guard let self = self else { return }
+ do {
+ try self.manageConference.mute(!self.muted)
+ self.muted.toggle()
+ button.state = self.muted ? .selected : .normal
+ } catch (let error) {
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ }
+ }
chooseAudioButton.state = .initial(model: CallOptionButtonModels.chooseAudio)
- chooseAudioButton.touchUpHandler = chooseAudioHandler(_:)
+ chooseAudioButton.touchUpHandler = { [weak self] button in
+ self?.showAudioDevicesActionSheet(sourceView: button)
+ }
switchCameraButton.state = .initial(model: CallOptionButtonModels.switchCamera)
- switchCameraButton.touchUpHandler = switchCameraHandler(_:)
+ switchCameraButton.touchUpHandler = { [weak self] _ in
+ self?.manageConference.switchCamera()
+ }
videoButton.state = .initial(model: CallOptionButtonModels.video)
- videoButton.touchUpHandler = videoHandler(_:)
+ videoButton.touchUpHandler = { [weak self] button in
+ guard let self = self else { return }
+ let previousState = button.state
+ button.state = .unavailable
+ self.manageConference.sendVideo(!self.video) { [weak self] error in
+ guard let self = self else { return }
+ if let error = error {
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ button.state = previousState
+ } else {
+ self.video.toggle()
+ self.conferenceView.hideVideoRenderer(!self.video, for: myId)
+ button.state = self.video ? .normal : .selected
+ }
+ }
+ }
exitButton.state = .initial(model: CallOptionButtonModels.exit)
- exitButton.touchUpHandler = exitHandler(_:)
+ exitButton.touchUpHandler = { [weak self] button in
+ guard let self = self else { return }
+ button.state = .unavailable
+ self.leftConference = true
+ self.leaveConference.execute { error in
+ if let error = error {
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ self.leftConference = false
+ button.state = .normal
+ } else {
+ self.dismiss(animated: true)
+ }
+ }
+ }
- manageConference.conferenceDisconnectedHandler = disconnectedHandler(error:)
+ manageConference.conferenceDisconnectedHandler = { [weak self] error in
+ guard let self = self, !self.leftConference else { return }
+ self.leaveConference.execute { error in
+ if let error = error {
+ print("Got an error while leaving conference - \(error.localizedDescription)")
+ }
+ }
+ AlertHelper.showAlert(
+ title: "Disconnected",
+ message: "You've been disconnected \(error != nil ? error!.localizedDescription : "")",
+ actions: [UIAlertAction(title: "Close", style: .default) { _ in self.dismiss(animated: true) }],
+ on: self
+ )
+ }
manageConference.userAddedHandler = conferenceView.addParticipant(withID:displayName:)
manageConference.userUpdatedHandler = conferenceView.updateParticipant(withID:displayName:)
@@ -54,69 +108,6 @@ final class ConferenceCallViewController: UIViewController, AudioDeviceSelecting
conferenceView.updateParticipant(withID: myId, displayName: "\(name ?? "") (you)")
}
- private func disconnectedHandler(error: Error?) {
- if (self.leftConference) { return }
- leaveConference.execute { error in
- if let error = error {
- print("Got an error while leaving conference - \(error.localizedDescription)")
- }
- }
- AlertHelper.showAlert(
- title: "Disconnected",
- message: "You've been disconnected \(error != nil ? error!.localizedDescription : "")",
- actions: [UIAlertAction(title: "Close", style: .default) { _ in self.dismiss(animated: true) }],
- on: self
- )
- }
-
- private func muteHandler(_ button: CallOptionButton) {
- do {
- try manageConference.mute(!muted)
- muted.toggle()
- button.state = muted ? .selected : .normal
- } catch (let error) {
- AlertHelper.showError(message: error.localizedDescription, on: self)
- }
- }
-
- private func chooseAudioHandler(_ button: CallOptionButton) {
- showAudioDevicesActionSheet(sourceView: button)
- }
-
- private func switchCameraHandler(_ button: CallOptionButton) {
- manageConference.switchCamera()
- }
-
- private func videoHandler(_ button: CallOptionButton) {
- let previousState = button.state
- button.state = .unavailable
- manageConference.sendVideo(!video) { [weak self] error in
- guard let self = self else { return }
- if let error = error {
- AlertHelper.showError(message: error.localizedDescription, on: self)
- button.state = previousState
- } else {
- self.video.toggle()
- self.conferenceView.hideVideoRenderer(!self.video, for: myId)
- button.state = self.video ? .normal : .selected
- }
- }
- }
-
- private func exitHandler(_ button: CallOptionButton) {
- button.state = .unavailable
- leftConference = true
- leaveConference.execute { error in
- if let error = error {
- AlertHelper.showError(message: error.localizedDescription, on: self)
- self.leftConference = false
- button.state = .normal
- } else {
- self.dismiss(animated: true)
- }
- }
- }
-
private func makeFormattedString(from device: VIAudioDevice, isCurrent: Bool) -> String {
let formattedString = String(describing: device).replacingOccurrences(of: "VIAudioDevice", with: "")
return isCurrent ? "\(formattedString) (Current)" : formattedString
diff --git a/InAppScreenSharing/AppDelegate.swift b/InAppScreenSharing/AppDelegate.swift
new file mode 100644
index 0000000..5a4f87e
--- /dev/null
+++ b/InAppScreenSharing/AppDelegate.swift
@@ -0,0 +1,52 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import UIKit
+import VoxImplantSDK
+
+fileprivate let client: VIClient = VIClient(delegateQueue: DispatchQueue.main)
+fileprivate let authService: AuthService = AuthService(client)
+fileprivate let callManager: CallManager = CallManager(client, authService)
+fileprivate let storyAssembler: StoryAssembler = StoryAssembler(authService: authService, callManager: callManager)
+
+@UIApplicationMain
+final class AppDelegate: UIResponder, UIApplicationDelegate, Loggable {
+ var window: UIWindow?
+ var appName: String { "InAppScreenSharing" }
+
+ override init() {
+ super.init()
+
+ configureDefaultLogging()
+ }
+
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ UIApplication.shared.isIdleTimerDisabled = true
+
+ window = UIWindow(frame: UIScreen.main.bounds)
+ window?.rootViewController = storyAssembler.login
+ window?.makeKeyAndVisible()
+
+ return true
+ }
+
+ // MARK: - AppLifeCycleDelegate -
+ func applicationWillResignActive(_ application: UIApplication) {
+ (window?.rootViewController?.toppestViewController as? AppLifeCycleDelegate)?.applicationWillResignActive(application)
+ UIApplication.shared.isIdleTimerDisabled = false
+ }
+
+ func applicationDidEnterBackground(_ application: UIApplication) {
+ (window?.rootViewController?.toppestViewController as? AppLifeCycleDelegate)?.applicationDidEnterBackground(application)
+ }
+
+ func applicationWillEnterForeground(_ application: UIApplication) {
+ (window?.rootViewController?.toppestViewController as? AppLifeCycleDelegate)?.applicationWillEnterForeground(application)
+ }
+
+ func applicationDidBecomeActive(_ application: UIApplication) {
+ (window?.rootViewController?.toppestViewController as? AppLifeCycleDelegate)?.applicationDidBecomeActive(application)
+ }
+}
+
diff --git a/InAppScreenSharing/README.md b/InAppScreenSharing/README.md
new file mode 100644
index 0000000..301df10
--- /dev/null
+++ b/InAppScreenSharing/README.md
@@ -0,0 +1,99 @@
+# Voximplant InApp Screen Sharing Demo (iOS)
+
+This demo demonstrates basic in-app screen sharing functionality of the Voximplant iOS SDK. The application supports video calls between this iOS app and other apps that use any Voximplant SDK.
+
+#### Features
+The application is able to:
+- log in to the Voximplant Cloud
+- auto login using access tokens
+- make an video call
+- receive an incoming call
+- switch camera during a call
+- enable/disable video during a call
+- enable/disable screen sharing during a call
+- auto reconnect/relogin
+
+
+## Getting started
+
+To get started, you'll need to [register](https://voximplant.com) a free Voximplant developer account.
+
+You'll need the following:
+- Voximplant application
+- two Voximplant users
+- VoxEngine scenario
+- routing setup
+- VoIP services certificate for push notifications. Follow [this tutorial](https://voximplant.com/docs/references/iossdk/push-notifications-for-ios) to upload the certificate to the Voximplant Control Panel
+
+### Automatic
+We've implemented a special template to enable you to quickly use the demo – just
+install [SDK tutorial](https://manage.voximplant.com/marketplace/sdk_tutorial) from our marketplace:
+
+
+### Manual
+
+You can set up it manually using our [quickstart guide](https://voximplant.com/docs/references/articles/quickstart) and tutorials
+
+#### VoxEngine scenario example:
+ ```
+ require(Modules.PushService);
+ VoxEngine.addEventListener(AppEvents.CallAlerting, (e) => {
+ const newCall = VoxEngine.callUserDirect(
+ e.call,
+ e.destination,
+ e.callerid,
+ e.displayName,
+ null
+ );
+ VoxEngine.easyProcess(e.call, newCall, ()=>{}, true);
+ });
+ ```
+
+## Installing
+
+1. Clone this repo
+
+1. Run `$ pod install` in the repo folder
+
+1. Open the `Swift.xcworkspace` workspace
+
+1. Target InAppScreenSharing and build the project using Xcode
+
+## Usage
+
+### User login
+
+
+Log in using:
+* Voximplant user name in the format `user@app.account`
+* password
+
+See the following classes for code details:
+* [AuthService.swift](Services/AuthService.swift)
+* [LoginViewController.swift](Stories/Login/LoginViewController.swift)
+
+### Make or receive calls
+
+
+Enter a Voximplant user name to the input field and press "Call" button to make a call.
+
+See the following classes for code details:
+- [CallManager.swift](Services/CallManager.swift)
+- [MainViewController.swift](Stories/Main/MainViewController.swift)
+- [IncomingCallViewController.swift](Stories/Main/IncomingCallViewController.swift)
+
+### Call controls
+
+
+Enable/disable video or screen sharing during a call.
+
+See the following classes for code details:
+- [CallViewController.swift](Stories/Call/CallViewController.swift)
+* [CallManager.swift](Services/CallManager.swift)
+
+
+## Useful links
+1. [Getting started](https://voximplant.com/docs/introduction)
+2. [Voximplant iOS SDK reference](https://voximplant.com/docs/references/iossdk)
+3. [Installing the Voximplant iOS SDK](https://voximplant.com/docs/introduction/integration/adding_sdks/installing/ios_sdk)
+4. [HowTo's](https://voximplant.com/docs/howtos)
diff --git a/InAppScreenSharing/Resources/Info.plist b/InAppScreenSharing/Resources/Info.plist
new file mode 100644
index 0000000..8163ac4
--- /dev/null
+++ b/InAppScreenSharing/Resources/Info.plist
@@ -0,0 +1,60 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ LSRequiresIPhoneOS
+
+ NSCameraUsageDescription
+ Camera is needed for video calls
+ NSMicrophoneUsageDescription
+ Microphone is needed for calls
+ UIBackgroundModes
+
+ audio
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UIRequiresFullScreen
+
+ UIStatusBarStyle
+ UIStatusBarStyleLightContent
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeRight
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/InAppScreenSharing/Screenshots/call.png b/InAppScreenSharing/Screenshots/call.png
new file mode 100644
index 0000000..9fa34ec
Binary files /dev/null and b/InAppScreenSharing/Screenshots/call.png differ
diff --git a/InAppScreenSharing/Screenshots/inCall.png b/InAppScreenSharing/Screenshots/inCall.png
new file mode 100644
index 0000000..baa62c8
Binary files /dev/null and b/InAppScreenSharing/Screenshots/inCall.png differ
diff --git a/InAppScreenSharing/Screenshots/login.png b/InAppScreenSharing/Screenshots/login.png
new file mode 100644
index 0000000..fdd2ad8
Binary files /dev/null and b/InAppScreenSharing/Screenshots/login.png differ
diff --git a/ScreenSharing/Screenshots/market.png b/InAppScreenSharing/Screenshots/market.png
similarity index 100%
rename from ScreenSharing/Screenshots/market.png
rename to InAppScreenSharing/Screenshots/market.png
diff --git a/InAppScreenSharing/Services/AuthService.swift b/InAppScreenSharing/Services/AuthService.swift
new file mode 100644
index 0000000..1b7a109
--- /dev/null
+++ b/InAppScreenSharing/Services/AuthService.swift
@@ -0,0 +1,167 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import VoxImplantSDK
+
+final class AuthService: NSObject, VIClientSessionDelegate {
+ private typealias ConnectCompletion = (Error?) -> Void
+ private typealias DisconnectCompletion = () -> Void
+ typealias LoginCompletion = (Error?) -> Void
+ typealias LogoutCompletion = () -> Void
+
+ private let client: VIClient
+ private var connectCompletion: ConnectCompletion?
+ private var disconnectCompletion: DisconnectCompletion?
+ var possibleToLogin: Bool { Tokens.areExist && !Tokens.areExpired }
+
+ init(_ client: VIClient) {
+ self.client = client
+ super.init()
+ client.sessionDelegate = self
+ }
+
+ @UserDefault("lastFullUsername")
+ var loggedInUser: String?
+ var loggedInUserDisplayName: String?
+ var isLoggedIn: Bool { state == .loggedIn }
+ private var state: VIClientState { client.clientState }
+
+ func login(user: String, password: String, _ completion: @escaping LoginCompletion) {
+ connect() { [weak self] error in
+ if let error = error {
+ completion(error)
+ return
+ }
+
+ self?.client.login(withUser: user, password: password,
+ success: { (displayUserName: String, tokens: VIAuthParams) in
+ Tokens.update(with: tokens)
+ self?.loggedInUser = user
+ self?.loggedInUserDisplayName = displayUserName
+ completion(nil)
+ },
+ failure: { (error: Error) in
+ completion(error)
+ }
+ )
+ }
+ }
+
+ func loginWithAccessToken(_ completion: @escaping LoginCompletion) {
+ guard let user = self.loggedInUser else {
+ let error = AuthError.loginDataNotFound
+ completion(error)
+ return
+ }
+
+ if client.clientState == .loggedIn,
+ loggedInUserDisplayName != nil,
+ !Tokens.areExpired
+ {
+ completion(nil)
+ return
+ }
+
+ connect() { [weak self] error in
+ if let error = error {
+ completion(error)
+ return
+ }
+
+ self?.updateAccessTokenIfNeeded(for: user) {
+ [weak self]
+ (result: Result) in
+
+ switch result {
+ case let .failure(error):
+ completion(error)
+ return
+
+ case let .success(accessKey):
+ self?.client.login(withUser: user, token: accessKey.token,
+ success: { (displayUserName: String, tokens: VIAuthParams) in
+ Tokens.update(with: tokens)
+ self?.loggedInUser = user
+ self?.loggedInUserDisplayName = displayUserName
+ completion(nil)
+ },
+ failure: { (error: Error) in
+ completion(error)
+ }
+ )
+ }
+ }
+ }
+ }
+
+ func logout(_ completion: @escaping LogoutCompletion) {
+ Tokens.clear()
+ self.loggedInUser = nil
+ self.loggedInUserDisplayName = nil
+ self.disconnect(completion)
+ }
+
+ private func updateAccessTokenIfNeeded(
+ for user: String,
+ _ completion: @escaping (Result)->Void
+ ) {
+ guard let accessToken = Tokens.access,
+ let refreshToken = Tokens.refresh else {
+ completion(.failure(AuthError.loginDataNotFound))
+ return
+ }
+
+ if accessToken.isExpired {
+ client.refreshToken(withUser: user, token: refreshToken.token)
+ { (authParams: VIAuthParams?, error: Error?) in
+ guard let tokens = authParams
+ else {
+ completion(.failure(error!))
+ return
+ }
+ Tokens.update(with: tokens)
+ completion(.success(Tokens.access!))
+ }
+ } else {
+ completion(.success(accessToken))
+ }
+ }
+
+ private func connect(_ completion: @escaping ConnectCompletion) {
+ if client.clientState == .disconnected ||
+ client.clientState == .connecting
+ {
+ connectCompletion = completion
+ client.connect()
+
+ } else {
+ completion(nil)
+ }
+ }
+
+ private func disconnect(_ completion: @escaping DisconnectCompletion) {
+ if client.clientState == .disconnected {
+ completion()
+ } else {
+ disconnectCompletion = completion
+ client.disconnect()
+ }
+ }
+
+ // MARK: - VIClientSessionDelegate -
+ func clientSessionDidConnect(_ client: VIClient) {
+ connectCompletion?(nil)
+ connectCompletion = nil
+ }
+
+ func client(_ client: VIClient, sessionDidFailConnectWithError error: Error) {
+ connectCompletion?(error)
+ connectCompletion = nil
+ }
+
+ func clientSessionDidDisconnect(_ client: VIClient) {
+ disconnectCompletion?()
+ disconnectCompletion = nil
+ }
+}
diff --git a/InAppScreenSharing/Services/CallManager.swift b/InAppScreenSharing/Services/CallManager.swift
new file mode 100644
index 0000000..5fe2524
--- /dev/null
+++ b/InAppScreenSharing/Services/CallManager.swift
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+ */
+
+import VoxImplantSDK
+
+final class CallManager:
+ NSObject,
+ VIClientCallManagerDelegate,
+ VIAudioManagerDelegate,
+ VICallDelegate,
+ VIEndpointDelegate
+{
+ typealias VideoStreamAdded = (_ local: Bool, (VIVideoRendererView) -> Void) -> Void
+ typealias VideoStreamRemoved = (_ local: Bool) -> Void
+
+ struct CallWrapper {
+ fileprivate let call: VICall
+ let callee: String
+ var displayName: String?
+ var state: CallState = .connecting
+ let direction: CallDirection
+ var sendingVideo: Bool = true
+ var sharingScreen: Bool = false
+
+ enum CallDirection {
+ case incoming
+ case outgoing
+ }
+
+ enum CallState: Equatable {
+ case connecting
+ case connected
+ case ended (reason: CallEndReason)
+
+ enum CallEndReason: Equatable {
+ case disconnected
+ case failed (message: String)
+ }
+ }
+ }
+
+ private let client: VIClient
+ private let authService: AuthService
+
+ // Voximplant SDK supports multiple calls at the same time, however
+ // this demo app demonstrates only one managed call at the moment,
+ // so it rejects new incoming call if there is already a call.
+ private(set) var managedCallWrapper: CallWrapper? {
+ willSet {
+ if managedCallWrapper?.call != newValue?.call {
+ managedCallWrapper?.call.remove(self)
+ }
+ }
+ didSet {
+ if let newValue = managedCallWrapper {
+ callObserver?(newValue)
+ }
+ if managedCallWrapper?.call != oldValue?.call {
+ managedCallWrapper?.call.add(self)
+ }
+ }
+ }
+ var hasManagedCall: Bool { managedCallWrapper != nil }
+ private var hasNoManagedCalls: Bool { !hasManagedCall }
+
+ var callObserver: ((CallWrapper) -> Void)?
+ var didReceiveIncomingCall: (() -> Void)?
+ var videoStreamAddedHandler: VideoStreamAdded?
+ var videoStreamRemovedHandler: VideoStreamRemoved?
+
+ private let callSettings: VICallSettings = {
+ let settings = VICallSettings()
+ settings.videoFlags = VIVideoFlags.videoFlags(receiveVideo: true, sendVideo: true)
+ return settings
+ }()
+
+ required init(_ client: VIClient, _ authService: AuthService) {
+ self.client = client
+ self.authService = authService
+
+ super.init()
+
+ VIAudioManager.shared().delegate = self
+ self.client.callManagerDelegate = self
+ }
+
+ func makeOutgoingCall(to contact: String) throws {
+ guard authService.isLoggedIn else { throw AuthError.notLoggedIn }
+ guard hasNoManagedCalls else { throw CallError.alreadyManagingACall }
+
+ if let call = client.call(contact, settings: callSettings) {
+ managedCallWrapper = CallWrapper(call: call, callee: contact, direction: .outgoing)
+ } else {
+ throw CallError.internalError
+ }
+ }
+
+ func startOutgoingCall() throws {
+ guard let call = managedCallWrapper?.call else { throw CallError.hasNoActiveCall }
+ guard authService.isLoggedIn else { throw AuthError.notLoggedIn }
+
+ if headphonesNotConnected {
+ self.selectIfAvailable(.speaker, from: VIAudioManager.shared().availableAudioDevices())
+ }
+ call.start()
+ }
+
+ func makeIncomingCallActive() throws {
+ guard let call = managedCallWrapper?.call else { throw CallError.hasNoActiveCall }
+ guard authService.isLoggedIn else { throw AuthError.notLoggedIn }
+
+ if headphonesNotConnected {
+ selectIfAvailable(.speaker, from: VIAudioManager.shared().availableAudioDevices())
+ }
+ call.answer(with: callSettings)
+ }
+
+ func changeSendVideo(_ completion: ((Error?) -> Void)? = nil) {
+ guard let wrapper = managedCallWrapper else { completion?(CallError.hasNoActiveCall); return }
+
+ wrapper.call.setSendVideo(!wrapper.sendingVideo) { [weak self] error in
+ if let error = error {
+ completion?(error)
+ return
+ }
+
+ self?.managedCallWrapper?.sendingVideo.toggle()
+ self?.managedCallWrapper?.sharingScreen = false
+ completion?(nil)
+ }
+ }
+
+ func changeShareScreen(_ completion: ((Error?) -> Void)? = nil) {
+ guard let wrapper = managedCallWrapper else { completion?(CallError.hasNoActiveCall); return }
+
+ if wrapper.sharingScreen {
+ wrapper.call.setSendVideo(wrapper.sendingVideo) { [weak self] error in
+ if let error = error {
+ completion?(error)
+ return
+ }
+ self?.managedCallWrapper?.sharingScreen = false
+ }
+ } else {
+ wrapper.call.startInAppScreenSharing { [weak self] error in
+ if let error = error {
+ completion?(error)
+ return
+ }
+ self?.managedCallWrapper?.sharingScreen = true
+ }
+ }
+ }
+
+ func endCall() throws {
+ guard let call = managedCallWrapper?.call else { throw CallError.hasNoActiveCall }
+
+ call.hangup(withHeaders: nil)
+ }
+
+ // MARK: - VIClientCallManagerDelegate -
+ func client(_ client: VIClient,
+ didReceiveIncomingCall call: VICall,
+ withIncomingVideo video: Bool,
+ headers: [AnyHashable: Any]?
+ ) {
+ if hasManagedCall {
+ call.reject(with: .busy, headers: nil)
+ } else {
+ managedCallWrapper = CallWrapper(call: call, callee: call.endpoints.first?.user ?? "", displayName: call.endpoints.first?.userDisplayName, direction: .incoming)
+ didReceiveIncomingCall?()
+ }
+ }
+
+ // MARK: - VICallDelegate -
+ func call(_ call: VICall,
+ didConnectWithHeaders headers: [AnyHashable : Any]?
+ ) {
+ if call.callId == managedCallWrapper?.call.callId {
+ managedCallWrapper?.displayName = call.endpoints.first?.userDisplayName ?? call.endpoints.first?.user
+ managedCallWrapper?.state = .connected
+ }
+ }
+
+ func call(_ call: VICall,
+ didDisconnectWithHeaders headers: [AnyHashable: Any]?,
+ answeredElsewhere: NSNumber
+ ) {
+ if call.callId == managedCallWrapper?.call.callId {
+ managedCallWrapper?.state = .ended(reason: .disconnected)
+ managedCallWrapper = nil
+ }
+ }
+
+ func call(_ call: VICall,
+ didFailWithError error: Error,
+ headers: [AnyHashable : Any]?
+ ) {
+ if call.callId == managedCallWrapper?.call.callId {
+ managedCallWrapper?.state = .ended(reason: .failed(message: error.localizedDescription))
+ managedCallWrapper = nil
+ }
+ }
+
+ func call(_ call: VICall, didAddLocalVideoStream videoStream: VIVideoStream) {
+ if videoStream.type == .screenSharing { return }
+ videoStreamAddedHandler?(true) { renderer in
+ videoStream.addRenderer(renderer)
+ }
+ }
+
+ func call(_ call: VICall, didRemoveLocalVideoStream videoStream: VIVideoStream) {
+ videoStreamRemovedHandler?(true)
+ videoStream.removeAllRenderers()
+ }
+
+ func call(_ call: VICall, didAdd endpoint: VIEndpoint) {
+ endpoint.delegate = self
+ }
+
+ // MARK: - VIEndpointDelegate -
+ func endpoint(_ endpoint: VIEndpoint, didAddRemoteVideoStream videoStream: VIVideoStream) {
+ videoStreamAddedHandler?(false) { renderer in
+ videoStream.addRenderer(renderer)
+ }
+ }
+
+ func endpoint(_ endpoint: VIEndpoint, didRemoveRemoteVideoStream videoStream: VIVideoStream) {
+ videoStreamRemovedHandler?(false)
+ videoStream.removeAllRenderers()
+ }
+
+ // MARK: - VIAudioManagerDelegate -
+ func audioDeviceChanged(_ audioDevice: VIAudioDevice) { }
+
+ func audioDeviceUnavailable(_ audioDevice: VIAudioDevice) { }
+
+ func audioDevicesListChanged(_ availableAudioDevices: Set) {
+ if headphonesNotConnected {
+ selectIfAvailable(.speaker, from: availableAudioDevices)
+ }
+ }
+
+ // MARK: - Private -
+ private var headphonesNotConnected: Bool {
+ !VIAudioManager.shared().availableAudioDevices().contains { $0.type == .wired || $0.type == .bluetooth }
+ }
+
+ private func selectIfAvailable(_ audioDeviceType: VIAudioDeviceType, from audioDevices: Set) {
+ if let device = audioDevices.first(where: { $0.type == audioDeviceType }) {
+ VIAudioManager.shared().select(device)
+ }
+ }
+}
diff --git a/ScreenSharing/Stories/Call/CallVideoView/CallVideoView.swift b/InAppScreenSharing/Stories/Call/CallVideoView/CallVideoView.swift
similarity index 100%
rename from ScreenSharing/Stories/Call/CallVideoView/CallVideoView.swift
rename to InAppScreenSharing/Stories/Call/CallVideoView/CallVideoView.swift
diff --git a/ScreenSharing/Stories/Call/CallVideoView/CallVideoView.xib b/InAppScreenSharing/Stories/Call/CallVideoView/CallVideoView.xib
similarity index 100%
rename from ScreenSharing/Stories/Call/CallVideoView/CallVideoView.xib
rename to InAppScreenSharing/Stories/Call/CallVideoView/CallVideoView.xib
diff --git a/InAppScreenSharing/Stories/Call/CallViewController.swift b/InAppScreenSharing/Stories/Call/CallViewController.swift
new file mode 100644
index 0000000..b309102
--- /dev/null
+++ b/InAppScreenSharing/Stories/Call/CallViewController.swift
@@ -0,0 +1,167 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import UIKit
+import VoxImplantSDK
+import ReplayKit
+
+final class CallViewController:
+ UIViewController,
+ RPScreenRecorderDelegate
+{
+ @IBOutlet private weak var videoButton: CallOptionButton!
+ @IBOutlet private weak var sharingButton: CallOptionButton!
+ @IBOutlet private weak var hangupButton: CallOptionButton!
+ @IBOutlet private weak var localVideoStreamView: CallVideoView!
+ @IBOutlet private weak var magneticView: EdgeMagneticView!
+ @IBOutlet private weak var remoteVideoStreamView: CallVideoView!
+ @IBOutlet private weak var callStateLabel: UILabel!
+
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+ .all
+ }
+
+ var callManager: CallManager! // DI
+ var storyAssembler: StoryAssembler! // DI
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ RPScreenRecorder.shared().delegate = self
+
+ callManager.callObserver = { [weak self] call in
+ guard let self = self else { return }
+ if case .ended (let reason) = call.state {
+ if case .disconnected = reason {
+ self.dismiss(animated: true)
+ }
+ if case .failed (let message) = reason {
+ weak var presentingViewController = self.presentingViewController
+ self.dismiss(animated: true) {
+ presentingViewController?.present(
+ self.storyAssembler.callFailed(
+ callee: call.callee,
+ displayName: call.displayName ?? call.callee,
+ reason: message
+ ),
+ animated: true)
+ }
+ return
+ }
+ }
+
+ self.sharingButton.state = call.state == .connected
+ ? call.sharingScreen ? .selected : .normal
+ : .unavailable
+ self.videoButton.state = call.state == .connected
+ ? call.sendingVideo ? .normal : .selected
+ : .unavailable
+
+ self.localVideoStreamView.streamEnabled = call.sendingVideo && !call.sharingScreen
+ self.callStateLabel.text = call.state == .connected ? "Call in progress" : "Connecting..."
+ }
+
+ videoButton.state = .initial(model: CallOptionButtonModels.camera)
+ videoButton.touchUpHandler = { [weak self] button in
+ Log.d("Changing sendVideo")
+ button.state = .unavailable
+ self?.sharingButton.state = .unavailable
+
+ self?.callManager.changeSendVideo { [weak self] error in
+ if let error = error {
+ Log.e("setSendVideo error \(error.localizedDescription)")
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ }
+ }
+ }
+
+ sharingButton.state = .initial(model: CallOptionButtonModels.screen)
+ sharingButton.touchUpHandler = { [weak self] button in
+ Log.d("Changing sharing")
+ button.state = .unavailable
+ self?.videoButton.state = .unavailable
+ self?.callManager.changeShareScreen { [weak self] error in
+ if let error = error {
+ Log.e("setSharing error \(error.localizedDescription)")
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ }
+ }
+ }
+
+ hangupButton.state = .initial(model: CallOptionButtonModels.hangup)
+ hangupButton.touchUpHandler = { [weak self] button in
+ Log.d("Call hangup called")
+ button.state = .unavailable
+ do {
+ try self?.callManager.endCall()
+ } catch (let error) {
+ Log.e(error.localizedDescription)
+ }
+ self?.dismiss(animated: true)
+ }
+
+ callManager.videoStreamAddedHandler = { [weak self] local, completion in
+ guard let self = self else { return }
+ if local {
+ self.localVideoStreamView.streamEnabled = true
+ completion(VIVideoRendererView(containerView: self.localVideoStreamView.streamView))
+ } else {
+ self.remoteVideoStreamView.streamEnabled = true
+ completion(VIVideoRendererView(containerView: self.remoteVideoStreamView.streamView))
+ }
+ }
+ callManager.videoStreamRemovedHandler = { [weak self] local in
+ (local ? self?.localVideoStreamView : self?.remoteVideoStreamView)?.streamEnabled = false
+ }
+
+ localVideoStreamView.showImage = false
+
+ guard let callDirection = callManager.managedCallWrapper?.direction else {
+ dismiss(animated: true)
+ return
+ }
+
+ do {
+ try callDirection == .outgoing
+ ? callManager.startOutgoingCall()
+ : callManager.makeIncomingCallActive()
+ } catch (let error) {
+ Log.e(" \(callDirection) call start failed with error \(error.localizedDescription)")
+ dismiss(animated: true)
+ }
+ }
+
+ @IBAction private func localVideoStreamTapped(_ sender: UITapGestureRecognizer) {
+ VICameraManager.shared().useBackCamera.toggle()
+ }
+
+ @IBAction private func localVideoStreamDragged(_ sender: UIPanGestureRecognizer) {
+ switch sender.state {
+ case .changed:
+ let translation = sender.translation(in: self.view)
+ localVideoStreamView.center = CGPoint(x: localVideoStreamView.center.x + translation.x,
+ y: localVideoStreamView.center.y + translation.y)
+ sender.setTranslation(CGPoint.zero, in: self.view)
+ case .ended:
+ magneticView.rearrangeInnerView()
+ default:
+ break
+ }
+ }
+
+ // MARK: - RPScreenRecorderDelegate
+ func screenRecorder(
+ _ screenRecorder: RPScreenRecorder,
+ didStopRecordingWith previewViewController: RPPreviewViewController?,
+ error: Error?
+ ) {
+ AlertHelper.showAlert(title: "Broadcast", message: "Broadcast has been ended \(error.debugDescription)", on: self)
+ }
+
+ private enum CallOptionButtonModels {
+ static let screen = CallOptionButtonModel(image: UIImage(named: "screenSharing"), text: "Screen")
+ static let camera = CallOptionButtonModel(image: UIImage(named: "videoOn"), imageSelected: UIImage(named: "videoOff"), text: "Camera")
+ static let hangup = CallOptionButtonModel(image: UIImage(named: "hangup"), imageTint: #colorLiteral(red: 1, green: 0.02352941176, blue: 0.2549019608, alpha: 1), text: "Hangup")
+ }
+}
diff --git a/ScreenSharing/Stories/CallFailed/CallFailedViewController.swift b/InAppScreenSharing/Stories/CallFailed/CallFailedViewController.swift
similarity index 100%
rename from ScreenSharing/Stories/CallFailed/CallFailedViewController.swift
rename to InAppScreenSharing/Stories/CallFailed/CallFailedViewController.swift
diff --git a/ScreenSharing/Stories/IncomingCall/IncomingCallViewController.swift b/InAppScreenSharing/Stories/IncomingCall/IncomingCallViewController.swift
similarity index 76%
rename from ScreenSharing/Stories/IncomingCall/IncomingCallViewController.swift
rename to InAppScreenSharing/Stories/IncomingCall/IncomingCallViewController.swift
index f50463e..7410d17 100644
--- a/ScreenSharing/Stories/IncomingCall/IncomingCallViewController.swift
+++ b/InAppScreenSharing/Stories/IncomingCall/IncomingCallViewController.swift
@@ -29,19 +29,22 @@ final class IncomingCallViewController: UIViewController, ErrorHandling {
incomingCallView.acceptHandler = { [weak self] in
Log.d("Call accepted from incomingCall view")
- PermissionsHelper.requestRecordPermissions(includingVideo: true) { [weak self] error in
- if let error = error {
- self?.handleError(error)
- return
- }
-
- if let self = self {
- weak var presentingViewController = self.presentingViewController
- self.dismiss(animated: true) {
- presentingViewController?.present(self.storyAssembler.call, animated: true)
+ PermissionsHelper.requestRecordPermissions(
+ includingVideo: true,
+ completion: { [weak self] error in
+ if let error = error {
+ self?.handleError(error)
+ return
+ }
+
+ if let self = self {
+ weak var presentingViewController = self.presentingViewController
+ self.dismiss(animated: true) {
+ presentingViewController?.present(self.storyAssembler.call, animated: true)
+ }
}
}
- }
+ )
}
callManager.callObserver = { [weak self] call in
diff --git a/ScreenSharing/Stories/Login/LoginViewController.swift b/InAppScreenSharing/Stories/Login/LoginViewController.swift
similarity index 100%
rename from ScreenSharing/Stories/Login/LoginViewController.swift
rename to InAppScreenSharing/Stories/Login/LoginViewController.swift
diff --git a/InAppScreenSharing/Stories/Main/MainViewController.swift b/InAppScreenSharing/Stories/Main/MainViewController.swift
new file mode 100644
index 0000000..e1e2921
--- /dev/null
+++ b/InAppScreenSharing/Stories/Main/MainViewController.swift
@@ -0,0 +1,113 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import UIKit
+
+final class MainViewController:
+ UIViewController,
+ LoadingShowable,
+ ErrorHandling,
+ AppLifeCycleDelegate
+{
+ @IBOutlet private var mainView: DefaultMainView!
+
+ var authService: AuthService! // DI
+ var callManager: CallManager! // DI
+ var storyAssembler: StoryAssembler! // DI
+
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask { .all }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ if let displayName = authService.loggedInUserDisplayName {
+ mainView.setDisplayName(text: "Logged in as \(displayName)")
+ }
+
+ mainView.callTouchHandler = { username in
+ Log.d("Calling \(String(describing: username))")
+ PermissionsHelper.requestRecordPermissions(
+ includingVideo: true,
+ completion: { [weak self] error in
+ if let error = error {
+ self?.handleError(error)
+ return
+ }
+
+ let beginCall = { [weak self] in
+ guard let self = self else { return }
+ do {
+ try self.callManager.makeOutgoingCall(to: username ?? "")
+ self.view.endEditing(true)
+ self.present(self.storyAssembler.call, animated: true)
+ } catch (let error) {
+ Log.e(error.localizedDescription)
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ }
+ }
+
+ guard let self = self else { return }
+
+ if !self.authService.isLoggedIn {
+ self.reconnect(onSuccess: beginCall)
+ } else {
+ beginCall()
+ }
+ }
+ )
+ }
+
+ mainView.logoutTouchHandler = { [weak self] in
+ self?.authService.logout { [weak self] in
+ self?.dismiss(animated: true)
+ }
+ }
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ callManager.didReceiveIncomingCall = { [weak self] in
+ guard let self = self else { return }
+
+ self.view.endEditing(true)
+ self.present(self.storyAssembler.incomingCall, animated: true)
+ }
+ }
+
+ private func reconnect(onSuccess: (() -> Void)? = nil) {
+ Log.d("Reconnecting")
+ showLoading(title: "Reconnecting", details: "Please wait...")
+ authService.loginWithAccessToken { [weak self] error in
+ guard let self = self else { return }
+ self.hideProgress()
+
+ if let error = error {
+ AlertHelper.showAlert(
+ title: "Connection error",
+ message: error.localizedDescription,
+ actions: [
+ UIAlertAction(title: "Try again", style: .default) {
+ _ in self.reconnect()
+ },
+ UIAlertAction(title: "Logout", style: .destructive) {
+ _ in self.authService.logout { [weak self] in
+ self?.dismiss(animated: true)
+ }
+ },
+ ],
+ defaultAction: false)
+ } else {
+ onSuccess?()
+ }
+ }
+ }
+
+ // MARK: - AppLifeCycleDelegate -
+ func applicationDidBecomeActive(_ application: UIApplication) {
+ if (!authService.isLoggedIn) {
+ reconnect()
+ }
+ }
+}
diff --git a/InAppScreenSharing/Stories/StoryAssembler.swift b/InAppScreenSharing/Stories/StoryAssembler.swift
new file mode 100644
index 0000000..8e4bc8f
--- /dev/null
+++ b/InAppScreenSharing/Stories/StoryAssembler.swift
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+ */
+
+import VoxImplantSDK
+
+final class StoryAssembler {
+ private let authService: AuthService
+ private let callManager: CallManager
+
+ required init(authService: AuthService, callManager: CallManager) {
+ self.authService = authService
+ self.callManager = callManager
+ }
+
+ var login: LoginViewController {
+ let controller = Storyboard.main.instantiateViewController(of: LoginViewController.self)
+ controller.authService = authService
+ controller.storyAssembler = self
+ return controller
+ }
+
+ var main: MainViewController {
+ let controller = Storyboard.main.instantiateViewController(of: MainViewController.self)
+ controller.authService = authService
+ controller.callManager = callManager
+ controller.storyAssembler = self
+ return controller
+ }
+
+ var call: CallViewController {
+ let controller = Storyboard.call.instantiateViewController(of: CallViewController.self)
+ controller.callManager = callManager
+ controller.storyAssembler = self
+ return controller
+ }
+
+ var incomingCall: IncomingCallViewController {
+ let controller = Storyboard.call.instantiateViewController(of: IncomingCallViewController.self)
+ controller.callManager = callManager
+ controller.storyAssembler = self
+ return controller
+ }
+
+ func callFailed(callee: String, displayName: String, reason: String) -> CallFailedViewController {
+ let controller = Storyboard.call.instantiateViewController(of: CallFailedViewController.self)
+ controller.user = callee
+ controller.displayName = displayName
+ controller.failReason = reason
+ controller.callManager = callManager
+ controller.storyAssembler = self
+ return controller
+ }
+}
diff --git a/InAppScreenSharing/Stories/Storyboards/Base.lproj/Main.storyboard b/InAppScreenSharing/Stories/Storyboards/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..9c2eda7
--- /dev/null
+++ b/InAppScreenSharing/Stories/Storyboards/Base.lproj/Main.storyboard
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/InAppScreenSharing/Stories/Storyboards/Call.storyboard b/InAppScreenSharing/Stories/Storyboards/Call.storyboard
new file mode 100644
index 0000000..15d95f7
--- /dev/null
+++ b/InAppScreenSharing/Stories/Storyboards/Call.storyboard
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/InAppScreenSharing/Stories/Storyboards/Storyboard.swift b/InAppScreenSharing/Stories/Storyboards/Storyboard.swift
new file mode 100644
index 0000000..6adfec5
--- /dev/null
+++ b/InAppScreenSharing/Stories/Storyboards/Storyboard.swift
@@ -0,0 +1,10 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import UIKit
+
+enum Storyboard {
+ static let main: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
+ static let call: UIStoryboard = UIStoryboard(name: "Call", bundle: nil)
+}
diff --git a/Podfile b/Podfile
index 50902c4..00bc4c2 100644
--- a/Podfile
+++ b/Podfile
@@ -9,8 +9,8 @@ def common_pods
end
def voximplant
- pod 'CocoaLumberjack/Swift', '~> 3.5'
- pod 'VoxImplantSDK/CocoaLumberjackLogger', '2.32.1'
+ pod 'CocoaLumberjack/Swift', '~> 3.5'
+ pod 'VoxImplantSDK/CocoaLumberjackLogger', '2.33.0'
end
target 'Voximplant Demo' do
@@ -43,6 +43,15 @@ target 'ScreenSharing' do
voximplant
end
+target 'InAppScreenSharing' do
+ common_pods
+ voximplant
+end
+
+target 'ScreenSharingUploadAppex' do
+ voximplant
+end
+
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
diff --git a/README.md b/README.md
index 84725b4..1f45395 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,9 @@ The demo demonstrates [CallKit](https://developer.apple.com/documentation/callki
> VideoCallKit demo application is implemented to meet new requirements. Please use it to test push notification on iOS 13.
## [ScreenSharing demo](ScreenSharing)
+The demo demonstrates basic screen sharing functionality of the Voximplant iOS SDK.
+
+## [InAppScreenSharing demo](InAppScreenSharing)
The demo demonstrates basic in-app screen sharing functionality of the Voximplant iOS SDK.
## [Voximplant demo](VoximplantDemo)
diff --git a/ScreenSharing/AppDelegate.swift b/ScreenSharing/AppDelegate.swift
index 1d6cf2b..ebf8723 100644
--- a/ScreenSharing/AppDelegate.swift
+++ b/ScreenSharing/AppDelegate.swift
@@ -5,9 +5,17 @@
import UIKit
import VoxImplantSDK
+extension UserDefaults {
+ static var main: UserDefaults {
+ // App Group UserDefaults needed for communication between the app and the appex
+ return UserDefaults(suiteName: "group.com.voximplant.demos")!
+ }
+}
+
fileprivate let client: VIClient = VIClient(delegateQueue: DispatchQueue.main)
fileprivate let authService: AuthService = AuthService(client)
-fileprivate let callManager: CallManager = CallManager(client, authService)
+fileprivate let darwinNotificationService = DarwinNotificationCenter()
+fileprivate let callManager: CallManager = CallManager(client, authService, darwinNotificationService)
fileprivate let storyAssembler: StoryAssembler = StoryAssembler(authService: authService, callManager: callManager)
@UIApplicationMain
diff --git a/ScreenSharing/README.md b/ScreenSharing/README.md
index 1d69918..a939679 100644
--- a/ScreenSharing/README.md
+++ b/ScreenSharing/README.md
@@ -1,14 +1,12 @@
-# Voximplant Screen Sharing Kit Demo (iOS)
+# Voximplant Screen Sharing Demo (iOS)
-This demo demonstrates basic in-app screen sharing functionality of the Voximplant iOS SDK. The application supports video calls between this iOS app and other apps that use any Voximplant SDK.
+This demo demonstrates basic screen sharing functionality of the Voximplant iOS SDK. The application supports video calls between this iOS app and other apps that use any Voximplant SDK.
#### Features
The application is able to:
- log in to the Voximplant Cloud
- auto login using access tokens
-- make an video call
-- receive an incoming call
-- switch camera during a call
+- make an video conference call
- enable/disable video during a call
- enable/disable screen sharing during a call
- auto reconnect/relogin
@@ -23,12 +21,6 @@ You'll need the following:
- two Voximplant users
- VoxEngine scenario
- routing setup
-- VoIP services certificate for push notifications. Follow [this tutorial](https://voximplant.com/docs/references/iossdk/push-notifications-for-ios) to upload the certificate to the Voximplant Control Panel
-
-### Automatic
-We've implemented a special template to enable you to quickly use the demo – just
-install [SDK tutorial](https://manage.voximplant.com/marketplace/sdk_tutorial) from our marketplace:
-
### Manual
@@ -36,16 +28,69 @@ You can set up it manually using our [quickstart guide](https://voximplant.com/d
#### VoxEngine scenario example:
```
- require(Modules.PushService);
- VoxEngine.addEventListener(AppEvents.CallAlerting, (e) => {
- const newCall = VoxEngine.callUserDirect(
- e.call,
- e.destination,
- e.callerid,
- e.displayName,
- null
- );
- VoxEngine.easyProcess(e.call, newCall, ()=>{}, true);
+ var MediaStatistic = true;
+ require("conference");
+ require("recorder");
+ var conf;
+ var partsCounter = 0;
+ var recorder;
+ function checkForTermination() {
+ if (partsCounter === 0) {
+ conf.stop();
+ conf = null;
+ setTimeout(VoxEngine.terminate, 2000);
+ }
+ }
+ VoxEngine.addEventListener(AppEvents.Started, function (e) {
+ conf = VoxEngine.createConference({hd_audio:true});
+ conf.addEventListener(ConferenceEvents.Stopped, function(e) {
+ Logger.write("stopped");
+ });
+ conf.addEventListener(ConferenceEvents.Started, function(e) {
+ Logger.write("started id=" + e.conference.getId());
+ });
+ conf.addEventListener(ConferenceEvents.EndpointAdded, function(e) {
+ ++partsCounter;
+ Logger.write("endpoint added pc = " + partsCounter);
+ });
+ conf.addEventListener(ConferenceEvents.EndpointRemoved, function(e) {
+ --partsCounter;
+ Logger.write("endpoint removed pc = " + partsCounter);
+ if (partsCounter == 0) {
+ setTimeout(checkForTermination, 1000*10); // wait for 10 ceconds
+ }
+ });
+ });
+ VoxEngine.addEventListener(AppEvents.CallAlerting, function (e) {
+ e.call.answer();
+ partsCounter = partsCounter + 1;
+ const endpoint = conf.add({
+ call: e.call,
+ mode: "FORWARD",
+ direction: "BOTH", scheme: e.scheme
+ });
+ Logger.write(`New endpoint was added ID: ${endpoint.id}`);
+ const endpoint1 = conf.add({
+ call: e.call,
+ mode: "FORWARD",
+ direction: "BOTH", scheme: e.scheme
+ });
+ Logger.write(`New endpoint was added ID: ${endpoint1.id}`);
+ function checkForTermination() {
+ if (partsCounter === 0) {
+ conf.stop();
+ conf = null;
+ }
+ }
+ function participantDisconnected() {
+ partsCounter = partsCounter - 1;
+ if (partsCounter === 0) {
+ setTimeout(checkForTermination, 1000 * 10); // wait for 10 ceconds
+ }
+ }
+ e.call.addEventListener(CallEvents.Disconnected, function (e) {
+ participantDisconnected();
+ });
});
```
@@ -70,17 +115,16 @@ Log in using:
See the following classes for code details:
* [AuthService.swift](Services/AuthService.swift)
-* [LoginViewController.swift](Stories/Login/LoginViewController.swift)
+* [LoginViewController.swift](Stories/LoginViewController.swift)
-### Make or receive calls
+### Make calls

-Enter a Voximplant user name to the input field and press "Call" button to make a call.
+Enter "myconf" to the input field and press "Call" button to join a conference.
See the following classes for code details:
- [CallManager.swift](Services/CallManager.swift)
-- [MainViewController.swift](Stories/Main/MainViewController.swift)
-- [IncomingCallViewController.swift](Stories/Main/IncomingCallViewController.swift)
+- [MainViewController.swift](Stories/MainViewController.swift)
### Call controls

@@ -88,12 +132,30 @@ See the following classes for code details:
Enable/disable video or screen sharing during a call.
See the following classes for code details:
-- [CallViewController.swift](Stories/Call/CallViewController.swift)
+- [CallViewController.swift](Stories/CallViewController.swift)
* [CallManager.swift](Services/CallManager.swift)
+
+## Broadcasting Architecture
+
+Screen capture happens within an broadcast upload app extension.
+look: [SampleHandler.swift](../ScreenSharingUploadAppex/SampleHandler)
+
+Every time user tries to begin screen sharing, extension receives ‘broadcastStarted’ method call, in which
+app extension will try to auth into Voximplant Cloud
+(using locally stored auth tokens in app group User Defaults) and to begin another call which is used for delivery screen frames.
+
+If an error occurs while doing so, broadcast upload app extension process will be ended and user will be notified about it with an alert.
+
+CFNotificationCenter ([DarwinNotificationsService.swift](Services/DarwinNotificationsService.swift)) is used to send messages between the app and extension.
+
+> Please note, ios app extensions are highly restricted and has only
+50mb ram limit. Due to this reason it is possible only use hardware accelerated encoding (which is h264) and is not possible to use software accelerated (vp8).
+
## Useful links
1. [Getting started](https://voximplant.com/docs/introduction)
2. [Voximplant iOS SDK reference](https://voximplant.com/docs/references/iossdk)
3. [Installing the Voximplant iOS SDK](https://voximplant.com/docs/introduction/integration/adding_sdks/installing/ios_sdk)
4. [HowTo's](https://voximplant.com/docs/howtos)
+5. [About ReplayKit on WWDC](https://developer.apple.com/videos/play/wwdc2018/601/)
diff --git a/ScreenSharing/Resources/Assets.xcassets/Contents.json b/ScreenSharing/Resources/Assets.xcassets/Contents.json
deleted file mode 100644
index 73c0059..0000000
--- a/ScreenSharing/Resources/Assets.xcassets/Contents.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "info" : {
- "author" : "xcode",
- "version" : 1
- }
-}
diff --git a/ScreenSharing/Resources/Info.plist b/ScreenSharing/Resources/Info.plist
index 2b06f11..8163ac4 100644
--- a/ScreenSharing/Resources/Info.plist
+++ b/ScreenSharing/Resources/Info.plist
@@ -36,19 +36,22 @@
armv7
+ UIRequiresFullScreen
+
UIStatusBarStyle
UIStatusBarStyleLightContent
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
UISupportedInterfaceOrientations~ipad
+ UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
diff --git a/ScreenSharing/ScreenSharing.entitlements b/ScreenSharing/ScreenSharing.entitlements
new file mode 100644
index 0000000..9ba9aa6
--- /dev/null
+++ b/ScreenSharing/ScreenSharing.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.com.voximplant.demos
+
+
+
diff --git a/ScreenSharing/Screenshots/call.png b/ScreenSharing/Screenshots/call.png
index 9fa34ec..deea986 100644
Binary files a/ScreenSharing/Screenshots/call.png and b/ScreenSharing/Screenshots/call.png differ
diff --git a/ScreenSharing/Screenshots/inCall.png b/ScreenSharing/Screenshots/inCall.png
index baa62c8..88f82f2 100644
Binary files a/ScreenSharing/Screenshots/inCall.png and b/ScreenSharing/Screenshots/inCall.png differ
diff --git a/ScreenSharing/Services/AuthService.swift b/ScreenSharing/Services/AuthService.swift
index 1b7a109..9c5cd38 100644
--- a/ScreenSharing/Services/AuthService.swift
+++ b/ScreenSharing/Services/AuthService.swift
@@ -4,6 +4,7 @@
import VoxImplantSDK
+// This class is common for ScreenSharing app and ScreenSharingUploadAppex
final class AuthService: NSObject, VIClientSessionDelegate {
private typealias ConnectCompletion = (Error?) -> Void
private typealias DisconnectCompletion = () -> Void
@@ -20,7 +21,7 @@ final class AuthService: NSObject, VIClientSessionDelegate {
super.init()
client.sessionDelegate = self
}
-
+
@UserDefault("lastFullUsername")
var loggedInUser: String?
var loggedInUserDisplayName: String?
@@ -69,11 +70,9 @@ final class AuthService: NSObject, VIClientSessionDelegate {
return
}
- self?.updateAccessTokenIfNeeded(for: user) {
- [weak self]
- (result: Result) in
-
+ self?.updateAccessTokenIfNeeded(for: user) { [weak self] result in
switch result {
+
case let .failure(error):
completion(error)
return
diff --git a/ScreenSharing/Services/CallManager.swift b/ScreenSharing/Services/CallManager.swift
index 5fe2524..7dd1e6b 100644
--- a/ScreenSharing/Services/CallManager.swift
+++ b/ScreenSharing/Services/CallManager.swift
@@ -3,6 +3,9 @@
*/
import VoxImplantSDK
+import ReplayKit
+
+let myId = "me"
final class CallManager:
NSObject,
@@ -11,23 +14,20 @@ final class CallManager:
VICallDelegate,
VIEndpointDelegate
{
- typealias VideoStreamAdded = (_ local: Bool, (VIVideoRendererView) -> Void) -> Void
- typealias VideoStreamRemoved = (_ local: Bool) -> Void
+ typealias UserAdded = (String, String?) -> Void
+ typealias UserUpdated = (String, String?) -> Void
+ typealias UserRemoved = (String) -> Void
+ typealias VideoStreamAdded = (String, (VIVideoRendererView?) -> Void) -> Void
+ typealias VideoStreamRemoved = (String) -> Void
struct CallWrapper {
fileprivate let call: VICall
let callee: String
var displayName: String?
var state: CallState = .connecting
- let direction: CallDirection
var sendingVideo: Bool = true
var sharingScreen: Bool = false
- enum CallDirection {
- case incoming
- case outgoing
- }
-
enum CallState: Equatable {
case connecting
case connected
@@ -42,14 +42,19 @@ final class CallManager:
private let client: VIClient
private let authService: AuthService
+ private let notificationCenter: DarwinNotificationCenter
+
+ @UserDefault("activecall")
+ private var managedCallee: String?
// Voximplant SDK supports multiple calls at the same time, however
// this demo app demonstrates only one managed call at the moment,
// so it rejects new incoming call if there is already a call.
private(set) var managedCallWrapper: CallWrapper? {
- willSet {
+ willSet {
if managedCallWrapper?.call != newValue?.call {
managedCallWrapper?.call.remove(self)
+ self.managedCallee = newValue?.callee
}
}
didSet {
@@ -64,23 +69,58 @@ final class CallManager:
var hasManagedCall: Bool { managedCallWrapper != nil }
private var hasNoManagedCalls: Bool { !hasManagedCall }
+ var endpointAddedHandler: UserAdded?
+ var endpointUpdatedHandler: UserUpdated?
+ var endpointRemovedHandler: UserRemoved?
+ var localVideoStreamAddedHandler: VideoStreamAdded?
+ var localVideoStreamRemovedHandler: VideoStreamRemoved?
+ var remoteVideoStreamAddedHandler: VideoStreamAdded?
+ var remoteVideoStreamRemovedHandler: VideoStreamRemoved?
var callObserver: ((CallWrapper) -> Void)?
- var didReceiveIncomingCall: (() -> Void)?
- var videoStreamAddedHandler: VideoStreamAdded?
- var videoStreamRemovedHandler: VideoStreamRemoved?
private let callSettings: VICallSettings = {
let settings = VICallSettings()
+ settings.preferredVideoCodec = .H264
settings.videoFlags = VIVideoFlags.videoFlags(receiveVideo: true, sendVideo: true)
return settings
}()
- required init(_ client: VIClient, _ authService: AuthService) {
+ init(_ client: VIClient,
+ _ authService: AuthService,
+ _ notificationCenter: DarwinNotificationCenter
+ ) {
self.client = client
self.authService = authService
+ self.notificationCenter = notificationCenter
super.init()
+ self.notificationCenter.registerForNotification(.broadcastEnded)
+ self.notificationCenter.broadcastEndedHandler = {
+ if let call = self.managedCallWrapper, call.sharingScreen {
+ self.managedCallWrapper?.sharingScreen = false
+ }
+ }
+
+ self.notificationCenter.registerForNotification(.broadcastStarted)
+ self.notificationCenter.broadcastStartedHandler = {
+ if let call = self.managedCallWrapper, !call.sharingScreen {
+ self.managedCallWrapper?.sharingScreen = true
+ }
+ }
+
+ self.notificationCenter.registerForNotification(.broadcastCallEnded)
+ self.notificationCenter.broadcastCallEndedHandler = {
+ if let call = self.managedCallWrapper, call.sharingScreen {
+ self.managedCallWrapper?.sharingScreen = false
+ AlertHelper.showAlert(
+ title: "Broadcast",
+ message: "Broadcast has been ended",
+ defaultAction: true
+ )
+ }
+ }
+
VIAudioManager.shared().delegate = self
self.client.callManagerDelegate = self
}
@@ -89,8 +129,8 @@ final class CallManager:
guard authService.isLoggedIn else { throw AuthError.notLoggedIn }
guard hasNoManagedCalls else { throw CallError.alreadyManagingACall }
- if let call = client.call(contact, settings: callSettings) {
- managedCallWrapper = CallWrapper(call: call, callee: contact, direction: .outgoing)
+ if let call = client.callConference(contact, settings: callSettings) {
+ managedCallWrapper = CallWrapper(call: call, callee: contact)
} else {
throw CallError.internalError
}
@@ -106,16 +146,6 @@ final class CallManager:
call.start()
}
- func makeIncomingCallActive() throws {
- guard let call = managedCallWrapper?.call else { throw CallError.hasNoActiveCall }
- guard authService.isLoggedIn else { throw AuthError.notLoggedIn }
-
- if headphonesNotConnected {
- selectIfAvailable(.speaker, from: VIAudioManager.shared().availableAudioDevices())
- }
- call.answer(with: callSettings)
- }
-
func changeSendVideo(_ completion: ((Error?) -> Void)? = nil) {
guard let wrapper = managedCallWrapper else { completion?(CallError.hasNoActiveCall); return }
@@ -131,28 +161,6 @@ final class CallManager:
}
}
- func changeShareScreen(_ completion: ((Error?) -> Void)? = nil) {
- guard let wrapper = managedCallWrapper else { completion?(CallError.hasNoActiveCall); return }
-
- if wrapper.sharingScreen {
- wrapper.call.setSendVideo(wrapper.sendingVideo) { [weak self] error in
- if let error = error {
- completion?(error)
- return
- }
- self?.managedCallWrapper?.sharingScreen = false
- }
- } else {
- wrapper.call.startInAppScreenSharing { [weak self] error in
- if let error = error {
- completion?(error)
- return
- }
- self?.managedCallWrapper?.sharingScreen = true
- }
- }
- }
-
func endCall() throws {
guard let call = managedCallWrapper?.call else { throw CallError.hasNoActiveCall }
@@ -165,12 +173,8 @@ final class CallManager:
withIncomingVideo video: Bool,
headers: [AnyHashable: Any]?
) {
- if hasManagedCall {
- call.reject(with: .busy, headers: nil)
- } else {
- managedCallWrapper = CallWrapper(call: call, callee: call.endpoints.first?.user ?? "", displayName: call.endpoints.first?.userDisplayName, direction: .incoming)
- didReceiveIncomingCall?()
- }
+ // Incoming calls are not supported in this demo
+ call.reject(with: .busy, headers: nil)
}
// MARK: - VICallDelegate -
@@ -191,6 +195,7 @@ final class CallManager:
managedCallWrapper?.state = .ended(reason: .disconnected)
managedCallWrapper = nil
}
+ notificationCenter.sendNotification(.callEnded)
}
func call(_ call: VICall,
@@ -201,33 +206,53 @@ final class CallManager:
managedCallWrapper?.state = .ended(reason: .failed(message: error.localizedDescription))
managedCallWrapper = nil
}
+ notificationCenter.sendNotification(.callEnded)
}
func call(_ call: VICall, didAddLocalVideoStream videoStream: VIVideoStream) {
if videoStream.type == .screenSharing { return }
- videoStreamAddedHandler?(true) { renderer in
- videoStream.addRenderer(renderer)
+ localVideoStreamAddedHandler?(myId) { renderer in
+ if let renderer = renderer {
+ videoStream.addRenderer(renderer)
+ }
}
}
func call(_ call: VICall, didRemoveLocalVideoStream videoStream: VIVideoStream) {
- videoStreamRemovedHandler?(true)
+ localVideoStreamRemovedHandler?(myId)
videoStream.removeAllRenderers()
}
func call(_ call: VICall, didAdd endpoint: VIEndpoint) {
+ if endpoint.endpointId == call.callId {
+ return
+ }
endpoint.delegate = self
+ endpointAddedHandler?(endpoint.endpointId, endpoint.userDisplayName ?? endpoint.user)
}
// MARK: - VIEndpointDelegate -
+ func endpointInfoDidUpdate(_ endpoint: VIEndpoint) {
+ if endpoint.endpointId == managedCallWrapper?.call.callId {
+ return
+ }
+ endpointUpdatedHandler?(endpoint.endpointId, endpoint.userDisplayName ?? endpoint.user)
+ }
+
+ func endpointDidRemove(_ endpoint: VIEndpoint) {
+ endpointRemovedHandler?(endpoint.endpointId)
+ }
+
func endpoint(_ endpoint: VIEndpoint, didAddRemoteVideoStream videoStream: VIVideoStream) {
- videoStreamAddedHandler?(false) { renderer in
- videoStream.addRenderer(renderer)
+ remoteVideoStreamAddedHandler?(endpoint.endpointId) { renderer in
+ if let renderer = renderer {
+ videoStream.addRenderer(renderer)
+ }
}
}
func endpoint(_ endpoint: VIEndpoint, didRemoveRemoteVideoStream videoStream: VIVideoStream) {
- videoStreamRemovedHandler?(false)
+ remoteVideoStreamRemovedHandler?(endpoint.endpointId)
videoStream.removeAllRenderers()
}
diff --git a/ScreenSharing/Services/DarwinNotificationsService.swift b/ScreenSharing/Services/DarwinNotificationsService.swift
new file mode 100644
index 0000000..24fedfd
--- /dev/null
+++ b/ScreenSharing/Services/DarwinNotificationsService.swift
@@ -0,0 +1,72 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import Foundation
+
+enum Notification: String {
+ case broadcastStarted = "broadcastStarted"
+ case broadcastEnded = "broadcastEnded"
+ case broadcastCallEnded = "broadcastCallEnded"
+ case callEnded = "callEnded"
+}
+
+final class DarwinNotificationCenter {
+ private var observer: UnsafeRawPointer {
+ UnsafeRawPointer(Unmanaged.passUnretained(self).toOpaque())
+ }
+
+ var broadcastEndedHandler: (() -> Void)?
+ var broadcastStartedHandler: (() -> Void)?
+ var broadcastCallEndedHandler: (() -> Void)?
+ var callEndedHandler: (() -> Void)?
+
+ func sendNotification(_ notification: Notification) {
+ CFNotificationCenterPostNotification(
+ CFNotificationCenterGetDarwinNotifyCenter(),
+ CFNotificationName(rawValue: notification.rawValue as CFString),
+ nil, nil, true
+ )
+ }
+
+ func registerForNotification(_ notification: Notification) {
+ CFNotificationCenterAddObserver(
+ CFNotificationCenterGetDarwinNotifyCenter(),
+ observer,
+ { _, observer, name, _, _ in
+ if let observer = observer, let name = name {
+ let mySelf = Unmanaged
+ .fromOpaque(observer).takeUnretainedValue()
+ mySelf.didReceiveNotification(name.rawValue as String)
+ }
+ },
+ notification.rawValue as CFString, nil, .deliverImmediately
+ )
+ }
+
+ func unregisterFromNotification(_ notification: Notification) {
+ CFNotificationCenterRemoveObserver(
+ CFNotificationCenterGetDarwinNotifyCenter(),
+ observer,
+ CFNotificationName(rawValue: notification.rawValue as CFString),
+ nil
+ )
+ }
+
+ private func didReceiveNotification(_ name: String) {
+ Log.i("DarwinNotificationCenter received notification \(name)")
+ let notification = Notification(rawValue: name)
+ switch notification {
+ case .broadcastEnded:
+ broadcastEndedHandler?()
+ case .broadcastCallEnded:
+ broadcastCallEndedHandler?()
+ case .callEnded:
+ callEndedHandler?()
+ case .broadcastStarted:
+ broadcastStartedHandler?()
+ case .none:
+ break
+ }
+ }
+}
diff --git a/ScreenSharing/Stories/Call/CallViewController.swift b/ScreenSharing/Stories/Call/CallViewController.swift
deleted file mode 100644
index 824789f..0000000
--- a/ScreenSharing/Stories/Call/CallViewController.swift
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
-* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
-*/
-
-import UIKit
-import VoxImplantSDK
-
-final class CallViewController:
- UIViewController
-{
- @IBOutlet private weak var videoButton: CallOptionButton!
- @IBOutlet private weak var sharingButton: CallOptionButton!
- @IBOutlet private weak var hangupButton: CallOptionButton!
- @IBOutlet private weak var localVideoStreamView: CallVideoView!
- @IBOutlet private weak var magneticView: EdgeMagneticView!
- @IBOutlet private weak var remoteVideoStreamView: CallVideoView!
- @IBOutlet private weak var callStateLabel: UILabel!
-
- var callManager: CallManager! // DI
- var storyAssembler: StoryAssembler! // DI
-
- override func viewDidLoad() {
- super.viewDidLoad()
-
- callManager.callObserver = updateContent(with:)
-
- videoButton.state = .initial(model: CallOptionButtonModels.camera)
- videoButton.touchUpHandler = videoHandler(_:)
-
- sharingButton.state = .initial(model: CallOptionButtonModels.screen)
- sharingButton.touchUpHandler = sharingHandler(_:)
-
- hangupButton.state = .initial(model: CallOptionButtonModels.hangup)
- hangupButton.touchUpHandler = hangupHandler(_:)
-
- localVideoStreamView.showImage = false
-
- callManager.videoStreamAddedHandler = videoStreamAdded(_:_:)
- callManager.videoStreamRemovedHandler = videoStreamRemoved(_:)
-
- guard let callDirection = callManager.managedCallWrapper?.direction else {
- dismiss(animated: true)
- return
- }
-
- do {
- try callDirection == .outgoing
- ? callManager.startOutgoingCall()
- : callManager.makeIncomingCallActive()
- } catch (let error) {
- Log.e(" \(callDirection) call start failed with error \(error.localizedDescription)")
- dismiss(animated: true)
- }
- }
-
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
-
- if let call = callManager.managedCallWrapper {
- updateContent(with: call)
- }
- }
-
- @IBAction private func localVideoStreamTapped(_ sender: UITapGestureRecognizer) {
- VICameraManager.shared().useBackCamera.toggle()
- }
-
- @IBAction private func localVideoStreamDragged(_ sender: UIPanGestureRecognizer) {
- switch sender.state {
- case .changed:
- let translation = sender.translation(in: self.view)
- localVideoStreamView.center = CGPoint(x: localVideoStreamView.center.x + translation.x,
- y: localVideoStreamView.center.y + translation.y)
- sender.setTranslation(CGPoint.zero, in: self.view)
- case .ended:
- magneticView.rearrangeInnerView()
- default:
- break
- }
- }
-
- func videoStreamAdded(_ local: Bool, _ completion: (VIVideoRendererView) -> Void) {
- if local {
- localVideoStreamView.streamEnabled = true
- completion(VIVideoRendererView(containerView: localVideoStreamView.streamView))
- } else {
- remoteVideoStreamView.streamEnabled = true
- completion(VIVideoRendererView(containerView: remoteVideoStreamView.streamView))
- }
- }
-
- func videoStreamRemoved(_ local: Bool) {
- (local ? localVideoStreamView : remoteVideoStreamView).streamEnabled = false
- }
-
- private func updateContent(with call: CallManager.CallWrapper) {
- if case .ended (let reason) = call.state {
- if case .disconnected = reason {
- self.dismiss(animated: true)
- }
- if case .failed (let message) = reason {
- weak var presentingViewController = self.presentingViewController
- self.dismiss(animated: true) {
- presentingViewController?.present(
- self.storyAssembler.callFailed(
- callee: call.callee,
- displayName: call.displayName ?? call.callee,
- reason: message
- ),
- animated: true)
- }
- return
- }
- }
-
- sharingButton.state = call.state == .connected
- ? call.sharingScreen ? .selected : .normal
- : .unavailable
- videoButton.state = call.state == .connected
- ? call.sendingVideo ? .normal : .selected
- : .unavailable
-
- localVideoStreamView.streamEnabled = call.sendingVideo && !call.sharingScreen
-
- callStateLabel.text = call.state == .connected ? "Call in progress" : "Connecting..."
- }
-
- private func videoHandler(_ button: CallOptionButton) {
- Log.d("Changing sendVideo")
- button.state = .unavailable
- sharingButton.state = .unavailable
-
- callManager.changeSendVideo { [weak self] error in
- if let error = error {
- Log.e("setSendVideo error \(error.localizedDescription)")
- AlertHelper.showError(message: error.localizedDescription, on: self)
- }
- }
- }
-
- private func sharingHandler(_ button: CallOptionButton) {
- Log.d("Changing sharing")
- button.state = .unavailable
- videoButton.state = .unavailable
-
- callManager.changeShareScreen { [weak self] error in
- if let error = error {
- Log.e("setSharing error \(error.localizedDescription)")
- AlertHelper.showError(message: error.localizedDescription, on: self)
- }
- }
- }
-
- private func hangupHandler(_ button: CallOptionButton) {
- Log.d("Call hangup called")
- button.state = .unavailable
- do {
- try callManager.endCall()
- } catch (let error) {
- Log.e(error.localizedDescription)
- }
- dismiss(animated: true)
- }
-
- private enum CallOptionButtonModels {
- static let screen = CallOptionButtonModel(image: UIImage(named: "screenSharing"), text: "Screen")
- static let camera = CallOptionButtonModel(image: UIImage(named: "videoOn"), imageSelected: UIImage(named: "videoOff"), text: "Camera")
- static let hangup = CallOptionButtonModel(image: UIImage(named: "hangup"), imageTint: #colorLiteral(red: 1, green: 0.02352941176, blue: 0.2549019608, alpha: 1), text: "Hangup")
- }
-}
diff --git a/ScreenSharing/Stories/CallFailedViewController.swift b/ScreenSharing/Stories/CallFailedViewController.swift
new file mode 100644
index 0000000..fae2d50
--- /dev/null
+++ b/ScreenSharing/Stories/CallFailedViewController.swift
@@ -0,0 +1,55 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import UIKit
+
+final class CallFailedViewController: UIViewController {
+ @IBOutlet private var callFailedView: DefaultCallFailedView!
+
+ var callManager: CallManager! // DI
+ var storyAssembler: StoryAssembler! // DI
+ var failReason: String! // DI
+ var user: String! // DI
+ var displayName: String! // DI
+
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask { .portrait }
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ callFailedView.displayName = displayName
+ callFailedView.reason = failReason
+
+ callFailedView.cancelHandler = { [weak self] in
+ self?.dismiss(animated: true)
+ }
+
+ callFailedView.callBackHandler = { [weak self] in
+ guard let self = self else { return }
+
+ do {
+ try self.callManager.makeOutgoingCall(to: self.user)
+
+ weak var presentingViewController = self.presentingViewController
+ self.dismiss(animated: true) {
+ presentingViewController?.present(self.storyAssembler.call, animated: true)
+ }
+
+ } catch (let error) {
+ Log.e("Error during call back \(error.localizedDescription)")
+ AlertHelper.showAlert(
+ title: "Error",
+ message: error.localizedDescription,
+ actions: [
+ UIAlertAction(title: "Dismiss", style: .cancel) { _ in
+ self.dismiss(animated: true)
+ }
+ ],
+ defaultAction: false,
+ on: self
+ )
+ }
+ }
+ }
+}
diff --git a/ScreenSharing/Stories/CallViewController.swift b/ScreenSharing/Stories/CallViewController.swift
new file mode 100644
index 0000000..149a1d8
--- /dev/null
+++ b/ScreenSharing/Stories/CallViewController.swift
@@ -0,0 +1,146 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import UIKit
+import VoxImplantSDK
+import ReplayKit
+
+final class CallViewController: UIViewController {
+ @IBOutlet private weak var conferenceView: ConferenceView!
+ @IBOutlet private weak var videoButton: CallOptionButton!
+ @IBOutlet private weak var sharingButton: CallOptionButton!
+ @IBOutlet private weak var hangupButton: CallOptionButton!
+
+ var myDisplayName: String! // DI
+ var callManager: CallManager! // DI
+ var storyAssembler: StoryAssembler! // DI
+
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+ .all
+ }
+
+ private var screenSharingButtonSubview: UIImageView?
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ callManager.callObserver = { [weak self] call in
+ guard let self = self else { return }
+ if case .ended (let reason) = call.state {
+ if case .disconnected = reason {
+ self.dismiss(animated: true)
+ }
+ if case .failed (let message) = reason {
+ weak var presentingViewController = self.presentingViewController
+ self.dismiss(animated: true) {
+ presentingViewController?.present(
+ self.storyAssembler.callFailed(
+ callee: call.callee,
+ displayName: call.displayName ?? call.callee,
+ reason: message
+ ),
+ animated: true)
+ }
+ return
+ }
+ }
+
+ if #available(iOS 12.0, *) {
+ self.screenSharingButtonSubview?.tintColor = call.sharingScreen ? #colorLiteral(red: 0.9607843137, green: 0.2941176471, blue: 0.368627451, alpha: 1) : .white
+ } else {
+ self.sharingButton.state = call.state == .connected
+ ? call.sharingScreen ? .selected : .normal
+ : .unavailable
+ }
+ self.videoButton.state = call.state == .connected
+ ? call.sendingVideo ? .normal : .selected
+ : .unavailable
+ }
+
+ videoButton.state = .initial(model: CallOptionButtonModels.camera)
+ videoButton.touchUpHandler = { [weak self] button in
+ Log.d("Changing sendVideo")
+ let previousState = button.state
+ button.state = .unavailable
+
+ self?.callManager.changeSendVideo { [weak self] error in
+ if let error = error {
+ Log.e("setSendVideo error \(error.localizedDescription)")
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ }
+ button.state = previousState
+ }
+ }
+
+ if #available(iOS 12.0, *) {
+ sharingButton.state = .initial(model: CallOptionButtonModels.screen)
+ } else {
+ sharingButton.state = .initial(model: CallOptionButtonModels.screenOld)
+ }
+
+ sharingButton.touchUpHandler = { _ in
+ if #available(iOS 12.0, *) {
+ // nothing, besause used handler of RPSystemBroadcastPickerView, which created in self.init() method
+ } else if #available(iOS 11.0, *) {
+ AlertHelper.showAlert(title: "On iOS 11 enabling screensharing", message: "Open the Control Panel (swipe up from bottom) -> hold down on the Record Button until options appeared -> select ScreenSharingUploadAppex -> start broadcast. If can't find Record Button in Control Panel go to the Settings -> Control Center -> Customize Controls and add Screen Recording to the Control Panel.", defaultAction: true)
+ }
+ }
+
+ if #available(iOS 12.0, *) {
+ let broadcastPicker = RPSystemBroadcastPickerView(frame: CGRect(x: 0, y: 0, width: 46, height: 46))
+ broadcastPicker.preferredExtension = "com.voximplant.demos.ScreenSharing.ScreenSharingUploadAppex"
+ if let button = broadcastPicker.subviews.first as? UIButton {
+ button.imageView?.tintColor = UIColor.white
+ screenSharingButtonSubview = button.imageView
+ }
+ sharingButton.addSubview(broadcastPicker)
+ }
+
+ hangupButton.state = .initial(model: CallOptionButtonModels.hangup)
+ hangupButton.touchUpHandler = { [weak self] button in
+ Log.d("Call hangup called")
+ button.state = .unavailable
+ do {
+ try self?.callManager.endCall()
+ } catch (let error) {
+ Log.e(error.localizedDescription)
+ }
+ self?.dismiss(animated: true)
+ }
+
+ callManager.endpointAddedHandler = conferenceView.addParticipant(withID:displayName:)
+ callManager.endpointUpdatedHandler = conferenceView.updateParticipant(withID:displayName:)
+ callManager.endpointRemovedHandler = conferenceView.removeParticipant(withId:)
+
+ callManager.localVideoStreamAddedHandler = conferenceView.prepareVideoRendererForStream(participantID:completion:)
+ callManager.remoteVideoStreamAddedHandler = conferenceView.prepareVideoRendererForStream(participantID:completion:)
+ callManager.remoteVideoStreamAddedHandler = conferenceView.prepareVideoRendererForStream(participantID:completion:)
+ callManager.remoteVideoStreamRemovedHandler = conferenceView.removeVideoStream(participantID:)
+
+ do {
+ try callManager.startOutgoingCall()
+ conferenceView.addParticipant(withID: myId, displayName: "\(myDisplayName ?? "") (you)")
+ } catch (let error) {
+ Log.e("Call start failed with error \(error.localizedDescription)")
+ dismiss(animated: true)
+ }
+ }
+
+ deinit {
+ callManager.endpointAddedHandler = nil
+ callManager.endpointUpdatedHandler = nil
+ callManager.endpointRemovedHandler = nil
+ callManager.localVideoStreamAddedHandler = nil
+ callManager.remoteVideoStreamAddedHandler = nil
+ callManager.remoteVideoStreamAddedHandler = nil
+ callManager.remoteVideoStreamRemovedHandler = nil
+ }
+
+ private enum CallOptionButtonModels {
+ static let screenOld = CallOptionButtonModel(image: UIImage(named: "screenSharing"), text: "Screen")
+ static let screen = CallOptionButtonModel(image: nil, text: "Screen")
+ static let camera = CallOptionButtonModel(image: UIImage(named: "videoOn"), imageSelected: UIImage(named: "videoOff"), text: "Camera")
+ static let hangup = CallOptionButtonModel(image: UIImage(named: "hangup"), imageTint: #colorLiteral(red: 1, green: 0.02352941176, blue: 0.2549019608, alpha: 1), text: "Hangup")
+ }
+}
diff --git a/ScreenSharing/Stories/LoginViewController.swift b/ScreenSharing/Stories/LoginViewController.swift
new file mode 100644
index 0000000..1a36e49
--- /dev/null
+++ b/ScreenSharing/Stories/LoginViewController.swift
@@ -0,0 +1,48 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import UIKit
+
+final class LoginViewController: UIViewController, LoadingShowable {
+ @IBOutlet private var loginView: DefaultLoginView!
+
+ var authService: AuthService! // DI
+ var storyAssembler: StoryAssembler! // DI
+
+ override func viewDidLoad() {
+ super.viewDidLoad()
+
+ loginView.setTitle(text: "Screen sharing demo")
+
+ let loginHandler: AuthService.LoginCompletion = { [weak self] error in
+ guard let self = self else { return }
+ self.hideProgress()
+ if let error = error {
+ AlertHelper.showError(message: error.localizedDescription, on: self)
+ } else {
+ self.present(self.storyAssembler.main, animated: true)
+ }
+ }
+
+ loginView.loginTouchHandler = { [weak self] username, password in
+ Log.d("Manually Logging in with password")
+ self?.showLoading(title: "Connecting", details: "Please wait...")
+ self?.authService.login(user: username.appendingVoxDomain, password: password, loginHandler)
+ }
+
+ if authService.possibleToLogin {
+ Log.d("Automatically Logging in with token")
+ self.showLoading(title: "Connecting", details: "Please wait...")
+ self.authService.loginWithAccessToken(loginHandler)
+ }
+ }
+
+ override func viewWillAppear(_ animated: Bool) {
+ super.viewWillAppear(animated)
+
+ loginView.username = authService.loggedInUser?.replacingOccurrences(of: ".voximplant.com", with: "")
+ loginView.password = ""
+ }
+}
+
diff --git a/ScreenSharing/Stories/Main/MainViewController.swift b/ScreenSharing/Stories/MainViewController.swift
similarity index 85%
rename from ScreenSharing/Stories/Main/MainViewController.swift
rename to ScreenSharing/Stories/MainViewController.swift
index 254d922..d0d00ee 100644
--- a/ScreenSharing/Stories/Main/MainViewController.swift
+++ b/ScreenSharing/Stories/MainViewController.swift
@@ -16,6 +16,10 @@ final class MainViewController:
var callManager: CallManager! // DI
var storyAssembler: StoryAssembler! // DI
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
+ .all
+ }
+
override func viewDidLoad() {
super.viewDidLoad()
@@ -25,7 +29,7 @@ final class MainViewController:
mainView.callTouchHandler = { username in
Log.d("Calling \(String(describing: username))")
- PermissionsHelper.requestRecordPermissions(includingVideo: true) { [weak self] error in
+ PermissionsHelper.requestRecordPermissions(includingVideo: true, completion: { [weak self] error in
if let error = error {
self?.handleError(error)
return
@@ -35,8 +39,10 @@ final class MainViewController:
guard let self = self else { return }
do {
try self.callManager.makeOutgoingCall(to: username ?? "")
- self.view.endEditing(true)
- self.present(self.storyAssembler.call, animated: true)
+ DispatchQueue.main.async {
+ self.view.endEditing(true)
+ self.present(self.storyAssembler.call, animated: true)
+ }
} catch (let error) {
Log.e(error.localizedDescription)
AlertHelper.showError(message: error.localizedDescription, on: self)
@@ -50,7 +56,7 @@ final class MainViewController:
} else {
beginCall()
}
- }
+ })
}
mainView.logoutTouchHandler = { [weak self] in
@@ -60,17 +66,6 @@ final class MainViewController:
}
}
- override func viewWillAppear(_ animated: Bool) {
- super.viewWillAppear(animated)
-
- callManager.didReceiveIncomingCall = { [weak self] in
- guard let self = self else { return }
-
- self.view.endEditing(true)
- self.present(self.storyAssembler.incomingCall, animated: true)
- }
- }
-
private func reconnect(onSuccess: (() -> Void)? = nil) {
Log.d("Reconnecting")
showLoading(title: "Reconnecting", details: "Please wait...")
diff --git a/ScreenSharing/Stories/StoryAssembler.swift b/ScreenSharing/Stories/StoryAssembler.swift
index 8e4bc8f..be4042b 100644
--- a/ScreenSharing/Stories/StoryAssembler.swift
+++ b/ScreenSharing/Stories/StoryAssembler.swift
@@ -31,13 +31,7 @@ final class StoryAssembler {
var call: CallViewController {
let controller = Storyboard.call.instantiateViewController(of: CallViewController.self)
controller.callManager = callManager
- controller.storyAssembler = self
- return controller
- }
-
- var incomingCall: IncomingCallViewController {
- let controller = Storyboard.call.instantiateViewController(of: IncomingCallViewController.self)
- controller.callManager = callManager
+ controller.myDisplayName = authService.loggedInUserDisplayName
controller.storyAssembler = self
return controller
}
diff --git a/ScreenSharing/Stories/Storyboards/Call.storyboard b/ScreenSharing/Stories/Storyboards/Call.storyboard
index 15d95f7..8f27e9d 100644
--- a/ScreenSharing/Stories/Storyboards/Call.storyboard
+++ b/ScreenSharing/Stories/Storyboards/Call.storyboard
@@ -1,6 +1,6 @@
-
+
@@ -8,90 +8,27 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
+
-
+
@@ -108,7 +45,7 @@
-
+
@@ -140,62 +77,42 @@
-
-
-
-
-
-
+
+
+
-
-
-
+
-
-
-
-
+
+
-
+
+
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
@@ -219,7 +136,7 @@
-
+
diff --git a/ScreenSharingUploadAppex/BroadcastError.swift b/ScreenSharingUploadAppex/BroadcastError.swift
new file mode 100644
index 0000000..beb2b2e
--- /dev/null
+++ b/ScreenSharingUploadAppex/BroadcastError.swift
@@ -0,0 +1,33 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import Foundation
+
+fileprivate let errorDomain = "ScreenSharingErrorDomain"
+
+enum BroadcastError {
+ static let noCall = NSError(
+ domain: "ScreenSharingErrorDomain",
+ code: 10000,
+ userInfo: [NSLocalizedFailureReasonErrorKey: "Call is not started"]
+ )
+
+ static let nilOnCallInitialisation = NSError(
+ domain: errorDomain,
+ code: 10001,
+ userInfo: [NSLocalizedFailureReasonErrorKey: "Internal broadcast error"]
+ )
+
+ static let authError = NSError(
+ domain: errorDomain,
+ code: 10002,
+ userInfo: [NSLocalizedFailureReasonErrorKey: "Internal broadcast error"]
+ )
+
+ static let callEnded = NSError(
+ domain: errorDomain,
+ code: 10003,
+ userInfo: [NSLocalizedFailureReasonErrorKey: "Call has been ended"]
+ )
+}
diff --git a/ScreenSharingUploadAppex/Info.plist b/ScreenSharingUploadAppex/Info.plist
new file mode 100644
index 0000000..f3bb47e
--- /dev/null
+++ b/ScreenSharingUploadAppex/Info.plist
@@ -0,0 +1,33 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ ScreenSharingUploadAppex
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ $(CURRENT_PROJECT_VERSION)
+ NSExtension
+
+ NSExtensionPointIdentifier
+ com.apple.broadcast-services-upload
+ NSExtensionPrincipalClass
+ $(PRODUCT_MODULE_NAME).SampleHandler
+ RPBroadcastProcessMode
+ RPBroadcastProcessModeSampleBuffer
+
+
+
diff --git a/ScreenSharingUploadAppex/SampleHandler.swift b/ScreenSharingUploadAppex/SampleHandler.swift
new file mode 100644
index 0000000..f66a374
--- /dev/null
+++ b/ScreenSharingUploadAppex/SampleHandler.swift
@@ -0,0 +1,161 @@
+/*
+* Copyright (c) 2011-2020, Zingaya, Inc. All rights reserved.
+*/
+
+import ReplayKit
+import VoxImplantSDK
+
+extension UserDefaults {
+ static var main: UserDefaults {
+ // App Group UserDefaults needed for communication between the app and the appex
+ return UserDefaults(suiteName: "group.com.voximplant.demos")!
+ }
+}
+
+extension CGImagePropertyOrientation {
+ var rotation: VIRotation {
+ switch self {
+ case .up, .upMirrored:
+ return ._0
+ case .right, .rightMirrored:
+ return ._270
+ case .left, .leftMirrored:
+ return ._90
+ case .down, .downMirrored:
+ return ._180
+ }
+ }
+}
+
+class SampleHandler: RPBroadcastSampleHandler, Loggable, VICallDelegate {
+ var client: VIClient
+ var authService: AuthService
+ var screenVideoSource = VICustomVideoSource(screenCastFormat: ())
+ var screenSharingCall: VICall?
+
+ @UserDefault("activecall")
+ var managedCallee: String?
+
+ override init() {
+ let viclient = VIClient.init(delegateQueue: .main)
+ self.client = viclient
+ self.authService = AuthService(viclient)
+ super.init()
+ }
+
+ var appName: String { "ScreenSharingUploadAppex" }
+
+ private let notificationCenter = DarwinNotificationCenter()
+
+ func call(_ call: VICall,
+ didDisconnectWithHeaders headers: [AnyHashable : Any]?,
+ answeredElsewhere: NSNumber
+ ) {
+ screenSharingCall?.remove(self)
+ notificationCenter.sendNotification(.broadcastCallEnded)
+ }
+
+ func call(_ call: VICall,
+ didFailWithError error: Error,
+ headers: [AnyHashable : Any]?
+ ) {
+ screenSharingCall?.remove(self)
+ notificationCenter.sendNotification(.broadcastCallEnded)
+ }
+
+ override func broadcastStarted(withSetupInfo setupInfo: [String : NSObject]?) {
+ // User has requested to start the broadcast.
+ // On iOS 11 it starts from Control Panel, on iOS 12 and above it could be started within the app (RPSystemBroadcastPickerView).
+
+ configureDefaultLogging()
+
+ let screenVideoSource = self.screenVideoSource
+
+ if authService.possibleToLogin,
+ let roomid = self.managedCallee
+ {
+ Log.i("Logging in with token which used in the app")
+ authService.loginWithAccessToken()
+ { [weak self] (isError: Error?) in
+ if let error = isError {
+ self?.finishBroadcast(with: BroadcastError.authError)
+ Log.i("Problem due to login: \(error)")
+ } else {
+ Log.i("Login success, roomid: \(roomid)")
+ let settings = VICallSettings()
+ // It is important to use only .H264 encoding in appex.
+ settings.preferredVideoCodec = .H264
+ settings.receiveAudio = false
+ settings.videoFlags = VIVideoFlags.videoFlags(receiveVideo: false, sendVideo: true)
+ if let screenSharingCall = self?.client.callConference(roomid, settings: settings),
+ let self = self
+ {
+ self.screenSharingCall = screenSharingCall
+ screenSharingCall.videoSource = screenVideoSource
+ screenSharingCall.sendAudio = false
+
+ screenSharingCall.add(self)
+ screenSharingCall.start()
+
+ self.notificationCenter.registerForNotification(.callEnded)
+ self.notificationCenter.callEndedHandler = {
+ Log.i("callEnded notification received, screensharing call will hangup")
+ screenSharingCall.hangup(withHeaders: nil)
+ self.finishBroadcast(with: BroadcastError.callEnded)
+ }
+ self.notificationCenter.sendNotification(.broadcastStarted)
+
+ } else {
+ self?.finishBroadcast(with: BroadcastError.nilOnCallInitialisation)
+ Log.i("Can't initiate a screensharing")
+ }
+ }
+ }
+ } else {
+ self.finishBroadcast(with: BroadcastError.noCall)
+ Log.i("Impossible to login or no room")
+ }
+ }
+
+ override func broadcastPaused() {
+ // Empty method should be. Don't need it. Used only in BroadcastSetupUIAppex design method.
+ }
+
+ override func broadcastResumed() {
+ // Empty method should be. Don't need it. Used only in BroadcastSetupUIAppex design method.
+ }
+
+ override func broadcastFinished() {
+ // User has requested to finish the broadcast or it interrupted by OS (with incoming call, for example).
+ self.notificationCenter.sendNotification(.broadcastEnded)
+ }
+
+ override func processSampleBuffer(_ sampleBuffer: CMSampleBuffer, with sampleBufferType: RPSampleBufferType) {
+ if sampleBufferType != .video {
+ return
+ }
+
+ if (CMSampleBufferGetNumSamples(sampleBuffer) != 1 || !CMSampleBufferIsValid(sampleBuffer) ||
+ !CMSampleBufferDataIsReady(sampleBuffer)) {
+ return;
+ }
+
+ if let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
+ // let options = [kCVPixelBufferCGImageCompatibilityKey: true, kCVPixelBufferCGBitmapContextCompatibilityKey: true]
+
+ var rotation: VIRotation?
+ if let uint32rotation = CMGetAttachment(sampleBuffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil)?.uint32Value {
+ rotation = CGImagePropertyOrientation(rawValue: uint32rotation)?.rotation
+ }
+
+ self.screenVideoSource.sendVideoFrame(pixelBuffer, rotation: rotation ?? VIRotation._0)
+ }
+ }
+
+ private func finishBroadcast(with error: Error) {
+ // This is iOS bug: sometimes finishBroadcastWithError could have no effect.
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
+ self.finishBroadcastWithError(error)
+ }
+ }
+}
diff --git a/ScreenSharingUploadAppex/ScreenSharingUploadAppex.entitlements b/ScreenSharingUploadAppex/ScreenSharingUploadAppex.entitlements
new file mode 100644
index 0000000..9ba9aa6
--- /dev/null
+++ b/ScreenSharingUploadAppex/ScreenSharingUploadAppex.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.application-groups
+
+ group.com.voximplant.demos
+
+
+
diff --git a/Shared/PermissionsHelper.swift b/Shared/PermissionsHelper.swift
index b129c99..bd9298a 100644
--- a/Shared/PermissionsHelper.swift
+++ b/Shared/PermissionsHelper.swift
@@ -7,8 +7,8 @@ import AVFoundation
final class PermissionsHelper {
static func requestRecordPermissions(
includingVideo video: Bool,
- accessRequestCompletionQueue: DispatchQueue = .main,
- completion: @escaping (Error?) -> Void
+ completion: @escaping (Error?) -> Void,
+ accessRequestCompletionQueue: DispatchQueue = .main
) {
requestPermissions(for: .audio, queue: accessRequestCompletionQueue) { granted in
if granted {
@@ -25,11 +25,7 @@ final class PermissionsHelper {
}
}
- static func requestPermissions(
- for mediaType: AVMediaType,
- queue: DispatchQueue = .main,
- completion: @escaping (Bool) -> Void
- ) {
+ static func requestPermissions(for mediaType: AVMediaType, queue: DispatchQueue = .main, completion: @escaping (Bool) -> Void) {
switch AVCaptureDevice.authorizationStatus(for: mediaType) {
case .notDetermined:
AVCaptureDevice.requestAccess(for: mediaType) { granted in
@@ -38,7 +34,7 @@ final class PermissionsHelper {
}
}
case .authorized: completion(true)
- case .denied: completion(false)
+ case .denied: completion(false)
case .restricted: completion(false)
@unknown default: completion(false)
}
diff --git a/Shared/Protocols/Loggable.swift b/Shared/Protocols/Loggable.swift
index 38401a3..0230f31 100644
--- a/Shared/Protocols/Loggable.swift
+++ b/Shared/Protocols/Loggable.swift
@@ -3,13 +3,12 @@
*/
import VoxImplantSDK
-import UIKit
protocol Loggable {
var appName: String { get }
}
-extension Loggable where Self: AppDelegate {
+extension Loggable where Self: AnyObject {
func configureDefaultLogging() {
// Configure logs:
Log.enable(level: .debug)
diff --git a/Shared/UI/AlertHelper.swift b/Shared/UI/AlertHelper.swift
index 19018e4..b4177a1 100644
--- a/Shared/UI/AlertHelper.swift
+++ b/Shared/UI/AlertHelper.swift
@@ -75,7 +75,7 @@ final class AlertHelper {
static func showAlert(
title: String,
message: String,
- actions: [UIAlertAction],
+ actions: [UIAlertAction] = [],
defaultAction: Bool = false,
on viewController: UIViewController? = nil
) {
diff --git a/Conference/Stories/ConferenceCall/ConferenceParticipantView/ConferenceParticipantView.swift b/Shared/UI/ConferenceView/ConferenceParticipantView/ConferenceParticipantView.swift
similarity index 100%
rename from Conference/Stories/ConferenceCall/ConferenceParticipantView/ConferenceParticipantView.swift
rename to Shared/UI/ConferenceView/ConferenceParticipantView/ConferenceParticipantView.swift
diff --git a/Conference/Stories/ConferenceCall/ConferenceParticipantView/ConferenceParticipantView.xib b/Shared/UI/ConferenceView/ConferenceParticipantView/ConferenceParticipantView.xib
similarity index 100%
rename from Conference/Stories/ConferenceCall/ConferenceParticipantView/ConferenceParticipantView.xib
rename to Shared/UI/ConferenceView/ConferenceParticipantView/ConferenceParticipantView.xib
diff --git a/Conference/Stories/ConferenceCall/ConferenceView.swift b/Shared/UI/ConferenceView/ConferenceView.swift
similarity index 98%
rename from Conference/Stories/ConferenceCall/ConferenceView.swift
rename to Shared/UI/ConferenceView/ConferenceView.swift
index 33ea5e7..e08b160 100644
--- a/Conference/Stories/ConferenceCall/ConferenceView.swift
+++ b/Shared/UI/ConferenceView/ConferenceView.swift
@@ -36,6 +36,11 @@ final class ConferenceView: UIView {
}
private var didDraw: Bool = false
+ override func layoutSubviews() {
+ super.layoutSubviews()
+ rearrange()
+ }
+
func addParticipant(withID id: String, displayName: String?) {
guard participantViews.count <= 25 else {
print("Limit!")
diff --git a/Shared/UserDefault.swift b/Shared/UserDefault.swift
index f00e305..91e8dd3 100644
--- a/Shared/UserDefault.swift
+++ b/Shared/UserDefault.swift
@@ -4,8 +4,21 @@
import Foundation
-fileprivate let userDefaults = UserDefaults.standard
-fileprivate let userDefaultsDomain = Bundle.main.bundleIdentifier ?? ""
+protocol UserDefaultsMain {
+ static var main: UserDefaults { get }
+}
+
+extension UserDefaultsMain {
+ static var main: UserDefaults {
+ // Switching UserDefaults implementation needed for ScreenSharing and ScreenSharingUploadAppex targets.
+ return UserDefaults.standard
+ }
+}
+
+extension UserDefaults: UserDefaultsMain {}
+
+fileprivate let userDefaults = UserDefaults.main
+fileprivate let userDefaultsDomain = "" //Bundle.main.bundleIdentifier ?? ""
fileprivate extension String {
var appendingAppDomain: String {
"\(userDefaultsDomain).\(self)"
diff --git a/Swift.xcodeproj/project.pbxproj b/Swift.xcodeproj/project.pbxproj
index b72b20d..9f4db19 100644
--- a/Swift.xcodeproj/project.pbxproj
+++ b/Swift.xcodeproj/project.pbxproj
@@ -21,7 +21,6 @@
7E386CAD248793B800556D3D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CAC248793B800556D3D /* AppDelegate.swift */; };
7E386CB1248793B800556D3D /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CB0248793B800556D3D /* LoginViewController.swift */; };
7E386CB4248793B800556D3D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E386CB2248793B800556D3D /* Main.storyboard */; };
- 7E386CB6248793B900556D3D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7E386CB5248793B900556D3D /* Assets.xcassets */; };
7E386CBE248794E900556D3D /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5E0CFCF22B773BC009C7F2D /* Log.swift */; };
7E386CBF248794FF00556D3D /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5E0CFCF22B773BC009C7F2D /* Log.swift */; };
7E386CC2248795C200556D3D /* Loggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CC02487954100556D3D /* Loggable.swift */; };
@@ -65,12 +64,9 @@
7E386CF62487B3D300556D3D /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CF52487B3D300556D3D /* CallViewController.swift */; };
7E386CF72487C02500556D3D /* AudioDeviceSelecting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E37C36B244F349F00F18266 /* AudioDeviceSelecting.swift */; };
7E386CF92487C09000556D3D /* EdgeMagneticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CF82487C09000556D3D /* EdgeMagneticView.swift */; };
- 7E386CFB2487C0E400556D3D /* CallVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CFA2487C0E400556D3D /* CallVideoView.swift */; };
- 7E386CFE2487C12B00556D3D /* CallVideoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7E386CFD2487C12B00556D3D /* CallVideoView.xib */; };
7E386D06248802BF00556D3D /* DefaultIncomingCallView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7E386D042488004900556D3D /* DefaultIncomingCallView.xib */; };
7E386D08248802C200556D3D /* DefaultIncomingCallView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7E386D042488004900556D3D /* DefaultIncomingCallView.xib */; };
7E386D0A2488040100556D3D /* Call.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E386D092488040100556D3D /* Call.storyboard */; };
- 7E386D0C2488041600556D3D /* IncomingCallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386D0B2488041600556D3D /* IncomingCallViewController.swift */; };
7E386D0D2488046700556D3D /* DefaultIncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386D022488003600556D3D /* DefaultIncomingCallView.swift */; };
7E386D112488046900556D3D /* DefaultIncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386D022488003600556D3D /* DefaultIncomingCallView.swift */; };
7E386D142488056000556D3D /* PressableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386D132488056000556D3D /* PressableButton.swift */; };
@@ -99,6 +95,9 @@
7E537DD12488235B00013D6E /* EdgeMagneticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CF82487C09000556D3D /* EdgeMagneticView.swift */; };
7E544266243BA45500450A73 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 7E544265243BA45500450A73 /* README.md */; };
7E55DF37248829880067A214 /* AppLifeCycleDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E37244DDE2C009DDA51 /* AppLifeCycleDelegate.swift */; };
+ 7E616725249A17820066C43B /* ConferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738F772435E259004387C2 /* ConferenceView.swift */; };
+ 7E616726249A17870066C43B /* ConferenceParticipantView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EC3FDB92436433A0021BD9E /* ConferenceParticipantView.swift */; };
+ 7E616727249A178A0066C43B /* ConferenceParticipantView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7EC3FDBB243643880021BD9E /* ConferenceParticipantView.xib */; };
7E708B7E2452D35B00F58F92 /* PermissionsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B6829E22F4492A002244B5 /* PermissionsHelper.swift */; };
7E738F912435E259004387C2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7E738F6E2435E259004387C2 /* Main.storyboard */; };
7E738F922435E259004387C2 /* Storyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738F702435E259004387C2 /* Storyboard.swift */; };
@@ -120,7 +119,42 @@
7E738FA82435E259004387C2 /* ManageConferenceUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738F8F2435E259004387C2 /* ManageConferenceUseCase.swift */; };
7E738FA92435E25A004387C2 /* JoinConferenceUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738F902435E259004387C2 /* JoinConferenceUseCase.swift */; };
7E738FAB2435E297004387C2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738FAA2435E297004387C2 /* AppDelegate.swift */; };
+ 7E7BE47824A6012700243B8E /* BroadcastError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E7BE47724A6012700243B8E /* BroadcastError.swift */; };
7E8A831124891BC3000578C6 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 7E8A831024891BC3000578C6 /* README.md */; };
+ 7E8E19F9249A4F6000BC78F9 /* Loggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CC02487954100556D3D /* Loggable.swift */; };
+ 7E8E19FA249A4F6600BC78F9 /* AppLifeCycleDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E37244DDE2C009DDA51 /* AppLifeCycleDelegate.swift */; };
+ 7E8E19FB249A4F7300BC78F9 /* UIExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F00F9322C610E00061AF13 /* UIExtensions.swift */; };
+ 7E8E19FC249A661700BC78F9 /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92C8E20022EB4139006CCCFF /* Shared.xcassets */; };
+ 7E8E19FD249A661E00BC78F9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C5E0CFB622B771A0009C7F2D /* LaunchScreen.storyboard */; };
+ 7E8E19FE249A668100BC78F9 /* DefaultCallFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E537DCB24880B9D00013D6E /* DefaultCallFailedView.swift */; };
+ 7E8E19FF249A668400BC78F9 /* DefaultCallFailedView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7E537DCD24880BA800013D6E /* DefaultCallFailedView.xib */; };
+ 7E8E1A00249A668900BC78F9 /* DefaultIncomingCallView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7E386D042488004900556D3D /* DefaultIncomingCallView.xib */; };
+ 7E8E1A01249A668C00BC78F9 /* DefaultIncomingCallView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386D022488003600556D3D /* DefaultIncomingCallView.swift */; };
+ 7E8E1A02249A669400BC78F9 /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738F812435E259004387C2 /* NibLoadable.swift */; };
+ 7E8E1A03249A669900BC78F9 /* PressableButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386D132488056000556D3D /* PressableButton.swift */; };
+ 7E8E1A04249A66A600BC78F9 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC4967DF41EEB4E5BA1A8B5 /* Log.swift */; };
+ 7E8E1A05249A66AF00BC78F9 /* DefaultLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E40244DE0FC009DDA51 /* DefaultLoginView.swift */; };
+ 7E8E1A06249A66B400BC78F9 /* DefaultLoginView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7EB67E3C244DE069009DDA51 /* DefaultLoginView.xib */; };
+ 7E8E1A07249A66BC00BC78F9 /* AlertHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9F20E0243C7323002B5700 /* AlertHelper.swift */; };
+ 7E8E1A08249A66C300BC78F9 /* ErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9A494B2451E04200F8773E /* ErrorHandling.swift */; };
+ 7E8E1A09249A66C600BC78F9 /* LoadingShowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E1A244DC327009DDA51 /* LoadingShowable.swift */; };
+ 7E8E1A0A249A66CC00BC78F9 /* DefaultMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E47560D244DEF010028495E /* DefaultMainView.swift */; };
+ 7E8E1A0B249A66D000BC78F9 /* DefaultMainView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7E475610244DEF100028495E /* DefaultMainView.xib */; };
+ 7E8E1A0C249A66D800BC78F9 /* PermissionsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92B6829E22F4492A002244B5 /* PermissionsHelper.swift */; };
+ 7E8E1A0D249A66E100BC78F9 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92C8E1F522E99419006CCCFF /* Errors.swift */; };
+ 7E8E1A0E249A66E900BC78F9 /* VoximplantSDKVersionContainable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E02244D8A8F009DDA51 /* VoximplantSDKVersionContainable.swift */; };
+ 7E8E1A0F249A66EE00BC78F9 /* MovingWithKeyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E47561B244ECC580028495E /* MovingWithKeyboard.swift */; };
+ 7E8E1A10249A66F800BC78F9 /* ColoredButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92049F8F22DCD177006DB07F /* ColoredButton.swift */; };
+ 7E8E1A11249A670000BC78F9 /* DefaultTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92F00F9122C60B7F0061AF13 /* DefaultTextField.swift */; };
+ 7E8E1A12249A670800BC78F9 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E1E244DC567009DDA51 /* Color.swift */; };
+ 7E8E1A14249A671900BC78F9 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E475627244EE41B0028495E /* Theme.swift */; };
+ 7E8E1A15249A672000BC78F9 /* UserDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E0E244DA159009DDA51 /* UserDefault.swift */; };
+ 7E8E1A16249A672700BC78F9 /* CallOptionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738F742435E259004387C2 /* CallOptionButton.swift */; };
+ 7E8E1A17249A672E00BC78F9 /* CallOptionButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E738F762435E259004387C2 /* CallOptionButtonModel.swift */; };
+ 7E8E1A18249A673900BC78F9 /* EdgeMagneticView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CF82487C09000556D3D /* EdgeMagneticView.swift */; };
+ 7E8E1A19249A673E00BC78F9 /* Tokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5E0CFD722B78633009C7F2D /* Tokens.swift */; };
+ 7E8E1A1A249A674B00BC78F9 /* Tokens+VIAuthParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9A49412451D5AD00F8773E /* Tokens+VIAuthParams.swift */; };
+ 7E8E1A1B249A67D800BC78F9 /* CallOptionButton.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7E738F752435E259004387C2 /* CallOptionButton.xib */; };
7E9A49392451BC7F00F8773E /* PushTokenHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9A49372451BC5A00F8773E /* PushTokenHolder.swift */; };
7E9A493A2451BC8000F8773E /* PushTokenHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9A49372451BC5A00F8773E /* PushTokenHolder.swift */; };
7E9A493D2451BEE800F8773E /* PushCallNotifierDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9A493B2451BCFD00F8773E /* PushCallNotifierDelegate.swift */; };
@@ -186,6 +220,23 @@
7EC3FDBC243643880021BD9E /* ConferenceParticipantView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7EC3FDBB243643880021BD9E /* ConferenceParticipantView.xib */; };
7EC3FDC124379DC40021BD9E /* LoginTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EC3FDC024379DC40021BD9E /* LoginTextField.swift */; };
7EC3FDC32437B0170021BD9E /* Shared.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 92C8E20022EB4139006CCCFF /* Shared.xcassets */; };
+ 7ECA29D124A508A100F3D1ED /* DarwinNotificationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECA29D024A508A100F3D1ED /* DarwinNotificationsService.swift */; };
+ 7ECA29D224A509CB00F3D1ED /* DarwinNotificationsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7ECA29D024A508A100F3D1ED /* DarwinNotificationsService.swift */; };
+ 7EF4B629249A4E9F00081013 /* Call.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7EF4B5E4249A4DA600081013 /* Call.storyboard */; };
+ 7EF4B62A249A4EA200081013 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7EF4B5E5249A4DA600081013 /* Main.storyboard */; };
+ 7EF4B62B249A4EA400081013 /* Storyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5E7249A4DA600081013 /* Storyboard.swift */; };
+ 7EF4B62C249A4EAC00081013 /* CallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5F9249A4DA600081013 /* CallManager.swift */; };
+ 7EF4B62D249A4EB100081013 /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5FA249A4DA600081013 /* AuthService.swift */; };
+ 7EF4B630249A4EC900081013 /* StoryAssembler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5F1249A4DA600081013 /* StoryAssembler.swift */; };
+ 7EF4B631249A4ECE00081013 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5F7249A4DA600081013 /* AppDelegate.swift */; };
+ 7EF4B632249A4ED100081013 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 7EF4B5E1249A4DA600081013 /* README.md */; };
+ 7EF4B633249A4ED600081013 /* CallFailedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5E9249A4DA600081013 /* CallFailedViewController.swift */; };
+ 7EF4B634249A4EDE00081013 /* CallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5EB249A4DA600081013 /* CallViewController.swift */; };
+ 7EF4B635249A4EE300081013 /* CallVideoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7EF4B5ED249A4DA600081013 /* CallVideoView.xib */; };
+ 7EF4B636249A4EE600081013 /* CallVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5EE249A4DA600081013 /* CallVideoView.swift */; };
+ 7EF4B637249A4EEA00081013 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5F0249A4DA600081013 /* MainViewController.swift */; };
+ 7EF4B638249A4EEE00081013 /* IncomingCallViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5F3249A4DA600081013 /* IncomingCallViewController.swift */; };
+ 7EF4B639249A4F0800081013 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF4B5F6249A4DA600081013 /* LoginViewController.swift */; };
7EF72227244F3CD000B0037E /* AudioDeviceSelecting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E37C36B244F349F00F18266 /* AudioDeviceSelecting.swift */; };
7EF7222A244F4FEA00B0037E /* CallVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EF72229244F4FEA00B0037E /* CallVideoView.swift */; };
7EF7222C244F506D00B0037E /* CallVideoView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 7EF7222B244F506D00B0037E /* CallVideoView.xib */; };
@@ -235,6 +286,16 @@
C5551BBD22C4FEC6003A90A9 /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5551BBA22C4FEC6003A90A9 /* SettingsViewController.swift */; };
C5551BBE22C4FEC6003A90A9 /* SettingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5551BBB22C4FEC6003A90A9 /* SettingTableViewCell.swift */; };
C5551BBF22C4FEC6003A90A9 /* SettingsDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5551BBC22C4FEC6003A90A9 /* SettingsDetailTableViewController.swift */; };
+ C56001612497686500818028 /* ReplayKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C56001602497686500818028 /* ReplayKit.framework */; };
+ C56001642497686500818028 /* SampleHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C56001632497686500818028 /* SampleHandler.swift */; };
+ C56001682497686500818028 /* ScreenSharingUploadAppex.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = C560015F2497686500818028 /* ScreenSharingUploadAppex.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
+ C56001832498BF1D00818028 /* Tokens.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5E0CFD722B78633009C7F2D /* Tokens.swift */; };
+ C56001842498C36200818028 /* UserDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB67E0E244DA159009DDA51 /* UserDefault.swift */; };
+ C56001852498C3C800818028 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92C8E1F522E99419006CCCFF /* Errors.swift */; };
+ C56001862498C3EF00818028 /* Tokens+VIAuthParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E9A49412451D5AD00F8773E /* Tokens+VIAuthParams.swift */; };
+ C560018924990C8100818028 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5E0CFCF22B773BC009C7F2D /* Log.swift */; };
+ C560018D2499422800818028 /* Loggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CC02487954100556D3D /* Loggable.swift */; };
+ C560018F249A023A00818028 /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E386CEF24879A2200556D3D /* AuthService.swift */; };
C58D0F4823D1A18200A337EB /* LoudAudioFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C58D0F4623D1A16E00A337EB /* LoudAudioFile.swift */; };
C590E7C32322E0F300E112D7 /* CallWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C590E7C22322E0F300E112D7 /* CallWrapper.swift */; };
C5A248C322B3F7150008FAF4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C5A248C222B3F7150008FAF4 /* Assets.xcassets */; };
@@ -278,6 +339,30 @@
CCC49FB0513FDFD9B086E1CD /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC4967DF41EEB4E5BA1A8B5 /* Log.swift */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ C56001662497686500818028 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 8EEA98A81FB5A1AC00E90861 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = C560015E2497686500818028;
+ remoteInfo = ScreenSharingUploadAppex;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ C56001692497686500818028 /* Embed App Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ C56001682497686500818028 /* ScreenSharingUploadAppex.appex in Embed App Extensions */,
+ );
+ name = "Embed App Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
/* Begin PBXFileReference section */
7E37C365244F1FD100F18266 /* CallWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallWrapper.swift; sourceTree = ""; };
7E37C369244F29C100F18266 /* CallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = ""; };
@@ -286,7 +371,6 @@
7E386CAC248793B800556D3D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7E386CB0248793B800556D3D /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
7E386CB3248793B800556D3D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
- 7E386CB5248793B900556D3D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
7E386CBA248793B900556D3D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
7E386CC02487954100556D3D /* Loggable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loggable.swift; sourceTree = ""; };
7E386CE1248798CF00556D3D /* StoryAssembler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoryAssembler.swift; sourceTree = ""; };
@@ -296,12 +380,9 @@
7E386CF32487B1F000556D3D /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; };
7E386CF52487B3D300556D3D /* CallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = ""; };
7E386CF82487C09000556D3D /* EdgeMagneticView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EdgeMagneticView.swift; sourceTree = ""; };
- 7E386CFA2487C0E400556D3D /* CallVideoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallVideoView.swift; sourceTree = ""; };
- 7E386CFD2487C12B00556D3D /* CallVideoView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CallVideoView.xib; sourceTree = ""; };
7E386D022488003600556D3D /* DefaultIncomingCallView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultIncomingCallView.swift; sourceTree = ""; };
7E386D042488004900556D3D /* DefaultIncomingCallView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DefaultIncomingCallView.xib; sourceTree = ""; };
7E386D092488040100556D3D /* Call.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Call.storyboard; sourceTree = ""; };
- 7E386D0B2488041600556D3D /* IncomingCallViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncomingCallViewController.swift; sourceTree = ""; };
7E386D132488056000556D3D /* PressableButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PressableButton.swift; sourceTree = ""; };
7E47560D244DEF010028495E /* DefaultMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultMainView.swift; sourceTree = ""; };
7E475610244DEF100028495E /* DefaultMainView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DefaultMainView.xib; sourceTree = ""; };
@@ -333,6 +414,7 @@
7E738F8F2435E259004387C2 /* ManageConferenceUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManageConferenceUseCase.swift; sourceTree = ""; };
7E738F902435E259004387C2 /* JoinConferenceUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoinConferenceUseCase.swift; sourceTree = ""; };
7E738FAA2435E297004387C2 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7E7BE47724A6012700243B8E /* BroadcastError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BroadcastError.swift; sourceTree = ""; };
7E8A831024891BC3000578C6 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
7E9A49372451BC5A00F8773E /* PushTokenHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushTokenHolder.swift; sourceTree = ""; };
7E9A493B2451BCFD00F8773E /* PushCallNotifierDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushCallNotifierDelegate.swift; sourceTree = ""; };
@@ -362,6 +444,24 @@
7EC3FDB92436433A0021BD9E /* ConferenceParticipantView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConferenceParticipantView.swift; sourceTree = ""; };
7EC3FDBB243643880021BD9E /* ConferenceParticipantView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ConferenceParticipantView.xib; sourceTree = ""; };
7EC3FDC024379DC40021BD9E /* LoginTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginTextField.swift; sourceTree = ""; };
+ 7ECA29D024A508A100F3D1ED /* DarwinNotificationsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DarwinNotificationsService.swift; sourceTree = ""; };
+ 7EF4B5E0249A4DA600081013 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 7EF4B5E1249A4DA600081013 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
+ 7EF4B5E4249A4DA600081013 /* Call.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Call.storyboard; sourceTree = ""; };
+ 7EF4B5E6249A4DA600081013 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 7EF4B5E7249A4DA600081013 /* Storyboard.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Storyboard.swift; sourceTree = ""; };
+ 7EF4B5E9249A4DA600081013 /* CallFailedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallFailedViewController.swift; sourceTree = ""; };
+ 7EF4B5EB249A4DA600081013 /* CallViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallViewController.swift; sourceTree = ""; };
+ 7EF4B5ED249A4DA600081013 /* CallVideoView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CallVideoView.xib; sourceTree = ""; };
+ 7EF4B5EE249A4DA600081013 /* CallVideoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallVideoView.swift; sourceTree = ""; };
+ 7EF4B5F0249A4DA600081013 /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; };
+ 7EF4B5F1249A4DA600081013 /* StoryAssembler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoryAssembler.swift; sourceTree = ""; };
+ 7EF4B5F3249A4DA600081013 /* IncomingCallViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IncomingCallViewController.swift; sourceTree = ""; };
+ 7EF4B5F6249A4DA600081013 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
+ 7EF4B5F7249A4DA600081013 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7EF4B5F9249A4DA600081013 /* CallManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallManager.swift; sourceTree = ""; };
+ 7EF4B5FA249A4DA600081013 /* AuthService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; };
+ 7EF4B615249A4DF500081013 /* InAppScreenSharing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = InAppScreenSharing.app; sourceTree = BUILT_PRODUCTS_DIR; };
7EF72229244F4FEA00B0037E /* CallVideoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallVideoView.swift; sourceTree = ""; };
7EF7222B244F506D00B0037E /* CallVideoView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = CallVideoView.xib; sourceTree = ""; };
8E85E3881FBB06E50033831A /* VoximplantDemo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = VoximplantDemo.entitlements; sourceTree = ""; };
@@ -397,6 +497,13 @@
C5551BBA22C4FEC6003A90A9 /* SettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; };
C5551BBB22C4FEC6003A90A9 /* SettingTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingTableViewCell.swift; sourceTree = ""; };
C5551BBC22C4FEC6003A90A9 /* SettingsDetailTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsDetailTableViewController.swift; sourceTree = ""; };
+ C560015F2497686500818028 /* ScreenSharingUploadAppex.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ScreenSharingUploadAppex.appex; sourceTree = BUILT_PRODUCTS_DIR; };
+ C56001602497686500818028 /* ReplayKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReplayKit.framework; path = System/Library/Frameworks/ReplayKit.framework; sourceTree = SDKROOT; };
+ C56001632497686500818028 /* SampleHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleHandler.swift; sourceTree = ""; };
+ C56001652497686500818028 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ C56001732497687A00818028 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ C56001872498CD1100818028 /* ScreenSharing.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ScreenSharing.entitlements; sourceTree = ""; };
+ C56001882498CD3800818028 /* ScreenSharingUploadAppex.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ScreenSharingUploadAppex.entitlements; sourceTree = ""; };
C58D0F4623D1A16E00A337EB /* LoudAudioFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoudAudioFile.swift; sourceTree = ""; };
C590E7C22322E0F300E112D7 /* CallWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallWrapper.swift; sourceTree = ""; };
C5A248B922B3F7140008FAF4 /* AudioCall.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AudioCall.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -469,6 +576,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 7EF4B612249A4DF500081013 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8EEA98AD1FB5A1AD00E90861 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -476,6 +590,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ C560015C2497686500818028 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C56001612497686500818028 /* ReplayKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
C5A248B622B3F7140008FAF4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -497,6 +619,8 @@
isa = PBXGroup;
children = (
92DB9E612333C8B5007BF73C /* PushKit.framework */,
+ C56001602497686500818028 /* ReplayKit.framework */,
+ C56001732497687A00818028 /* UIKit.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -545,6 +669,7 @@
7E386CAB248793B800556D3D /* ScreenSharing */ = {
isa = PBXGroup;
children = (
+ C56001872498CD1100818028 /* ScreenSharing.entitlements */,
7E8A831024891BC3000578C6 /* README.md */,
7E386CAC248793B800556D3D /* AppDelegate.swift */,
7E386CE0248798C300556D3D /* Stories */,
@@ -558,39 +683,13 @@
isa = PBXGroup;
children = (
7E386CE1248798CF00556D3D /* StoryAssembler.swift */,
- 7E386CE3248798E500556D3D /* Login */,
- 7E386CE4248798F000556D3D /* Main */,
- 7E386D122488049400556D3D /* IncomingCall */,
- 7E537DC724880B0C00013D6E /* CallFailed */,
- 7E386CE5248798F500556D3D /* Call */,
- 7E386CE6248798FA00556D3D /* Storyboards */,
- );
- path = Stories;
- sourceTree = "";
- };
- 7E386CE3248798E500556D3D /* Login */ = {
- isa = PBXGroup;
- children = (
7E386CB0248793B800556D3D /* LoginViewController.swift */,
- );
- path = Login;
- sourceTree = "";
- };
- 7E386CE4248798F000556D3D /* Main */ = {
- isa = PBXGroup;
- children = (
7E386CF32487B1F000556D3D /* MainViewController.swift */,
- );
- path = Main;
- sourceTree = "";
- };
- 7E386CE5248798F500556D3D /* Call */ = {
- isa = PBXGroup;
- children = (
- 7E386CFC2487C0EA00556D3D /* CallVideoView */,
+ 7E537DC824880B3300013D6E /* CallFailedViewController.swift */,
7E386CF52487B3D300556D3D /* CallViewController.swift */,
+ 7E386CE6248798FA00556D3D /* Storyboards */,
);
- path = Call;
+ path = Stories;
sourceTree = "";
};
7E386CE6248798FA00556D3D /* Storyboards */ = {
@@ -606,7 +705,6 @@
7E386CE92487993800556D3D /* Resources */ = {
isa = PBXGroup;
children = (
- 7E386CB5248793B900556D3D /* Assets.xcassets */,
7E386CBA248793B900556D3D /* Info.plist */,
);
path = Resources;
@@ -617,19 +715,11 @@
children = (
7E386CEF24879A2200556D3D /* AuthService.swift */,
7E386CF12487A3F600556D3D /* CallManager.swift */,
+ 7ECA29D024A508A100F3D1ED /* DarwinNotificationsService.swift */,
);
path = Services;
sourceTree = "";
};
- 7E386CFC2487C0EA00556D3D /* CallVideoView */ = {
- isa = PBXGroup;
- children = (
- 7E386CFD2487C12B00556D3D /* CallVideoView.xib */,
- 7E386CFA2487C0E400556D3D /* CallVideoView.swift */,
- );
- path = CallVideoView;
- sourceTree = "";
- };
7E386D012488002200556D3D /* IncomingCallView */ = {
isa = PBXGroup;
children = (
@@ -639,14 +729,6 @@
path = IncomingCallView;
sourceTree = "";
};
- 7E386D122488049400556D3D /* IncomingCall */ = {
- isa = PBXGroup;
- children = (
- 7E386D0B2488041600556D3D /* IncomingCallViewController.swift */,
- );
- path = IncomingCall;
- sourceTree = "";
- };
7E47560F244DEF050028495E /* MainView */ = {
isa = PBXGroup;
children = (
@@ -656,21 +738,22 @@
path = MainView;
sourceTree = "";
};
- 7E537DC724880B0C00013D6E /* CallFailed */ = {
+ 7E537DCA24880B8F00013D6E /* CallFailedView */ = {
isa = PBXGroup;
children = (
- 7E537DC824880B3300013D6E /* CallFailedViewController.swift */,
+ 7E537DCB24880B9D00013D6E /* DefaultCallFailedView.swift */,
+ 7E537DCD24880BA800013D6E /* DefaultCallFailedView.xib */,
);
- path = CallFailed;
+ path = CallFailedView;
sourceTree = "";
};
- 7E537DCA24880B8F00013D6E /* CallFailedView */ = {
+ 7E616724249A17700066C43B /* ConferenceView */ = {
isa = PBXGroup;
children = (
- 7E537DCB24880B9D00013D6E /* DefaultCallFailedView.swift */,
- 7E537DCD24880BA800013D6E /* DefaultCallFailedView.xib */,
+ 7E738F772435E259004387C2 /* ConferenceView.swift */,
+ 7EC3FDBD243738BE0021BD9E /* ConferenceParticipantView */,
);
- path = CallFailedView;
+ path = ConferenceView;
sourceTree = "";
};
7E738F5A2435DF92004387C2 /* Conference */ = {
@@ -702,8 +785,6 @@
7E738F712435E259004387C2 /* ConferenceCall */ = {
isa = PBXGroup;
children = (
- 7E738F772435E259004387C2 /* ConferenceView.swift */,
- 7EC3FDBD243738BE0021BD9E /* ConferenceParticipantView */,
7E738F792435E259004387C2 /* ConferenceCallViewController.swift */,
);
path = ConferenceCall;
@@ -862,6 +943,109 @@
path = ConferenceParticipantView;
sourceTree = "";
};
+ 7EF4B5DE249A4DA600081013 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5E0249A4DA600081013 /* Info.plist */,
+ );
+ path = Resources;
+ sourceTree = "";
+ };
+ 7EF4B5E2249A4DA600081013 /* Stories */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5F1249A4DA600081013 /* StoryAssembler.swift */,
+ 7EF4B5F4249A4DA600081013 /* Login */,
+ 7EF4B5EF249A4DA600081013 /* Main */,
+ 7EF4B5F2249A4DA600081013 /* IncomingCall */,
+ 7EF4B5E8249A4DA600081013 /* CallFailed */,
+ 7EF4B5EA249A4DA600081013 /* Call */,
+ 7EF4B5E3249A4DA600081013 /* Storyboards */,
+ );
+ path = Stories;
+ sourceTree = "";
+ };
+ 7EF4B5E3249A4DA600081013 /* Storyboards */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5E5249A4DA600081013 /* Main.storyboard */,
+ 7EF4B5E4249A4DA600081013 /* Call.storyboard */,
+ 7EF4B5E7249A4DA600081013 /* Storyboard.swift */,
+ );
+ path = Storyboards;
+ sourceTree = "";
+ };
+ 7EF4B5E8249A4DA600081013 /* CallFailed */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5E9249A4DA600081013 /* CallFailedViewController.swift */,
+ );
+ path = CallFailed;
+ sourceTree = "";
+ };
+ 7EF4B5EA249A4DA600081013 /* Call */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5EB249A4DA600081013 /* CallViewController.swift */,
+ 7EF4B5EC249A4DA600081013 /* CallVideoView */,
+ );
+ path = Call;
+ sourceTree = "";
+ };
+ 7EF4B5EC249A4DA600081013 /* CallVideoView */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5ED249A4DA600081013 /* CallVideoView.xib */,
+ 7EF4B5EE249A4DA600081013 /* CallVideoView.swift */,
+ );
+ path = CallVideoView;
+ sourceTree = "";
+ };
+ 7EF4B5EF249A4DA600081013 /* Main */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5F0249A4DA600081013 /* MainViewController.swift */,
+ );
+ path = Main;
+ sourceTree = "";
+ };
+ 7EF4B5F2249A4DA600081013 /* IncomingCall */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5F3249A4DA600081013 /* IncomingCallViewController.swift */,
+ );
+ path = IncomingCall;
+ sourceTree = "";
+ };
+ 7EF4B5F4249A4DA600081013 /* Login */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5F6249A4DA600081013 /* LoginViewController.swift */,
+ );
+ path = Login;
+ sourceTree = "";
+ };
+ 7EF4B5F8249A4DA600081013 /* Services */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5F9249A4DA600081013 /* CallManager.swift */,
+ 7EF4B5FA249A4DA600081013 /* AuthService.swift */,
+ );
+ path = Services;
+ sourceTree = "";
+ };
+ 7EF4B616249A4DF500081013 /* InAppScreenSharing */ = {
+ isa = PBXGroup;
+ children = (
+ 7EF4B5F7249A4DA600081013 /* AppDelegate.swift */,
+ 7EF4B5E1249A4DA600081013 /* README.md */,
+ 7EF4B5E2249A4DA600081013 /* Stories */,
+ 7EF4B5F8249A4DA600081013 /* Services */,
+ 7EF4B5DE249A4DA600081013 /* Resources */,
+ );
+ path = InAppScreenSharing;
+ sourceTree = "";
+ };
7EF72228244F4FD300B0037E /* CallVideoView */ = {
isa = PBXGroup;
children = (
@@ -909,6 +1093,8 @@
7E738F5A2435DF92004387C2 /* Conference */,
7EB67DE8244D8543009DDA51 /* VideoCallKit */,
7E386CAB248793B800556D3D /* ScreenSharing */,
+ C56001622497686500818028 /* ScreenSharingUploadAppex */,
+ 7EF4B616249A4DF500081013 /* InAppScreenSharing */,
8EEA98B11FB5A1AD00E90861 /* Products */,
369334A608BD108D7AD7AA32 /* Pods */,
1DD0F974DCACD349A4CA3C60 /* Frameworks */,
@@ -924,6 +1110,8 @@
7E738F592435DF92004387C2 /* Conference.app */,
7EB67DE7244D8543009DDA51 /* VideoCallKit.app */,
7E386CAA248793B800556D3D /* ScreenSharing.app */,
+ C560015F2497686500818028 /* ScreenSharingUploadAppex.appex */,
+ 7EF4B615249A4DF500081013 /* InAppScreenSharing.app */,
);
name = Products;
sourceTree = "";
@@ -993,6 +1181,7 @@
7E537DCA24880B8F00013D6E /* CallFailedView */,
7E386D012488002200556D3D /* IncomingCallView */,
7E738F732435E259004387C2 /* CallOptionButton */,
+ 7E616724249A17700066C43B /* ConferenceView */,
7E386CF82487C09000556D3D /* EdgeMagneticView.swift */,
92049F8F22DCD177006DB07F /* ColoredButton.swift */,
7E386D132488056000556D3D /* PressableButton.swift */,
@@ -1037,6 +1226,17 @@
path = Fonts;
sourceTree = "";
};
+ C56001622497686500818028 /* ScreenSharingUploadAppex */ = {
+ isa = PBXGroup;
+ children = (
+ C56001882498CD3800818028 /* ScreenSharingUploadAppex.entitlements */,
+ C56001632497686500818028 /* SampleHandler.swift */,
+ C56001652497686500818028 /* Info.plist */,
+ 7E7BE47724A6012700243B8E /* BroadcastError.swift */,
+ );
+ path = ScreenSharingUploadAppex;
+ sourceTree = "";
+ };
C5A248BA22B3F7140008FAF4 /* AudioCall */ = {
isa = PBXGroup;
children = (
@@ -1155,10 +1355,12 @@
7E386CA6248793B800556D3D /* Sources */,
7E386CA7248793B800556D3D /* Frameworks */,
7E386CA8248793B800556D3D /* Resources */,
+ C56001692497686500818028 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
+ C56001672497686500818028 /* PBXTargetDependency */,
);
name = ScreenSharing;
productName = ScreenSharing;
@@ -1199,6 +1401,23 @@
productReference = 7EB67DE7244D8543009DDA51 /* VideoCallKit.app */;
productType = "com.apple.product-type.application";
};
+ 7EF4B614249A4DF500081013 /* InAppScreenSharing */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 7EF4B626249A4DF700081013 /* Build configuration list for PBXNativeTarget "InAppScreenSharing" */;
+ buildPhases = (
+ 7EF4B611249A4DF500081013 /* Sources */,
+ 7EF4B612249A4DF500081013 /* Frameworks */,
+ 7EF4B613249A4DF500081013 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = InAppScreenSharing;
+ productName = InAppScreenSharing;
+ productReference = 7EF4B615249A4DF500081013 /* InAppScreenSharing.app */;
+ productType = "com.apple.product-type.application";
+ };
8EEA98AF1FB5A1AD00E90861 /* Voximplant Demo */ = {
isa = PBXNativeTarget;
buildConfigurationList = 8EEA98C21FB5A1AD00E90861 /* Build configuration list for PBXNativeTarget "Voximplant Demo" */;
@@ -1216,6 +1435,23 @@
productReference = 8EEA98B01FB5A1AD00E90861 /* Voximplant Demo.app */;
productType = "com.apple.product-type.application";
};
+ C560015E2497686500818028 /* ScreenSharingUploadAppex */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = C560016C2497686500818028 /* Build configuration list for PBXNativeTarget "ScreenSharingUploadAppex" */;
+ buildPhases = (
+ C560015B2497686500818028 /* Sources */,
+ C560015C2497686500818028 /* Frameworks */,
+ C560015D2497686500818028 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ScreenSharingUploadAppex;
+ productName = ScreenSharingUploadAppex;
+ productReference = C560015F2497686500818028 /* ScreenSharingUploadAppex.appex */;
+ productType = "com.apple.product-type.app-extension";
+ };
C5A248B822B3F7140008FAF4 /* AudioCall */ = {
isa = PBXNativeTarget;
buildConfigurationList = C5A248CA22B3F7150008FAF4 /* Build configuration list for PBXNativeTarget "AudioCall" */;
@@ -1272,6 +1508,10 @@
CreatedOnToolsVersion = 11.4.1;
ProvisioningStyle = Automatic;
};
+ 7EF4B614249A4DF500081013 = {
+ CreatedOnToolsVersion = 11.5;
+ ProvisioningStyle = Automatic;
+ };
8EEA98AF1FB5A1AD00E90861 = {
CreatedOnToolsVersion = 9.1;
LastSwiftMigration = 1000;
@@ -1285,6 +1525,10 @@
};
};
};
+ C560015E2497686500818028 = {
+ CreatedOnToolsVersion = 11.3.1;
+ ProvisioningStyle = Automatic;
+ };
C5A248B822B3F7140008FAF4 = {
CreatedOnToolsVersion = 10.2.1;
ProvisioningStyle = Automatic;
@@ -1319,6 +1563,8 @@
7E738F582435DF92004387C2 /* Conference */,
7EB67DE6244D8543009DDA51 /* VideoCallKit */,
7E386CA9248793B800556D3D /* ScreenSharing */,
+ C560015E2497686500818028 /* ScreenSharingUploadAppex */,
+ 7EF4B614249A4DF500081013 /* InAppScreenSharing */,
);
};
/* End PBXProject section */
@@ -1329,13 +1575,12 @@
buildActionMask = 2147483647;
files = (
7E537DCE24880BA800013D6E /* DefaultCallFailedView.xib in Resources */,
- 7E386CFE2487C12B00556D3D /* CallVideoView.xib in Resources */,
7E386CED248799AA00556D3D /* LaunchScreen.storyboard in Resources */,
7E386D0A2488040100556D3D /* Call.storyboard in Resources */,
7E386CD1248797CE00556D3D /* DefaultLoginView.xib in Resources */,
- 7E386CB6248793B900556D3D /* Assets.xcassets in Resources */,
7E8A831124891BC3000578C6 /* README.md in Resources */,
7E386CD5248797E000556D3D /* CallOptionButton.xib in Resources */,
+ 7E616727249A178A0066C43B /* ConferenceParticipantView.xib in Resources */,
7E386CD3248797D500556D3D /* DefaultMainView.xib in Resources */,
7E386D08248802C200556D3D /* DefaultIncomingCallView.xib in Resources */,
7E386CCF248797BE00556D3D /* Shared.xcassets in Resources */,
@@ -1373,6 +1618,24 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 7EF4B613249A4DF500081013 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7EF4B629249A4E9F00081013 /* Call.storyboard in Resources */,
+ 7E8E19FC249A661700BC78F9 /* Shared.xcassets in Resources */,
+ 7EF4B632249A4ED100081013 /* README.md in Resources */,
+ 7E8E19FD249A661E00BC78F9 /* LaunchScreen.storyboard in Resources */,
+ 7E8E1A00249A668900BC78F9 /* DefaultIncomingCallView.xib in Resources */,
+ 7E8E1A0B249A66D000BC78F9 /* DefaultMainView.xib in Resources */,
+ 7E8E1A1B249A67D800BC78F9 /* CallOptionButton.xib in Resources */,
+ 7E8E19FF249A668400BC78F9 /* DefaultCallFailedView.xib in Resources */,
+ 7E8E1A06249A66B400BC78F9 /* DefaultLoginView.xib in Resources */,
+ 7EF4B62A249A4EA200081013 /* Main.storyboard in Resources */,
+ 7EF4B635249A4EE300081013 /* CallVideoView.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8EEA98AE1FB5A1AD00E90861 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1392,6 +1655,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ C560015D2497686500818028 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
C5A248B722B3F7140008FAF4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1449,19 +1719,20 @@
7E386CE82487991D00556D3D /* Storyboard.swift in Sources */,
7E386CD92487980F00556D3D /* UIExtensions.swift in Sources */,
7E386CE2248798CF00556D3D /* StoryAssembler.swift in Sources */,
- 7E386D0C2488041600556D3D /* IncomingCallViewController.swift in Sources */,
7E386CD7248797E600556D3D /* ColoredButton.swift in Sources */,
7E386CF92487C09000556D3D /* EdgeMagneticView.swift in Sources */,
7E386CCC248797A000556D3D /* ErrorHandling.swift in Sources */,
+ 7E616726249A17870066C43B /* ConferenceParticipantView.swift in Sources */,
7E386CCA2487977000556D3D /* MovingWithKeyboard.swift in Sources */,
7E386CDF2487983600556D3D /* UserDefault.swift in Sources */,
+ 7ECA29D124A508A100F3D1ED /* DarwinNotificationsService.swift in Sources */,
7E386CB1248793B800556D3D /* LoginViewController.swift in Sources */,
7E55DF37248829880067A214 /* AppLifeCycleDelegate.swift in Sources */,
+ 7E616725249A17820066C43B /* ConferenceView.swift in Sources */,
7E386CF42487B1F000556D3D /* MainViewController.swift in Sources */,
7E386CCD248797B300556D3D /* Color.swift in Sources */,
7E386D142488056000556D3D /* PressableButton.swift in Sources */,
7E386CDA2487981100556D3D /* AlertHelper.swift in Sources */,
- 7E386CFB2487C0E400556D3D /* CallVideoView.swift in Sources */,
7E537DC924880B3300013D6E /* CallFailedViewController.swift in Sources */,
7E386CDC2487981800556D3D /* Tokens+VIAuthParams.swift in Sources */,
7E386CDD2487981E00556D3D /* PermissionsHelper.swift in Sources */,
@@ -1565,6 +1836,51 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 7EF4B611249A4DF500081013 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7EF4B62D249A4EB100081013 /* AuthService.swift in Sources */,
+ 7E8E1A02249A669400BC78F9 /* NibLoadable.swift in Sources */,
+ 7E8E1A10249A66F800BC78F9 /* ColoredButton.swift in Sources */,
+ 7E8E19FB249A4F7300BC78F9 /* UIExtensions.swift in Sources */,
+ 7EF4B62B249A4EA400081013 /* Storyboard.swift in Sources */,
+ 7E8E19FE249A668100BC78F9 /* DefaultCallFailedView.swift in Sources */,
+ 7E8E1A16249A672700BC78F9 /* CallOptionButton.swift in Sources */,
+ 7E8E1A0A249A66CC00BC78F9 /* DefaultMainView.swift in Sources */,
+ 7E8E1A1A249A674B00BC78F9 /* Tokens+VIAuthParams.swift in Sources */,
+ 7E8E1A0C249A66D800BC78F9 /* PermissionsHelper.swift in Sources */,
+ 7EF4B630249A4EC900081013 /* StoryAssembler.swift in Sources */,
+ 7EF4B633249A4ED600081013 /* CallFailedViewController.swift in Sources */,
+ 7E8E19FA249A4F6600BC78F9 /* AppLifeCycleDelegate.swift in Sources */,
+ 7EF4B636249A4EE600081013 /* CallVideoView.swift in Sources */,
+ 7E8E1A19249A673E00BC78F9 /* Tokens.swift in Sources */,
+ 7E8E1A0E249A66E900BC78F9 /* VoximplantSDKVersionContainable.swift in Sources */,
+ 7E8E1A11249A670000BC78F9 /* DefaultTextField.swift in Sources */,
+ 7E8E1A15249A672000BC78F9 /* UserDefault.swift in Sources */,
+ 7E8E1A0F249A66EE00BC78F9 /* MovingWithKeyboard.swift in Sources */,
+ 7E8E1A03249A669900BC78F9 /* PressableButton.swift in Sources */,
+ 7E8E1A01249A668C00BC78F9 /* DefaultIncomingCallView.swift in Sources */,
+ 7E8E1A17249A672E00BC78F9 /* CallOptionButtonModel.swift in Sources */,
+ 7EF4B631249A4ECE00081013 /* AppDelegate.swift in Sources */,
+ 7E8E19F9249A4F6000BC78F9 /* Loggable.swift in Sources */,
+ 7E8E1A12249A670800BC78F9 /* Color.swift in Sources */,
+ 7EF4B637249A4EEA00081013 /* MainViewController.swift in Sources */,
+ 7E8E1A04249A66A600BC78F9 /* Log.swift in Sources */,
+ 7EF4B62C249A4EAC00081013 /* CallManager.swift in Sources */,
+ 7EF4B638249A4EEE00081013 /* IncomingCallViewController.swift in Sources */,
+ 7E8E1A08249A66C300BC78F9 /* ErrorHandling.swift in Sources */,
+ 7E8E1A14249A671900BC78F9 /* Theme.swift in Sources */,
+ 7E8E1A18249A673900BC78F9 /* EdgeMagneticView.swift in Sources */,
+ 7E8E1A0D249A66E100BC78F9 /* Errors.swift in Sources */,
+ 7EF4B634249A4EDE00081013 /* CallViewController.swift in Sources */,
+ 7E8E1A07249A66BC00BC78F9 /* AlertHelper.swift in Sources */,
+ 7E8E1A09249A66C600BC78F9 /* LoadingShowable.swift in Sources */,
+ 7E8E1A05249A66AF00BC78F9 /* DefaultLoginView.swift in Sources */,
+ 7EF4B639249A4F0800081013 /* LoginViewController.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
8EEA98AC1FB5A1AD00E90861 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1591,6 +1907,23 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ C560015B2497686500818028 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ C56001852498C3C800818028 /* Errors.swift in Sources */,
+ C56001642497686500818028 /* SampleHandler.swift in Sources */,
+ 7ECA29D224A509CB00F3D1ED /* DarwinNotificationsService.swift in Sources */,
+ C560018D2499422800818028 /* Loggable.swift in Sources */,
+ C560018924990C8100818028 /* Log.swift in Sources */,
+ C56001832498BF1D00818028 /* Tokens.swift in Sources */,
+ C56001862498C3EF00818028 /* Tokens+VIAuthParams.swift in Sources */,
+ 7E7BE47824A6012700243B8E /* BroadcastError.swift in Sources */,
+ C56001842498C36200818028 /* UserDefault.swift in Sources */,
+ C560018F249A023A00818028 /* AuthService.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
C5A248B522B3F7140008FAF4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -1682,6 +2015,14 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ C56001672497686500818028 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = C560015E2497686500818028 /* ScreenSharingUploadAppex */;
+ targetProxy = C56001662497686500818028 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
7E386CB2248793B800556D3D /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -1707,16 +2048,26 @@
name = Main.storyboard;
sourceTree = "";
};
+ 7EF4B5E5249A4DA600081013 /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 7EF4B5E6249A4DA600081013 /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
7E386CBB248793B900556D3D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_ENTITLEMENTS = ScreenSharing/ScreenSharing.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 2;
+ CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = W9BHJBL635;
INFOPLIST_FILE = "$(SRCROOT)/ScreenSharing/Resources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
@@ -1733,10 +2084,12 @@
7E386CBC248793B900556D3D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_ENTITLEMENTS = ScreenSharing/ScreenSharing.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 2;
+ CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = W9BHJBL635;
INFOPLIST_FILE = "$(SRCROOT)/ScreenSharing/Resources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
@@ -1831,6 +2184,47 @@
};
name = Release;
};
+ 7EF4B627249A4DF700081013 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 3;
+ DEVELOPMENT_TEAM = W9BHJBL635;
+ INFOPLIST_FILE = "$(SRCROOT)/InAppScreenSharing/Resources/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ MARKETING_VERSION = 1.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = com.voximplant.demos.InAppScreenSharing;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 7EF4B628249A4DF700081013 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 3;
+ DEVELOPMENT_TEAM = W9BHJBL635;
+ INFOPLIST_FILE = "$(SRCROOT)/InAppScreenSharing/Resources/Info.plist";
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ MARKETING_VERSION = 1.0;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = com.voximplant.demos.InAppScreenSharing;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
8EEA98C01FB5A1AD00E90861 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1989,6 +2383,47 @@
};
name = Release;
};
+ C560016A2497686500818028 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_ENTITLEMENTS = ScreenSharingUploadAppex/ScreenSharingUploadAppex.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = W9BHJBL635;
+ INFOPLIST_FILE = ScreenSharingUploadAppex/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = com.voximplant.demos.ScreenSharing.ScreenSharingUploadAppex;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ C560016B2497686500818028 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_ENTITLEMENTS = ScreenSharingUploadAppex/ScreenSharingUploadAppex.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 4;
+ DEVELOPMENT_TEAM = W9BHJBL635;
+ INFOPLIST_FILE = ScreenSharingUploadAppex/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
+ MTL_FAST_MATH = YES;
+ PRODUCT_BUNDLE_IDENTIFIER = com.voximplant.demos.ScreenSharing.ScreenSharingUploadAppex;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
C5A248C822B3F7150008FAF4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -2109,6 +2544,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 7EF4B626249A4DF700081013 /* Build configuration list for PBXNativeTarget "InAppScreenSharing" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7EF4B627249A4DF700081013 /* Debug */,
+ 7EF4B628249A4DF700081013 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
8EEA98AB1FB5A1AC00E90861 /* Build configuration list for PBXProject "Swift" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -2127,6 +2571,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ C560016C2497686500818028 /* Build configuration list for PBXNativeTarget "ScreenSharingUploadAppex" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C560016A2497686500818028 /* Debug */,
+ C560016B2497686500818028 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
C5A248CA22B3F7150008FAF4 /* Build configuration list for PBXNativeTarget "AudioCall" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/Swift.xcodeproj/xcshareddata/xcschemes/ScreenSharing.xcscheme b/Swift.xcodeproj/xcshareddata/xcschemes/ScreenSharing.xcscheme
new file mode 100644
index 0000000..db5b7b8
--- /dev/null
+++ b/Swift.xcodeproj/xcshareddata/xcschemes/ScreenSharing.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Swift.xcodeproj/xcshareddata/xcschemes/ScreenSharingUploadAppex.xcscheme b/Swift.xcodeproj/xcshareddata/xcschemes/ScreenSharingUploadAppex.xcscheme
new file mode 100644
index 0000000..68ae189
--- /dev/null
+++ b/Swift.xcodeproj/xcshareddata/xcschemes/ScreenSharingUploadAppex.xcscheme
@@ -0,0 +1,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/VideoCallKit/Resources/Info.plist b/VideoCallKit/Resources/Info.plist
index 2d6ce4d..971cbc2 100644
--- a/VideoCallKit/Resources/Info.plist
+++ b/VideoCallKit/Resources/Info.plist
@@ -33,8 +33,6 @@
UILaunchStoryboardName
LaunchScreen
- UIMainStoryboardFile
- Main
UIRequiredDeviceCapabilities
armv7