{"id":19845473,"url":"https://github.com/opentok/accelerator-sample-apps-ios","last_synced_at":"2026-03-14T20:02:32.948Z","repository":{"id":56330246,"uuid":"83855084","full_name":"opentok/accelerator-sample-apps-ios","owner":"opentok","description":"A comprehensive sample app built by OpenTok Accelerator Packs","archived":false,"fork":false,"pushed_at":"2022-11-08T17:33:52.000Z","size":141,"stargazers_count":10,"open_issues_count":2,"forks_count":8,"subscribers_count":27,"default_branch":"main","last_synced_at":"2025-05-01T21:31:56.282Z","etag":null,"topics":["audio","communication","ios","objective-c","opentok","real-time","real-time-video-streaming","realtime","realtime-audio","screen-capture","swift","tokbox","video","video-communication","videocall","webrtc"],"latest_commit_sha":null,"homepage":"https://tokbox.com/","language":"Objective-C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opentok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-04T01:07:15.000Z","updated_at":"2022-11-08T17:33:53.000Z","dependencies_parsed_at":"2022-08-15T16:50:16.748Z","dependency_job_id":null,"html_url":"https://github.com/opentok/accelerator-sample-apps-ios","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/opentok/accelerator-sample-apps-ios","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opentok%2Faccelerator-sample-apps-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opentok%2Faccelerator-sample-apps-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opentok%2Faccelerator-sample-apps-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opentok%2Faccelerator-sample-apps-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opentok","download_url":"https://codeload.github.com/opentok/accelerator-sample-apps-ios/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opentok%2Faccelerator-sample-apps-ios/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270820793,"owners_count":24651534,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-17T02:00:09.016Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","communication","ios","objective-c","opentok","real-time","real-time-video-streaming","realtime","realtime-audio","screen-capture","swift","tokbox","video","video-communication","videocall","webrtc"],"created_at":"2024-11-12T13:08:01.122Z","updated_at":"2026-03-14T20:02:32.849Z","avatar_url":"https://github.com/opentok.png","language":"Objective-C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Accelerator Sample App for iOS\n\n\u003cimg src=\"https://assets.tokbox.com/img/vonage/Vonage_VideoAPI_black.svg\" height=\"48px\" alt=\"Tokbox is now known as Vonage\" /\u003e\n\n## Quick start\n\nThis app is built using [accelerator-core-ios](https://github.com/opentok/accelerator-core-ios) and the following accelerator packs:\n\n- [TextChat](https://github.com/opentok/accelerator-textchat-ios)\n- [Annotation](https://github.com/opentok/accelerator-annotation-ios)\n\n### Install the project files\n\nUse CocoaPods to install the project files and dependencies.\n\n1. Install CocoaPods as described in [CocoaPods Getting Started](https://guides.cocoapods.org/using/getting-started.html#getting-started).\n1. In Terminal, `cd` to your project directory and type `pod install`. (Sometimes, `pod update` is magical)\n1. Reopen your project in Xcode using the new `*.xcworkspace` file.\n\n### Configure and build the app\n\nConfigure the sample app code. Then, build and run the app.\n\n1. The application **requires** values for **API Key**, **Session ID**, and **Token**. In the sample, you can get these values at the [OpenTok Developer Dashboard](https://dashboard.tokbox.com/). For production deployment, you must generate the **Session ID** and **Token** values using one of the [OpenTok Server SDKs](https://tokbox.com/developer/sdks/server/).\n\n1. Replace the following empty strings with the corresponding **API Key**, **Session ID**, and **Token** values:\n\n    ```objc\n    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n        self.acceleratorSession = [[OTAcceleratorSession alloc] initWithOpenTokApiKey:\u003c#apikey#\u003e\n                                                                            sessionId:\u003c#sessionid#\u003e\n                                                                                token:\u003c#token#\u003e];\n        return YES;\n    }\n    ```\n    ```swift\n     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -\u003e Bool {\n        \n        session = OTAcceleratorSession.init(openTokApiKey: \u003c#apikey#\u003e, sessionId: \u003c#sessionid#\u003e, token: \u003c#token#\u003e)\n        return true\n    }\n    ```\n\n1. Use Xcode to build and run the app on an iOS simulator or device.\n\n## Exploring the code\n\nThis section shows you how to prepare, build, and run the sample application. Example code is added in [Objective-C](https://github.com/opentok/accelerator-sample-apps-ios/tree/master/AcceleratorSample) and [Swift](https://github.com/opentok/accelerator-sample-apps-ios/tree/master/AcceleratorSampleApp-Swift). With the sample application you can:\n\n- [Start a Audio/Video Call](#call)\n- [Send text messages](#textchat)\n- [Share your screen](#screenshare)\n- [Annotate on the screen](#annotation)\n\nFor details about developing with the SDK and the APIs this sample uses, see the [OpenTok iOS SDK Requirements](https://tokbox.com/developer/sdks/ios/) and the [OpenTok iOS SDK Reference](https://tokbox.com/developer/sdks/ios/reference/).\n\n_**NOTE:** This sample app collects anonymous usage data for internal TokBox purposes only. Please do not modify or remove any logging code from this sample application._\n\n\n###Call\n\nWhen the call button is pressed `OTMultiPartyCommunicator` initiates the connection to the OpenTok session and sets up the listeners for the publisher and subscriber streams:\n\n\n```objc\n// start call\n[SVProgressHUD show];\n__weak MainViewController *weakSelf = self;\n[self.multipartyCommunicator connectWithHandler:^(OTCommunicationSignal signal, OTMultiPartyRemote *subscriber, NSError *error) {\n    if (!error) {\n        [weakSelf handleCommunicationSignal:signal remote:subscriber];\n    }\n    else {\n        [SVProgressHUD showErrorWithStatus:error.localizedDescription];\n    }\n}];\n```\n\n```swift\n// start call\nSVProgressHUD.show()\nmultipartyCommunicator.connect {\n    [unowned self] (signal, remote, error) in\n    \n    guard error == nil else {\n        SVProgressHUD.showError(withStatus: error!.localizedDescription)\n        return\n    }\n    self.handleCommunicationSignal(signal, remote: remote)\n}\n```\n\n\nThe remote connection to the subscriber is handled according to the signal obtained:\n\n```objc\n- (void)handleCommunicationSignal:(OTCommunicationSignal)signal\n                        remote:(OTMultiPartyRemote *)remote {\n    switch (signal) {\n        case OTPublisherCreated: {  // join a call\n            [SVProgressHUD popActivity];\n            self.multipartyCommunicator.publisherView.showAudioVideoControl = NO;\n            [self.mainView enableControlButtonsForCall:YES];\n            [self.mainView connectCallHolder:self.multipartyCommunicator.isCallEnabled];\n            [self.mainView addPublisherView:self.multipartyCommunicator.publisherView];\n            break;\n        }\n        case OTSubscriberCreated: { // one participant is ready to join\n            [SVProgressHUD show];\n        }\n        case OTSubscriberReady: {   // one participant joins\n            [SVProgressHUD popActivity];\n            if (![self.subscribers containsObject:remote]) {\n                [self.subscribers addObject:remote];\n                [self.mainView updateSubscriberViews:self.subscribers\n                                       publisherView:self.multipartyCommunicator.publisherView];\n            }\n            break;\n        }\n        case OTSubscriberDestroyed:{    // one participant leaves\n            if ([self.subscribers containsObject:remote]) {\n                [self.subscribers removeObject:remote];\n                [self.mainView updateSubscriberViews:self.subscribers\n                                       publisherView:self.multipartyCommunicator.publisherView];\n            }\n            break;\n        }\n        ...\n    }\n}\n``` \n\n```swift\nfileprivate func handleCommunicationSignal(_ signal: OTCommunicationSignal, remote: OTMultiPartyRemote?) {\n    switch signal {\n    case .publisherCreated: // join a call\n        \n        guard let multipartyCommunicator = multipartyCommunicator else {break}\n        SVProgressHUD.popActivity()\n        multipartyCommunicator.publisherView.showAudioVideoControl = false\n        mainView.enableControlButtonsForCall(enabled: true)\n        mainView.connectCallHolder(connected: multipartyCommunicator.isCallEnabled)\n        mainView.addPublisherView(multipartyCommunicator.publisherView)\n        \n    case .subscriberReady:  // one participant joins\n        SVProgressHUD.popActivity()\n        if let remote = remote, subscribers.firstIndex(of: remote) == nil {\n            subscribers.append(remote)\n            mainView.updateSubscriberViews(subscribers, publisherView: multipartyCommunicator?.publisherView)\n        }\n        \n    case .subscriberDestroyed:  // one participant leaves\n        if let remote = remote, let index = subscribers.firstIndex(of: remote) {\n            subscribers.remove(at: index)\n            mainView.updateSubscriberViews(subscribers, publisherView: multipartyCommunicator?.publisherView)\n        }\n        ...\n    }\n}\n```\n   \n### TextChat\n\nThe **TextCHat** feature is built using [accelerator-textchat-ios](https://github.com/opentok/accelerator-textchat-ios). When the text message button is pressed the view changes to present the chat UI:\n\n```objc\n- (IBAction)textMessageButtonPressed:(id)sender {\n    [self presentViewController:[[TextChatTableViewController alloc] init] animated:YES completion:nil]; //When the text message button is pressed the view changes to present the chat UI\n}\n```\n\nThe textchat logic and UI is pre-configured, you can also change properties like `textChatNavigationBar.topItem.title` and `alias` in `TextChatTableViewController`:\n\n```objc\n- (void)viewDidLoad {\n    [super viewDidLoad];\n    \n    self.textChat = [[OTTextChat alloc] init];\n    self.textChat.dataSource = self;\n    self.textChat.alias = @\"Tokboxer\";\n    self.textMessages = [[NSMutableArray alloc] init];\n    \n    self.textChatNavigationBar.topItem.title = self.textChat.alias;\n    self.tableView.textChatTableViewDelegate = self;\n    self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;\n    self.textChatInputView.textField.delegate = self;\n    \n    __weak TextChatTableViewController *weakSelf = self;\n    [self.textChat connectWithHandler:^(OTTextChatConnectionEventSignal signal, OTConnection *connection, NSError *error) {\n        if (signal == OTTextChatConnectionEventSignalDidConnect) {\n            NSLog(@\"Text Chat starts\");\n        }\n        else if (signal == OTTextChatConnectionEventSignalDidDisconnect) {\n            NSLog(@\"Text Chat stops\");\n        }\n    } messageHandler:^(OTTextChatMessageEventSignal signal, OTTextMessage *message, NSError *error) {\n        \n        if (signal == OTTextChatMessageEventSignalDidSendMessage || signal == OTTextChatMessageEventSignalDidReceiveMessage) {\n            \n            if (!error) {\n                [weakSelf.textMessages addObject:message];\n                [weakSelf.tableView reloadData];\n                weakSelf.textChatInputView.textField.text = nil;\n                [weakSelf scrollTextChatTableViewToBottom];\n            }\n        }\n    }];\n    \n    [self.textChatInputView.sendButton addTarget:self action:@selector(sendTextMessage) forControlEvents:UIControlEventTouchUpInside];\n}\n``` \n\n```swift\noverride func viewDidLoad() {\n    super.viewDidLoad()\n    \n    textChat = OTTextChat()\n    textChat?.dataSource = self\n    textChat?.alias = \"Toxboxer\"\n    \n    textChatNavigationBar.topItem?.title = textChat?.alias\n    tableView.textChatTableViewDelegate = self\n    tableView.separatorStyle = .none\n    textChatInputView.textField.delegate = self\n    \n    textChat?.connect(handler: { (signal, connection, error) in\n        \n        guard error == nil else {\n            SVProgressHUD.showError(withStatus: error!.localizedDescription)\n            return\n        }\n        \n        if signal == .didConnect {\n            print(\"Text Chat starts\")\n        }\n        else if signal == .didDisconnect {\n            print(\"Text Chat stops\")\n        }\n        \n    }) { [unowned self](signal, message, error) in\n        \n        guard error == nil, let message = message else {\n            SVProgressHUD.showError(withStatus: error!.localizedDescription)\n            return\n        }\n        \n        self.textMessages.append(message)\n        self.tableView.reloadData()\n        self.textChatInputView.textField.text = nil\n        self.scrollTextChatTableViewToBottom()\n    }\n    \n    textChatInputView.sendButton.addTarget(self, action: #selector(sendTextMessage), for: .touchUpInside)\n}\n```\n\n### ScreenShare\n\nThe **screen share** features shares images from your camera roll using the `ScreenShareViewController` class which publishes the content.\n\n```objc\n- (void)startScreenSharing {\n    self.multipartyScreenSharer = [[OTMultiPartyCommunicator alloc] initWithView:self.annotationView];\n    self.multipartyScreenSharer.dataSource = self;\n    \n    // publishOnly here is to avoid subscripting to those who already subscribed\n    self.multipartyScreenSharer.publishOnly = YES;\n    \n    __weak ScreenShareViewController *weakSelf = self;\n    [self.multipartyScreenSharer connectWithHandler:^(OTCommunicationSignal signal, OTMultiPartyRemote *subscriber, NSError *error) {\n        \n        if (error) {\n            [weakSelf dismissViewControllerAnimated:YES completion:^(){\n                [SVProgressHUD showErrorWithStatus:error.localizedDescription];\n            }];\n            return;\n        }\n        \n        if (signal == OTPublisherCreated) {\n            \n            weakSelf.multipartyScreenSharer.publishAudio = NO;\n            [weakSelf startAnnotation];\n        }\n    }];\n}\n```\n\n```swift\nfileprivate func startScreenSharing() {\n    multipartyScreenSharer = OTMultiPartyCommunicator.init(view: annotationView)\n    multipartyScreenSharer?.dataSource = self\n    \n    // publishOnly here is to avoid subscripting to those who already subscribed\n    multipartyScreenSharer?.isPublishOnly = true\n    \n    multipartyScreenSharer?.connect {\n        [unowned self](signal, remote, error) in\n        \n        guard error == nil else {\n            self.dismiss(animated: true) {\n                SVProgressHUD.showError(withStatus: error!.localizedDescription)\n            }\n            return\n        }\n        \n        if signal == .publisherCreated {\n            self.multipartyScreenSharer?.isPublishAudio = false\n            self.startAnnotation()\n        }\n    }\n}\n```\n   \n### Annotation\n\nThe `ScreenShareViewController` class also handles local annotation:\n\nThe beta version is unstable when it comes to work with cross-platform, it's much stable if two canvas has same aspect ratio.\n```objc\n- (void)startAnnotation {\n    self.annotator = [[OTAnnotator alloc] init];\n    self.annotator.dataSource = self;\n    __weak ScreenShareViewController *weakSelf = self;\n    [self.annotator connectWithCompletionHandler:^(OTAnnotationSignal signal, NSError *error) {\n        if (error) {\n            [weakSelf dismissViewControllerAnimated:YES completion:^(){\n                [SVProgressHUD showErrorWithStatus:error.localizedDescription];\n            }];\n            return;\n        }\n        \n        if (signal == OTAnnotationSessionDidConnect) {\n            \n            // using frame and self.view to contain toolbarView is for having more space to interact with color picker\n            [weakSelf.annotator.annotationScrollView initializeToolbarView];\n            weakSelf.annotator.annotationScrollView.toolbarView.toolbarViewDataSource = self;\n            weakSelf.annotator.annotationScrollView.toolbarView.frame = weakSelf.annotationToolbarView.frame;\n            [weakSelf.view addSubview:weakSelf.annotator.annotationScrollView.toolbarView];\n            \n            weakSelf.annotator.annotationScrollView.frame = weakSelf.annotationView.bounds;\n            weakSelf.annotator.annotationScrollView.scrollView.contentSize = CGSizeMake(CGRectGetWidth(weakSelf.annotator.annotationScrollView.bounds), CGRectGetHeight(weakSelf.annotator.annotationScrollView.bounds));\n            [weakSelf.annotationView addSubview:weakSelf.annotator.annotationScrollView];\n            \n            weakSelf.annotator.annotationScrollView.annotatable = NO;\n        }\n    }];\n}\n```\n\n```swift\nfileprivate func startAnnotation() {\n    annotator = OTAnnotator()\n    annotator?.dataSource = self\n    annotator?.connect {\n        [unowned self] (signal, error) in\n        \n        guard error == nil else {\n            self.dismiss(animated: true) {\n                SVProgressHUD.showError(withStatus: error!.localizedDescription)\n            }\n            return\n        }\n        \n        if signal == .sessionDidConnect {\n            \n            guard let annotator = self.annotator,\n                    let toolbarView = annotator.annotationScrollView.toolbarView else {\n                print(\"Error on launching annotation\")\n                return\n            }\n            \n            // using frame and self.view to contain toolbarView is for having more space to interact with color picker\n            self.annotator?.annotationScrollView.initializeToolbarView()\n            toolbarView.toolbarViewDataSource = self\n            toolbarView.frame = self.annotationToolbarView.frame\n            self.view.addSubview(toolbarView)\n\n            annotator.annotationScrollView.frame = self.annotationView.bounds;\n            annotator.annotationScrollView.scrollView.contentSize = CGSize(width: CGFloat(annotator.annotationScrollView.bounds.width), height: CGFloat(annotator.annotationScrollView.bounds.height))\n            self.annotationView.addSubview(annotator.annotationScrollView)\n            \n            annotator.annotationScrollView.isAnnotatable = false\n        }\n    }\n}\n```\n\n## Development and Contributing\n\nInterested in contributing? We :heart: pull requests! See the [Contribution](CONTRIBUTING.md) guidelines.\n\n## Getting Help\n\nWe love to hear from you so if you have questions, comments or find a bug in the project, let us know! You can either:\n\n- Open an issue on this repository\n- See \u003chttps://support.tokbox.com/\u003e for support options\n- Tweet at us! We're [@VonageDev](https://twitter.com/VonageDev) on Twitter\n- Or [join the Vonage Developer Community Slack](https://developer.nexmo.com/community/slack)\n\n## Further Reading\n\n- Check out the Developer Documentation at \u003chttps://tokbox.com/developer/\u003e\n  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopentok%2Faccelerator-sample-apps-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopentok%2Faccelerator-sample-apps-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopentok%2Faccelerator-sample-apps-ios/lists"}