├── SkylinkSample
├── SkylinkSample
│ ├── Assets.xcassets
│ │ ├── Contents.json
│ │ ├── banner.imageset
│ │ │ ├── banner4.png
│ │ │ └── Contents.json
│ │ ├── btn_chat.imageset
│ │ │ ├── btn_chat.png
│ │ │ └── Contents.json
│ │ ├── call_off.imageset
│ │ │ ├── call_off.png
│ │ │ └── Contents.json
│ │ ├── call_on.imageset
│ │ │ ├── call_on.png
│ │ │ └── Contents.json
│ │ ├── btn_audio.imageset
│ │ │ ├── btn_audio.png
│ │ │ └── Contents.json
│ │ ├── btn_video.imageset
│ │ │ ├── btn_video.png
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ ├── SkylinkAppLogo4-29.png
│ │ │ ├── SkylinkAppLogo4-40.png
│ │ │ ├── SkylinkAppLogo4-76.png
│ │ │ ├── SkylinkAppLogo4-29@2x.png
│ │ │ ├── SkylinkAppLogo4-29@3x.png
│ │ │ ├── SkylinkAppLogo4-40@2x.png
│ │ │ ├── SkylinkAppLogo4-40@3x.png
│ │ │ ├── SkylinkAppLogo4-60@2x.png
│ │ │ ├── SkylinkAppLogo4-60@3x.png
│ │ │ ├── SkylinkAppLogo4-76@2x.png
│ │ │ ├── SkylinkAppLogo4-29@2x-1.png
│ │ │ ├── SkylinkAppLogo4-40@2x-1.png
│ │ │ ├── SkylinkAppLogo4-83_5@2x.png
│ │ │ └── Contents.json
│ │ ├── btn_multiVideo.imageset
│ │ │ ├── btn_multiVideo.png
│ │ │ └── Contents.json
│ │ ├── btn_dataTransfer.imageset
│ │ │ ├── btn_dataTransfer.png
│ │ │ └── Contents.json
│ │ ├── btn_fileTransfer.imageset
│ │ │ ├── btn_fileTransfer.png
│ │ │ └── Contents.json
│ │ ├── sampleImage_transfer.imageset
│ │ │ ├── sampleImage_transfer.png
│ │ │ └── Contents.json
│ │ └── sampleImage_groupTransfer.imageset
│ │ │ ├── sampleImage_groupTransfer.png
│ │ │ └── Contents.json
│ ├── images
│ │ ├── icons
│ │ │ ├── Cancel.png
│ │ │ ├── Unlock.png
│ │ │ ├── Cancel@2x.png
│ │ │ ├── Refresh.png
│ │ │ ├── Unlock@2x.png
│ │ │ ├── VideoCall.png
│ │ │ ├── LockFilled.png
│ │ │ ├── Microphone.png
│ │ │ ├── Refresh@2x.png
│ │ │ ├── HideKeyboard.png
│ │ │ ├── LockFilled@2x.png
│ │ │ ├── Microphone@2x.png
│ │ │ ├── NoVideoFilled.png
│ │ │ ├── SwitchCamera.png
│ │ │ ├── VideoCall@2x.png
│ │ │ ├── HideKeyboard@2x.png
│ │ │ ├── NoVideoFilled@2x.png
│ │ │ ├── SwitchCamera@2x.png
│ │ │ ├── NoMicrophoneFilled.png
│ │ │ └── NoMicrophoneFilled@2x.png
│ │ ├── logoSkylink.png
│ │ ├── logoSkylink@2x.png
│ │ └── logoSkylink@3x.png
│ ├── DataTransferSamples
│ │ └── dataTransferImage.png
│ ├── SkylinkSample-Bridging-Header.h
│ ├── TransferFileSamples
│ │ ├── sampleImage_transfer.png
│ │ └── sampleImage_groupTransfer.png
│ ├── AudioCallViewController.h
│ ├── MessagesViewController.h
│ ├── VideoCallViewController.h
│ ├── utils
│ │ ├── UIAspectFitButton.h
│ │ ├── Utils.h
│ │ ├── UIAspectFitButton.m
│ │ └── Utils.m
│ ├── DataTransferViewController.h
│ ├── MultiVideoCallViewController.h
│ ├── HomeViewController.h
│ ├── FileTransferViewController.h
│ ├── PrefixHeader.pch
│ ├── AppDelegate.h
│ ├── Categories
│ │ ├── NSString+Ext.h
│ │ ├── NSString+Ext.m
│ │ ├── Categories.h
│ │ ├── NSDate+Ext.h
│ │ ├── UIView+Ext.h
│ │ ├── UIAlertController+Ext.h
│ │ ├── NSDate+Ext.m
│ │ ├── UIView+Ext.m
│ │ └── UIAlertController+Ext.m
│ ├── SettingsViewController.h
│ ├── main.m
│ ├── SettingCell.h
│ ├── BaseVC.h
│ ├── EncryptSecretCell.h
│ ├── Peer.h
│ ├── Peer.m
│ ├── SAMessage.h
│ ├── StatsView.h
│ ├── SAMessage.m
│ ├── EncryptSecretCell.m
│ ├── SettingCell.m
│ ├── Info.plist
│ ├── AppDelegate.m
│ ├── Constant.h
│ ├── Constant.m
│ ├── StatsView.m
│ ├── BaseVC.m
│ ├── SettingCell.xib
│ ├── EncryptSecretCell.xib
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ ├── SettingsViewController.m
│ ├── HomeViewController.m
│ ├── AudioCallViewController.m
│ ├── DataTransferViewController.m
│ ├── VideoCallViewController.m
│ ├── MultiVideoCallViewController.m
│ ├── MessagesViewController.m
│ ├── FileTransferViewController.m
│ └── StatsView.xib
├── Podfile
└── SkylinkSample.xcodeproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ └── xcschemes
│ └── SkylinkSample.xcscheme
├── LICENSE.md
├── .gitignore
└── README.md
/SkylinkSample/SkylinkSample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Cancel.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Unlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Unlock.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/logoSkylink.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/logoSkylink.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Cancel@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Cancel@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Refresh.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Unlock@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Unlock@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/VideoCall.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/VideoCall.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/logoSkylink@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/logoSkylink@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/logoSkylink@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/logoSkylink@3x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/LockFilled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/LockFilled.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Microphone.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Microphone.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Refresh@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Refresh@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/HideKeyboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/HideKeyboard.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/LockFilled@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/LockFilled@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/Microphone@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/Microphone@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/NoVideoFilled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/NoVideoFilled.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/SwitchCamera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/SwitchCamera.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/VideoCall@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/VideoCall@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/HideKeyboard@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/HideKeyboard@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/NoVideoFilled@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/NoVideoFilled@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/SwitchCamera@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/SwitchCamera@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/NoMicrophoneFilled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/NoMicrophoneFilled.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/images/icons/NoMicrophoneFilled@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/images/icons/NoMicrophoneFilled@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/DataTransferSamples/dataTransferImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/DataTransferSamples/dataTransferImage.png
--------------------------------------------------------------------------------
/SkylinkSample/Podfile:
--------------------------------------------------------------------------------
1 | project 'SkylinkSample.xcodeproj'
2 |
3 | platform :ios, '9.0'
4 |
5 | target 'SkylinkSample' do
6 | use_frameworks!
7 |
8 |
9 | pod 'SKYLINK'
10 |
11 | end
12 |
13 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/banner.imageset/banner4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/banner.imageset/banner4.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SkylinkSample-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // Use this file to import your target's public headers that you would like to expose to Swift.
3 | //
4 |
5 | //#import "Constant.h"
6 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_chat.imageset/btn_chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/btn_chat.imageset/btn_chat.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/call_off.imageset/call_off.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/call_off.imageset/call_off.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/call_on.imageset/call_on.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/call_on.imageset/call_on.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/TransferFileSamples/sampleImage_transfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/TransferFileSamples/sampleImage_transfer.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_audio.imageset/btn_audio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/btn_audio.imageset/btn_audio.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_video.imageset/btn_video.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/btn_video.imageset/btn_video.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/TransferFileSamples/sampleImage_groupTransfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/TransferFileSamples/sampleImage_groupTransfer.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-76.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_multiVideo.imageset/btn_multiVideo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/btn_multiVideo.imageset/btn_multiVideo.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29@3x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40@3x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-60@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-60@3x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-76@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-29@2x-1.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-40@2x-1.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-83_5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/SkylinkAppLogo4-83_5@2x.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_dataTransfer.imageset/btn_dataTransfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/btn_dataTransfer.imageset/btn_dataTransfer.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_fileTransfer.imageset/btn_fileTransfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/btn_fileTransfer.imageset/btn_fileTransfer.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/sampleImage_transfer.imageset/sampleImage_transfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/sampleImage_transfer.imageset/sampleImage_transfer.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/sampleImage_groupTransfer.imageset/sampleImage_groupTransfer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Temasys/SkylinkSDK-iOS-Sample/HEAD/SkylinkSample/SkylinkSample/Assets.xcassets/sampleImage_groupTransfer.imageset/sampleImage_groupTransfer.png
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/AudioCallViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // AudioCallViewController.h
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 07/01/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "BaseVC.h"
10 |
11 | @interface AudioCallViewController : BaseVC
12 | @end
13 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/MessagesViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MessagesViewController.h
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 04/01/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "BaseVC.h"
10 |
11 | @interface MessagesViewController : BaseVC
12 | @end
13 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/VideoCallViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // VideCallViewController.h
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 11/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import "BaseVC.h"
10 |
11 | @interface VideoCallViewController : BaseVC
12 | @end
13 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/utils/UIAspectFitButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIAspectFitButton.h
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 16/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface UIAspectFitButton : UIButton
12 |
13 | @end
14 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/DataTransferViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // DataTransferViewController.h
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 08/06/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "BaseVC.h"
10 |
11 | @interface DataTransferViewController : BaseVC
12 | @end
13 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/MultiVideoCallViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // MultiVideoCallViewController.h
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 11/01/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "BaseVC.h"
10 |
11 | @interface MultiVideoCallViewController : BaseVC
12 | @end
13 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/HomeViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.h
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 11/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 |
12 |
13 | @interface HomeViewController : UIViewController
14 |
15 | @end
16 |
17 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/FileTransferViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // FileTransferViewController.h
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 18/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import "BaseVC.h"
10 | #import
11 |
12 | @interface FileTransferViewController : BaseVC
13 | @end
14 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/PrefixHeader.pch:
--------------------------------------------------------------------------------
1 | //
2 | // PrefixHeader.pch
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 26/11/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #ifndef PrefixHeader_pch
10 | #define PrefixHeader_pch
11 |
12 | #import "Constant.h"
13 | #import "Categories.h"
14 |
15 | #endif /* PrefixHeader_pch */
16 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 01/02/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface AppDelegate : UIResponder
12 |
13 | @property (strong, nonatomic) UIWindow *window;
14 |
15 |
16 | @end
17 |
18 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/NSString+Ext.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+Ext.h
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 10/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NSString(Ext)
14 | - (BOOL)isNotEmpty;
15 | @end
16 |
17 | NS_ASSUME_NONNULL_END
18 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SettingsViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewController.h
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 29/08/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface SettingsViewController : UIViewController
12 | @property (strong, nonatomic) IBOutlet UITableView *tableView;
13 | @end
14 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/utils/Utils.h:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.h
3 | // objc_SampleApp
4 | //
5 | // Created by Charlie on 19/11/19.
6 | // Copyright © 2019 Charlie. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface Utils : NSObject
15 | UIViewController * topVC(void);
16 | @end
17 |
18 | NS_ASSUME_NONNULL_END
19 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/NSString+Ext.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSString+Ext.m
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 10/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "NSString+Ext.h"
10 |
11 | @implementation NSString(Ext)
12 | - (BOOL)isNotEmpty{
13 | if (self && self.length>0) {
14 | return YES;
15 | }
16 | return NO;
17 | }
18 | @end
19 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/Categories.h:
--------------------------------------------------------------------------------
1 | //
2 | // Categories.h
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 25/11/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #ifndef Categories_h
10 | #define Categories_h
11 |
12 | #import "UIView+Ext.h"
13 | #import "UIAlertController+Ext.h"
14 | #import "NSDate+Ext.h"
15 | #import "NSString+Ext.h"
16 |
17 | #endif /* Categories_h */
18 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 01/02/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import
10 | #import "AppDelegate.h"
11 |
12 | int main(int argc, char * argv[]) {
13 | @autoreleasepool {
14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/banner.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "banner4.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_audio.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "btn_audio.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_chat.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "btn_chat.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_video.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "btn_video.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/call_off.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "call_off.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/call_on.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "call_on.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_multiVideo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "btn_multiVideo.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_dataTransfer.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "btn_dataTransfer.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/btn_fileTransfer.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "btn_fileTransfer.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/sampleImage_transfer.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "sampleImage_transfer.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/sampleImage_groupTransfer.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "sampleImage_groupTransfer.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SettingCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // SettingCell.h
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 5/9/19.
6 | // Copyright © 2019 Temasys. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //NS_ASSUME_NONNULL_BEGIN
12 |
13 | //extern NSString * const CELL_IDENTIFIER;
14 | static NSString *CELL_IDENTIFIER = @"SettingCell";
15 |
16 | @interface SettingCell : UITableViewCell
17 | - (void)setupCellWithKey:(NSString *)key value:(NSString *)value;
18 | @end
19 |
20 | //NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/NSDate+Ext.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSDate+Ext.h
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 9/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface NSDate(Ext)
14 | - (NSString*)skylinkDateString;
15 | + (NSDate*)skylinkDateFromString:(NSString*)string;
16 |
17 | - (long long)toTimeStamp;
18 | + (NSDate *)dateFromTimeStamp:(long long)timeStamp;
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/utils/UIAspectFitButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIAspectFitButton.m
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 16/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import "UIAspectFitButton.h"
10 |
11 | @implementation UIAspectFitButton
12 |
13 | -(void)layoutSubviews {
14 | [super layoutSubviews];
15 | for(UIView *buttonSubview in self.subviews)
16 | if ([buttonSubview isKindOfClass:[UIImageView class]]) [buttonSubview setContentMode:UIViewContentModeScaleAspectFit];
17 | }
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/UIView+Ext.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Ext.h
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 25/11/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface UIView(Ext)
15 | - (void)removeSubviews;
16 | - (void)aspectFitRectForSize:(CGSize)insideSize container:(UIView*)container;
17 | - (void)aspectFillRectForSize:(CGSize)insideSize container:(UIView*)container;
18 | @end
19 |
20 | NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2017 Temasys Communications Pte Ltd
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/BaseVC.h:
--------------------------------------------------------------------------------
1 | //
2 | // BaseVC.h
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 26/11/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface BaseVC : UIViewController{
15 | SKYLINKConnection *_skylinkConnection;
16 | NSString *roomName;
17 | }
18 | - (void)startLocalMediaDevice:(SKYLINKMediaDevice)mediaDevice;
19 | - (void)joinRoom;
20 | @end
21 |
22 | NS_ASSUME_NONNULL_END
23 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/utils/Utils.m:
--------------------------------------------------------------------------------
1 | //
2 | // Utils.m
3 | // objc_SampleApp
4 | //
5 | // Created by Charlie on 19/11/19.
6 | // Copyright © 2019 Charlie. All rights reserved.
7 | //
8 |
9 | #import "Utils.h"
10 | #import
11 |
12 | @implementation Utils
13 | UIViewController * topVC(){
14 | UIWindow *keyWindow = nil;
15 | NSArray *windows = [[UIApplication sharedApplication]windows];
16 | for (UIWindow *window in windows) {
17 | if (window.isKeyWindow) {
18 | keyWindow = window;
19 | break;
20 | }
21 | }
22 | return keyWindow.rootViewController;
23 | }
24 | @end
25 |
26 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/EncryptSecretCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // EncryptSecretCell.h
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 9/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | static NSString *CELL_IDENTIFIER_ENCRYPT_SECRET = @"EncryptSecretCell";
14 |
15 | @interface EncryptSecretCell : UITableViewCell
16 | @property (weak, nonatomic) IBOutlet UITextField *secretIdField;
17 | @property (weak, nonatomic) IBOutlet UITextField *secretField;
18 | - (void)setupCell:(NSString *)secretId secret:(NSString *)secret;
19 | @end
20 |
21 | NS_ASSUME_NONNULL_END
22 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/UIAlertController+Ext.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIAlertController+Ext.h
3 | // objc_SampleApp
4 | //
5 | // Created by Charlie on 18/11/19.
6 | // Copyright © 2019 Charlie. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //NS_ASSUME_NONNULL_BEGIN
12 |
13 | @interface UIAlertController(Ext)
14 | void showAlert(NSString *title, NSString* message);
15 | void showAlertTouchDismiss(NSString *title, NSString* message);
16 | void showAlertAutoDismiss(NSString *title, NSString *message, float duration, UIViewController *vc);
17 | + (void)showAlertWithAutoDisappearTitle:(NSString *)title message:(NSString *)message duration:(CGFloat)duration onViewController:(UIViewController *)viewController;
18 | @end
19 |
20 | //NS_ASSUME_NONNULL_END
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 |
20 | # CocoaPods
21 | #
22 | # We recommend against adding the Pods directory to your .gitignore. However
23 | # you should judge for yourself, the pros and cons are mentioned at:
24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
25 | #
26 | #Pods/
27 | SkylinkSample.xcscmblueprint
28 |
29 | # Fabric
30 | #
31 | # User will add their own fabric.sh if desired.
32 | # Refer to README.md for details on adding this file.
33 | SampleAppObjectiveC/fabric.sh
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Peer.h:
--------------------------------------------------------------------------------
1 | //
2 | // Peer.h
3 | // SkylinkSample
4 | //
5 | // Created by Yuxi Liu on 13/9/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | @interface Peer : NSObject
15 | @property(nonatomic, copy) NSString *peerId;
16 | @property(nonatomic, copy) NSString *userName;
17 | @property(nonatomic, weak) UIView *videoView;
18 | @property(nonatomic, assign) CGSize videoSize;
19 | @property(nonatomic, strong) NSMutableArray *medias;
20 | - (instancetype)initWithPeerID:(NSString *)peerId;
21 | - (instancetype)initWithPeerID:(NSString *)peerId userName:(NSString *)userName videoView:(UIView *)videoView videoSize:(CGSize)videoSize;
22 | @end
23 |
24 | NS_ASSUME_NONNULL_END
25 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Peer.m:
--------------------------------------------------------------------------------
1 | //
2 | // Peer.m
3 | // SkylinkSample
4 | //
5 | // Created by Yuxi Liu on 13/9/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "Peer.h"
10 |
11 | @implementation Peer
12 | - (instancetype)initWithPeerID:(NSString *)peerId{
13 | if (self = [super init]) {
14 | self.peerId = peerId;
15 | self.medias = [NSMutableArray new];
16 | }
17 | return self;
18 | }
19 | - (instancetype)initWithPeerID:(NSString *)peerId userName:(NSString *)userName videoView:(UIView *)videoView videoSize:(CGSize)videoSize
20 | {
21 | if (self = [super init]) {
22 | self.peerId = peerId;
23 | self.userName = userName;
24 | self.videoView = videoView;
25 | self.videoSize = videoSize;
26 | self.medias = [NSMutableArray new];
27 | }
28 | return self;
29 | }
30 | @end
31 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SAMessage.h:
--------------------------------------------------------------------------------
1 | //
2 | // SAMessage.h
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 9/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | typedef enum SAMessageType{
14 | SAMessageTypeSignaling,
15 | SAMessageTypeP2P
16 | }SAMessageType;
17 |
18 | @interface SAMessage : NSObject
19 | @property(strong, nonatomic)NSString *data;
20 | @property(assign, nonatomic)long long timeStamp;
21 | @property(strong, nonatomic)NSString *sender;
22 | @property(strong, nonatomic)NSString *target;
23 | @property(assign, nonatomic)SAMessageType type;
24 |
25 | - (instancetype)initWithData:(NSString *)data timeStamp:(long long)timeStamp sender:(nullable NSString *)sender target:(nullable NSString *)target type:(SAMessageType)type;
26 | - (NSString *)typeToString;
27 | - (BOOL)isPublic;
28 | - (NSString *)isPublicString;
29 | - (NSString *)timeStampString;
30 | @end
31 |
32 | NS_ASSUME_NONNULL_END
33 |
34 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/StatsView.h:
--------------------------------------------------------------------------------
1 | //
2 | // StatsView.h
3 | // SkylinkSample
4 | //
5 | // Created by Yuxi Liu on 5/9/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | NS_ASSUME_NONNULL_BEGIN
12 |
13 | typedef enum Status {
14 | StatusInput = 0,
15 | StatusSent,
16 | StatusReceived,
17 | StatusAll
18 | } Status;
19 |
20 | @interface Stats : NSObject
21 | @property(nonatomic, copy) NSString *inputWidth;
22 | @property(nonatomic, copy) NSString *inputHeight;
23 | @property(nonatomic, copy) NSString *inputFPS;
24 | @property(nonatomic, copy) NSString *sentWidth;
25 | @property(nonatomic, copy) NSString *sentHeight;
26 | @property(nonatomic, copy) NSString *sentFPS;
27 | @property(nonatomic, copy) NSString *receivedWidth;
28 | @property(nonatomic, copy) NSString *receivedHeight;
29 | @property(nonatomic, copy) NSString *receivedFPS;
30 |
31 | - (instancetype)initWithDict:(NSDictionary *)dict;
32 | @end
33 |
34 | @class Stats;
35 | @interface StatsView : UIView
36 | - (void)setupViewWithStats:(Stats *)stats status:(Status)status;
37 | @end
38 |
39 | NS_ASSUME_NONNULL_END
40 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SAMessage.m:
--------------------------------------------------------------------------------
1 | //
2 | // SAMessage.m
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 9/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "SAMessage.h"
10 |
11 | @implementation SAMessage
12 | - (instancetype)initWithData:(NSString *)data timeStamp:(long long)timeStamp sender:(NSString *)sender target:(NSString *)target type:(SAMessageType)type{
13 | if(self = [super init]){
14 | self.data = data;
15 | self.timeStamp = timeStamp;
16 | self.sender = sender;
17 | self.target = target;
18 | self.type = type;
19 | }
20 | return self;
21 | }
22 | - (NSString *)typeToString{
23 | if (self.type == SAMessageTypeSignaling) {
24 | return @"Signaling";
25 | }else{
26 | return @"P2P";
27 |
28 | }
29 | }
30 | - (BOOL)isPublic{
31 | return (self.target == nil);
32 | }
33 | - (NSString *)isPublicString{
34 | return self.isPublic ? @"Public" : @"Private";
35 | }
36 | - (NSString *)timeStampString{
37 | NSDate *dateTS = [NSDate dateFromTimeStamp:self.timeStamp];
38 | return [dateTS skylinkDateString];
39 | }
40 | @end
41 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/EncryptSecretCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // EncryptSecretCell.m
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 9/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "EncryptSecretCell.h"
10 | #import "Constant.h"
11 | @implementation EncryptSecretCell
12 |
13 | - (void)awakeFromNib {
14 | [super awakeFromNib];
15 | // Initialization code
16 | }
17 |
18 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
19 | [super setSelected:selected animated:animated];
20 |
21 | // Configure the view for the selected state
22 | }
23 | - (void)setupCell:(NSString *)secretId secret:(NSString *)secret {
24 | _secretIdField.text = secretId;
25 | _secretField.text = secret;
26 | }
27 | - (void)textFieldDidChangeSelection:(UITextField *)textField{
28 | if (textField == _secretField){
29 | [SAConstants.shared.ENCRYPTION_SECRETS setObject:textField.text forKey:_secretIdField.text];
30 | }
31 | }
32 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
33 | if (textField == _secretField) {
34 | NSString *_updateString = [textField.text stringByReplacingCharactersInRange:range withString:string];
35 | SAConstants.shared.ENCRYPTION_SECRETS[_secretIdField.text] = _updateString;
36 | }
37 | return YES;
38 | }
39 | @end
40 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/NSDate+Ext.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSDate+Ext.m
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 9/3/20.
6 | // Copyright © 2020 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "NSDate+Ext.h"
10 |
11 | @implementation NSDate(Ext)
12 | - (long long)toTimeStamp{
13 | return (long long) (self.timeIntervalSince1970 * 1000);
14 | }
15 | + (NSDate *)dateFromTimeStamp:(long long)timeStamp{
16 | return [NSDate dateWithTimeIntervalSince1970:(double)timeStamp];
17 | }
18 |
19 | - (NSString *)skylinkDateString{
20 | return [NSDate stringFromDate:self format:@"yyyy-MM-dd'T'HH:mm:ss.0'Z'"];
21 | }
22 | + (NSDate*)skylinkDateFromString:(NSString*)string{
23 | return [NSDate dateFromString:string format:@"yyyy-MM-dd'T'HH:mm:ss.0'Z'"];
24 | }
25 |
26 | + (NSString*)stringFromDate:(NSDate*)date format:(NSString*)format{
27 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
28 | [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
29 | dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; // IOS-429
30 | [dateFormatter setDateFormat:format];
31 | return [dateFormatter stringFromDate:date];
32 | }
33 | + (NSDate*)dateFromString:(NSString*)string format:(NSString*)format{
34 | NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
35 | [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];
36 | dateFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; // IOS-429
37 | [dateFormatter setDateFormat:format];
38 | return [dateFormatter dateFromString:string];
39 | }
40 | @end
41 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SettingCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // SettingCell.m
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 5/9/19.
6 | // Copyright © 2019 Temasys. All rights reserved.
7 | //
8 |
9 | #import "SettingCell.h"
10 | #import "Constant.h"
11 |
12 | //static NSString *CELL_IDENTIFIER = @"SettingCell";
13 | //static SAConstants *shared = nil;
14 |
15 | @interface SettingCell()
16 | @property (weak, nonatomic) IBOutlet UILabel *keyLabel;
17 | @property (weak, nonatomic) IBOutlet UITextField *valueField;
18 | @end
19 |
20 | @implementation SettingCell
21 |
22 | - (void)awakeFromNib {
23 | [super awakeFromNib];
24 | // Initialization code
25 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textChanged) name:UITextFieldTextDidChangeNotification object:nil];
26 | }
27 |
28 | - (void)setupCellWithKey:(NSString *)key value:(NSString *)value
29 | {
30 | self.keyLabel.text = key;
31 | self.valueField.text = value;
32 | }
33 |
34 | - (void)textChanged
35 | {
36 | if (!self.valueField.text || [self.valueField.text containsString:@" "]) {
37 | MyLog(@"Room name not valid");
38 | } else {
39 | if ([self.keyLabel.text isEqualToString:@"App Key"]) APP_KEY = self.valueField.text;
40 | else if ([self.keyLabel.text isEqualToString:@"App Secret"]) APP_SECRET = self.valueField.text;
41 | else if ([self.keyLabel.text isEqualToString:@"1-1 video call"]) ROOM_ONE_TO_ONE_VIDEO = self.valueField.text;
42 | else if ([self.keyLabel.text isEqualToString:@"Multi video call"]) ROOM_MULTI_VIDEO = self.valueField.text;
43 | else if ([self.keyLabel.text isEqualToString:@"Audio call"]) ROOM_AUDIO = self.valueField.text;
44 | else if ([self.keyLabel.text isEqualToString:@"Messages"]) ROOM_MESSAGES = self.valueField.text;
45 | else if ([self.keyLabel.text isEqualToString:@"File transfer"]) ROOM_FILE_TRANSFER = self.valueField.text;
46 | else if ([self.keyLabel.text isEqualToString:@"Data transfer"]) ROOM_DATA_TRANSFER = self.valueField.text;
47 | }
48 | }
49 | @end
50 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | Sample App
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(MARKETING_VERSION)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(CURRENT_PROJECT_VERSION)
25 | LSRequiresIPhoneOS
26 |
27 | NSAppTransportSecurity
28 |
29 | NSAllowsArbitraryLoads
30 |
31 |
32 | NSCameraUsageDescription
33 | Perform webRTC video calls
34 | NSMicrophoneUsageDescription
35 | Perform webRTC video & audio calls
36 | NSPhotoLibraryAddUsageDescription
37 | Save and transfer photos and videos
38 | NSPhotoLibraryUsageDescription
39 | Save images & videos received via webRTC file transfer
40 | UIBackgroundModes
41 |
42 | voip
43 |
44 | UILaunchStoryboardName
45 | LaunchScreen
46 | UIMainStoryboardFile
47 | Main
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UISupportedInterfaceOrientations
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationLandscapeLeft
56 | UIInterfaceOrientationLandscapeRight
57 |
58 | UISupportedInterfaceOrientations~ipad
59 |
60 | UIInterfaceOrientationPortrait
61 | UIInterfaceOrientationPortraitUpsideDown
62 | UIInterfaceOrientationLandscapeLeft
63 | UIInterfaceOrientationLandscapeRight
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/UIView+Ext.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Ext.m
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 25/11/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "UIView+Ext.h"
10 |
11 | @implementation UIView(Ext)
12 | - (void)removeSubviews{
13 | while (self.subviews.count>0) {
14 | [self.subviews.firstObject removeFromSuperview];
15 | }
16 | }
17 | - (void)aspectFitRectForSize:(CGSize)insideSize container:(UIView*)container{
18 | if (!container ||
19 | insideSize.width<=0 ||
20 | insideSize.height<=0) {
21 | return;
22 | }
23 |
24 | CGFloat originRate = insideSize.width/insideSize.height;
25 | CGFloat containerRate = container.frame.size.width/container.frame.size.height;
26 | CGRect resultFrame = CGRectMake(0, 0, 0, 0);
27 | if (originRate > containerRate){
28 | resultFrame.size.width = container.frame.size.width;
29 | resultFrame.size.height = container.frame.size.width/originRate;
30 | }else{
31 | resultFrame.size.height = container.frame.size.height;
32 | resultFrame.size.width = container.frame.size.height*originRate;
33 | }
34 | resultFrame.origin.x = container.frame.size.width/2 - resultFrame.size.width/2;
35 | resultFrame.origin.y = container.frame.size.height/2 - resultFrame.size.height/2;
36 | self.frame = resultFrame;
37 | }
38 | - (void)aspectFillRectForSize:(CGSize)insideSize container:(UIView*)container{
39 | if (!container ||
40 | insideSize.width<=0 ||
41 | insideSize.height<=0) {
42 | return;
43 | }
44 | self.frame = container.frame;
45 | /*var maxFloat: CGFloat = 0
46 | if container.frame.size.height > container.frame.size.width {
47 | maxFloat = container.frame.size.height
48 | } else if container.frame.size.height < container.frame.size.width {
49 | maxFloat = container.frame.size.width
50 | } else {
51 | maxFloat = 0
52 | }
53 | var aspectRatio: CGFloat = 0
54 | if insideSize.height != 0 {
55 | aspectRatio = insideSize.width / insideSize.height
56 | } else {
57 | aspectRatio = 1
58 | }
59 | var frame = CGRect(x: 0, y: 0, width: container.frame.size.width, height: container.frame.size.height)
60 | if insideSize.width < insideSize.height {
61 | frame.size.width = maxFloat
62 | frame.size.height = frame.size.width / aspectRatio
63 | } else {
64 | frame.size.height = maxFloat;
65 | frame.size.width = frame.size.height * aspectRatio
66 | }
67 | frame.origin.x = (container.frame.size.width - frame.size.width) / 2
68 | frame.origin.y = (container.frame.size.height - frame.size.height) / 2
69 | self.frame = frame*/
70 | }
71 | @end
72 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "size" : "29x29",
15 | "idiom" : "iphone",
16 | "filename" : "SkylinkAppLogo4-29@2x.png",
17 | "scale" : "2x"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "iphone",
22 | "filename" : "SkylinkAppLogo4-29@3x.png",
23 | "scale" : "3x"
24 | },
25 | {
26 | "size" : "40x40",
27 | "idiom" : "iphone",
28 | "filename" : "SkylinkAppLogo4-40@2x.png",
29 | "scale" : "2x"
30 | },
31 | {
32 | "size" : "40x40",
33 | "idiom" : "iphone",
34 | "filename" : "SkylinkAppLogo4-40@3x.png",
35 | "scale" : "3x"
36 | },
37 | {
38 | "size" : "60x60",
39 | "idiom" : "iphone",
40 | "filename" : "SkylinkAppLogo4-60@2x.png",
41 | "scale" : "2x"
42 | },
43 | {
44 | "size" : "60x60",
45 | "idiom" : "iphone",
46 | "filename" : "SkylinkAppLogo4-60@3x.png",
47 | "scale" : "3x"
48 | },
49 | {
50 | "idiom" : "ipad",
51 | "size" : "20x20",
52 | "scale" : "1x"
53 | },
54 | {
55 | "idiom" : "ipad",
56 | "size" : "20x20",
57 | "scale" : "2x"
58 | },
59 | {
60 | "size" : "29x29",
61 | "idiom" : "ipad",
62 | "filename" : "SkylinkAppLogo4-29.png",
63 | "scale" : "1x"
64 | },
65 | {
66 | "size" : "29x29",
67 | "idiom" : "ipad",
68 | "filename" : "SkylinkAppLogo4-29@2x-1.png",
69 | "scale" : "2x"
70 | },
71 | {
72 | "size" : "40x40",
73 | "idiom" : "ipad",
74 | "filename" : "SkylinkAppLogo4-40.png",
75 | "scale" : "1x"
76 | },
77 | {
78 | "size" : "40x40",
79 | "idiom" : "ipad",
80 | "filename" : "SkylinkAppLogo4-40@2x-1.png",
81 | "scale" : "2x"
82 | },
83 | {
84 | "size" : "76x76",
85 | "idiom" : "ipad",
86 | "filename" : "SkylinkAppLogo4-76.png",
87 | "scale" : "1x"
88 | },
89 | {
90 | "size" : "76x76",
91 | "idiom" : "ipad",
92 | "filename" : "SkylinkAppLogo4-76@2x.png",
93 | "scale" : "2x"
94 | },
95 | {
96 | "size" : "83.5x83.5",
97 | "idiom" : "ipad",
98 | "filename" : "SkylinkAppLogo4-83_5@2x.png",
99 | "scale" : "2x"
100 | },
101 | {
102 | "idiom" : "ios-marketing",
103 | "size" : "1024x1024",
104 | "scale" : "1x"
105 | }
106 | ],
107 | "info" : {
108 | "version" : 1,
109 | "author" : "xcode"
110 | }
111 | }
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // SkylinkSample/Users/romainpellen/Desktop/Bitbucket/Temasys/SkylinkSDK-iOS-Sample/SkylinkSample/SkylinkSample
4 | //
5 | // Created by Temasys on 01/02/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "AppDelegate.h"
10 | #import
11 | #import "Constant.h"
12 |
13 | @interface AppDelegate ()
14 |
15 | @end
16 |
17 | @implementation AppDelegate
18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
19 | // Override point for customization after application launch.
20 | signal(SIGPIPE, SIG_IGN); // Resolve SIGPIPE, as sugested by Apple doc.
21 | MyLog(@"- SKYLINK SampleApp launched -");
22 | return YES;
23 | }
24 |
25 | - (void)applicationWillResignActive:(UIApplication *)application {
26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
27 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
28 | }
29 |
30 | - (void)applicationDidEnterBackground:(UIApplication *)application {
31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
33 | }
34 |
35 | - (void)applicationWillEnterForeground:(UIApplication *)application {
36 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
37 | }
38 |
39 | - (void)applicationDidBecomeActive:(UIApplication *)application {
40 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
41 | }
42 |
43 | - (void)applicationWillTerminate:(UIApplication *)application {
44 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
45 | }
46 |
47 | - (void)createFolder
48 | {
49 | NSArray *allDirectories = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
50 | NSString *documentDirectory = allDirectories[0];
51 | appFilesFolder = [documentDirectory stringByAppendingPathComponent:@"app_files"];
52 | if (![[NSFileManager defaultManager] fileExistsAtPath:appFilesFolder]) {
53 | NSError *error;
54 | [[NSFileManager defaultManager] createDirectoryAtURL:[NSURL URLWithString:appFilesFolder] withIntermediateDirectories:YES attributes:nil error:&error];
55 | if (error) MyLog(@"%@", error.localizedDescription);
56 | }
57 | }
58 | @end
59 |
60 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Constant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Constant.h
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 5/9/19.
6 | // Copyright © 2019 Temasys. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 |
12 | extern NSString *APP_KEY;
13 | extern NSString *APP_SECRET;
14 | extern NSString *ROOM_NAME;
15 | //extern NSDictionary *APP_KEYS;
16 |
17 | extern NSString *ROOM_ONE_TO_ONE_VIDEO;
18 | extern NSString *ROOM_MULTI_VIDEO;
19 | extern NSString *ROOM_AUDIO;
20 | extern NSString *ROOM_MESSAGES;
21 | extern NSString *ROOM_FILE_TRANSFER;
22 | extern NSString *ROOM_DATA_TRANSFER;
23 |
24 | extern NSString *appFilesFolder;
25 |
26 | //NSDictionary* APP_KEYS(void);
27 | //NSDictionary* ENCRYPTION_SECRETS(void);
28 | #define USER_NAME UIDevice.currentDevice.name
29 |
30 |
31 | #define SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height
32 | #define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width
33 |
34 |
35 | #define iPhone4 [[UIScreen mainScreen] bounds].size.height == 480
36 | #define iPhone5 [[UIScreen mainScreen] bounds].size.height == 568
37 | #define iPhone6 [[UIScreen mainScreen] bounds].size.height == 667
38 | #define iPhone6Plus [[UIScreen mainScreen] bounds].size.height == 736
39 | #define iPhoneX [[UIScreen mainScreen] bounds].size.height == 812
40 | #define iPhoneXR_XSMAX [[UIScreen mainScreen] bounds].size.height == 896
41 |
42 |
43 | #define iOS7 [[UIDevice currentDevice] systemVersion].floatValue < 8.0
44 | #define iOS8 [[UIDevice currentDevice] systemVersion].floatValue >= 8.0 && [[UIDevice currentDevice] systemVersion].floatValue < 9.0
45 | #define iOS9 [[UIDevice currentDevice] systemVersion].floatValue >= 9.0 && [[UIDevice currentDevice] systemVersion].floatValue < 10.0
46 | #define iOS10 [[UIDevice currentDevice] systemVersion].floatValue >= 10.0 && [[UIDevice currentDevice] systemVersion].floatValue < 11.0
47 | #define iOS11 [[UIDevice currentDevice] systemVersion].floatValue >= 11.0 && [[UIDevice currentDevice] systemVersion].floatValue < 12.0
48 | #define iOS12 [[UIDevice currentDevice] systemVersion].floatValue >= 12.0 && [[UIDevice currentDevice] systemVersion].floatValue < 13.0
49 | #define iOS13 [[UIDevice currentDevice] systemVersion].floatValue >= 13.0 && [[UIDevice currentDevice] systemVersion].floatValue < 14.0
50 | #define iOS11Above [[UIDevice currentDevice] systemVersion].floatValue >= 11.0
51 |
52 |
53 | #define UIRGBColor(r, g, b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1.0]
54 | #define UIRGBAColor(r, g, b, alpha) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:alpha]
55 | #define UIRandomColor UIRGBColor(arc4random_uniform(256), arc4random_uniform(256), arc4random_uniform(256))
56 | #define UIGlobalBackgroundColor UIRGBColor(242, 243, 244)
57 | #define UIGlobalTextLightColor UIRGBColor(155, 155, 155)
58 | #define UIGlobalTextDarkColor UIRGBColor(74, 74, 74)
59 |
60 | #ifdef DEBUG
61 | #define MyLog(...) NSLog(__VA_ARGS__)
62 | #else
63 | #define MyLog(...)
64 | #endif
65 |
66 | @interface SAConstants : NSObject
67 | +(instancetype)shared;
68 | @property(strong, nonatomic) NSDictionary *APP_KEYS;
69 | @property(strong, nonatomic) NSMutableDictionary *ENCRYPTION_SECRETS;
70 | + (void)switchOutput;
71 | @end
72 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample.xcodeproj/xcshareddata/xcschemes/SkylinkSample.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Constant.m:
--------------------------------------------------------------------------------
1 | //
2 | // Constant.m
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 5/9/19.
6 | // Copyright © 2019 Temasys. All rights reserved.
7 | //
8 |
9 | #import "Constant.h"
10 | #import
11 |
12 | NSString *APP_KEY = @"Enter AppKey";
13 | NSString *APP_SECRET = @"Enter AppSecret";
14 | NSString *ROOM_NAME = @"rt";
15 |
16 | NSString *ROOM_ONE_TO_ONE_VIDEO = @"rt";
17 | NSString *ROOM_MULTI_VIDEO = @"ROOM_MULTI_VIDEO";
18 | NSString *ROOM_AUDIO = @"ROOM_AUDIO";
19 | NSString *ROOM_MESSAGES = @"MESSAGES-ROOM";
20 | NSString *ROOM_FILE_TRANSFER = @"ROOM_FILE_TRANSFER";
21 | NSString *ROOM_DATA_TRANSFER = @"ROOM_DATA_TRANSFER";
22 |
23 | NSString *appFilesFolder = @"";
24 | @implementation SAConstants
25 |
26 | + (instancetype)shared{
27 | static SAConstants *shared = nil;
28 | static dispatch_once_t onceToken;
29 | dispatch_once(&onceToken, ^{
30 | shared = [[self alloc] init];
31 | //initial keys
32 | shared.APP_KEYS = @{@"APP_KEY1": @"APP_SECRET1",
33 | @"APP_KEY2": @"APP_SECRET2",
34 | @"APP_KEY3": @"APP_SECRET3"};
35 | shared.ENCRYPTION_SECRETS = [NSMutableDictionary dictionaryWithDictionary: @{@"key1": @"secret1",
36 | @"key2": @"secret2",
37 | @"key3": @"secret3"}];
38 | });
39 | return shared;
40 | }
41 |
42 | + (void)switchOutput
43 | {
44 | AVAudioSessionPortDescription *builtInPortDescription = [AVAudioSessionPortDescription new];
45 | AVAudioSessionPortDescription *bluetoothPortDescription = [AVAudioSessionPortDescription new];
46 | BOOL isBluetoothPortDescriptionAssigned = NO;
47 | NSArray *availableInputs = [AVAudioSession sharedInstance].availableInputs;
48 | for (AVAudioSessionPortDescription *description in availableInputs) {
49 | if (description.portType == AVAudioSessionPortBuiltInMic) builtInPortDescription = description;
50 | if (description.portType == AVAudioSessionPortBluetoothLE || description.portType == AVAudioSessionPortBluetoothHFP || description.portType == AVAudioSessionPortBluetoothA2DP) {
51 | builtInPortDescription = description;
52 | isBluetoothPortDescriptionAssigned = YES;
53 | }
54 | }
55 | NSArray *dataSources = builtInPortDescription.dataSources;
56 | for (AVAudioSessionDataSourceDescription *description in dataSources) {
57 | if (description.orientation == AVAudioSessionOrientationFront || description.orientation == AVAudioSessionOrientationBottom || description.orientation == AVAudioSessionOrientationBack) {
58 | NSError *error;
59 | [bluetoothPortDescription setPreferredDataSource:description error:&error];
60 | if (error) MyLog(@"bluetoothPortDescription setPreferredDataSource error ---> %@", error.localizedDescription);
61 | break;
62 | }
63 | }
64 | NSError *error;
65 | isBluetoothPortDescriptionAssigned ? [[AVAudioSession sharedInstance] setPreferredInput:bluetoothPortDescription error:&error] : [[AVAudioSession sharedInstance] setPreferredInput:(builtInPortDescription) error:&error];
66 | MyLog(@"bluetoothPortDescription setPreferredInput error ---> %@", error.localizedDescription);
67 | }
68 | @end
69 |
70 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Categories/UIAlertController+Ext.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIAlertController+Ext.m
3 | // objc_SampleApp
4 | //
5 | // Created by Charlie on 18/11/19.
6 | // Copyright © 2019 Charlie. All rights reserved.
7 | //
8 |
9 | #import "UIAlertController+Ext.h"
10 | #import "Utils.h"
11 |
12 | //@interface UIAlertController(Ext)
13 | //
14 | //@end
15 |
16 | @implementation UIAlertController(Ext)
17 |
18 | void showAlert(NSString *title, NSString* message){
19 | UIAlertController * _alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
20 | UIAlertAction *_okBtn = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
21 | [_alert addAction:_okBtn];
22 | [topVC() presentViewController:_alert animated:YES completion:nil];
23 | }
24 | void showAlertTouchDismiss(NSString *title, NSString* message){
25 | UIAlertController * _alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
26 | [topVC() presentViewController:_alert animated:YES completion:^{
27 | [_alert.view.superview setUserInteractionEnabled:YES];
28 | [_alert.view.superview addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:_alert action:@selector(touchToDismiss)]];
29 | UIView *bg = [[UIView alloc] initWithFrame:_alert.view.bounds];
30 | bg.backgroundColor = [UIColor clearColor];
31 | [_alert.view addSubview:bg];
32 | [_alert.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:_alert action:@selector(touchToDismiss)]];
33 | }];
34 | }
35 | - (void)touchToDismiss{
36 | [self dismissViewControllerAnimated:YES completion:nil];
37 | }
38 | void showAlertAutoDismiss(NSString *title, NSString *message, float duration, UIViewController *vc){
39 | UIAlertController * _alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
40 | [vc presentViewController:_alert animated:YES completion:nil];
41 | // __weak typeof(vc) _weakVC = vc;
42 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
43 | [_alert dismissViewControllerAnimated:YES completion:nil];
44 |
45 | });
46 | }
47 | + (void)showAlertWithAutoDisappearTitle:(NSString *)title message:(NSString *)message duration:(CGFloat)duration onViewController:(UIViewController *)viewController
48 | {
49 | UIAlertController *alertVc = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
50 | [viewController presentViewController:alertVc animated:YES completion:^{
51 | }];
52 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
53 | [alertVc dismissViewControllerAnimated:YES completion:^{
54 | }];
55 | });
56 | }
57 | /*
58 | #pragma mark - Navigation
59 |
60 | // In a storyboard-based application, you will often want to do a little preparation before navigation
61 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
62 | // Get the new view controller using [segue destinationViewController].
63 | // Pass the selected object to the new view controller.
64 | }
65 | */
66 |
67 | @end
68 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/StatsView.m:
--------------------------------------------------------------------------------
1 | //
2 | // StatsView.m
3 | // SkylinkSample
4 | //
5 | // Created by Yuxi Liu on 5/9/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "StatsView.h"
10 |
11 | @implementation Stats
12 |
13 | - (instancetype)initWithDict:(NSDictionary *)dict
14 | {
15 | if (self = [super init]) {
16 | self.inputWidth = dict[@"FrameWidthInput"] ? dict[@"FrameWidthInput"] : @"480";
17 | self.inputHeight = dict[@"FrameHeightInput"] ? dict[@"FrameHeightInput"] : @"640";
18 | self.inputFPS = dict[@"FrameRateInput"] ? dict[@"FrameRateInput"] : @"30";
19 | self.sentWidth = dict[@"FrameWidthSent"] ? dict[@"FrameWidthSent"] : @"0";
20 | self.sentHeight = dict[@"FrameHeightSent"] ? dict[@"FrameHeightSent"] : @"0";
21 | self.sentFPS = dict[@"FrameRateSent"] ? dict[@"FrameRateSent"] : @"0";
22 | self.receivedWidth = dict[@"FrameWidthReceived"] ? dict[@"FrameWidthReceived"] : @"0";
23 | self.receivedHeight = dict[@"FrameHeightReceived"] ? dict[@"FrameHeightReceived"] : @"0";
24 | self.receivedFPS = dict[@"FrameRateReceived"] ? dict[@"FrameRateReceived"] : @"0";
25 | }
26 | return self;
27 | }
28 | @end
29 |
30 | @interface StatsView()
31 | @property (weak, nonatomic) IBOutlet UILabel *inputWidthLabel;
32 | @property (weak, nonatomic) IBOutlet UILabel *inputHeightLabel;
33 | @property (weak, nonatomic) IBOutlet UILabel *inputFPSLabel;
34 | @property (weak, nonatomic) IBOutlet UILabel *sentWidthLabel;
35 | @property (weak, nonatomic) IBOutlet UILabel *sentHeightLabel;
36 | @property (weak, nonatomic) IBOutlet UILabel *sentFPSLabel;
37 | @property (weak, nonatomic) IBOutlet UILabel *receivedWidthLabel;
38 | @property (weak, nonatomic) IBOutlet UILabel *receivedHeightLabel;
39 | @property (weak, nonatomic) IBOutlet UILabel *receivedFPSLabel;
40 | @end
41 |
42 | @implementation StatsView
43 |
44 | - (void)setupViewWithStats:(Stats *)stats status:(Status)status
45 | {
46 | dispatch_async(dispatch_get_main_queue(), ^{
47 | if (status == StatusInput) {
48 | self.inputWidthLabel.text = stats.inputWidth;
49 | self.inputHeightLabel.text = stats.inputHeight;
50 | self.inputFPSLabel.text = stats.inputFPS;
51 | }
52 | if (status == StatusSent) {
53 | self.sentWidthLabel.text = stats.sentWidth;
54 | self.sentHeightLabel.text = stats.sentHeight;
55 | self.sentFPSLabel.text = stats.sentFPS;
56 | }
57 | if (status == StatusReceived) {
58 | self.receivedWidthLabel.text = stats.receivedWidth;
59 | self.receivedHeightLabel.text = stats.receivedHeight;
60 | self.receivedFPSLabel.text = stats.receivedFPS;
61 | }
62 | if (status == StatusAll) {
63 | self.inputWidthLabel.text = stats.inputWidth;
64 | self.inputHeightLabel.text = stats.inputHeight;
65 | self.inputFPSLabel.text = stats.inputFPS;
66 | self.sentWidthLabel.text = stats.sentWidth;
67 | self.sentHeightLabel.text = stats.sentHeight;
68 | self.sentFPSLabel.text = stats.sentFPS;
69 | self.receivedWidthLabel.text = stats.receivedWidth;
70 | self.receivedHeightLabel.text = stats.receivedHeight;
71 | self.receivedFPSLabel.text = stats.receivedFPS;
72 | }
73 | });
74 | }
75 | @end
76 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/BaseVC.m:
--------------------------------------------------------------------------------
1 | //
2 | // BaseVC.m
3 | // SkylinkSample
4 | //
5 | // Created by Charlie on 26/11/19.
6 | // Copyright © 2019 Romain Pellen. All rights reserved.
7 | //
8 |
9 | #import "BaseVC.h"
10 | #import "AudioCallViewController.h"
11 | #import "VideoCallViewController.h"
12 | #import "MessagesViewController.h"
13 | #import "MultiVideoCallViewController.h"
14 | #import "FileTransferViewController.h"
15 | #import "DataTransferViewController.h"
16 |
17 | @interface BaseVC ()
18 |
19 | @end
20 |
21 | @implementation BaseVC
22 |
23 | - (void)viewDidLoad {
24 | [super viewDidLoad];
25 | [self initUI];
26 | [self setupRoomName];
27 | }
28 |
29 | - (void)initUI{
30 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"Cancel.png"] style:UIBarButtonItemStylePlain target:self action:@selector(backToMainMenu)];
31 | UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight];
32 | [infoButton addTarget:self action:@selector(showInfo) forControlEvents:UIControlEventTouchUpInside];
33 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:infoButton];
34 | }
35 | - (void)setupRoomName{
36 | if ([ROOM_NAME isNotEmpty]) {
37 | roomName = ROOM_NAME;
38 | return;
39 | }
40 | if ([self isKindOfClass:[AudioCallViewController class]]) {
41 | roomName = ROOM_AUDIO; return;
42 | }
43 | if ([self isKindOfClass:[VideoCallViewController class]]) {
44 | roomName = ROOM_ONE_TO_ONE_VIDEO; return;
45 | }
46 | if ([self isKindOfClass:[MessagesViewController class]]) {
47 | roomName = ROOM_MESSAGES; return;
48 | }
49 | if ([self isKindOfClass:[MultiVideoCallViewController class]]) {
50 | roomName = ROOM_MULTI_VIDEO; return;
51 | }
52 | if ([self isKindOfClass:[FileTransferViewController class]]) {
53 | roomName = ROOM_FILE_TRANSFER; return;
54 | }
55 | if ([self isKindOfClass:[DataTransferViewController class]]) {
56 | roomName = ROOM_DATA_TRANSFER; return;
57 | }
58 | }
59 | - (void)showInfo {
60 | showAlertTouchDismiss([NSString stringWithFormat:@"%@", NSStringFromClass([self class])], [NSString stringWithFormat:@"\nRoom name:\n%@\n\nLocal ID:\n%@\n\nKey: •••••%@\n\nSkylink version %@", roomName, _skylinkConnection.localPeerId, [APP_KEY substringFromIndex: [APP_KEY length] - 7], [SKYLINKConnection getSkylinkVersion]]);
61 | // showAlert([NSString stringWithFormat:@"%@ infos", NSStringFromClass([self class])], [NSString stringWithFormat:@"\nRoom name:\n%@\n\nLocal ID:\n%@\n\nKey: •••••%@\n\nSkylink version %@", roomName, _skylinkConnection.localPeerId, [APP_KEY substringFromIndex: [APP_KEY length] - 7], [SKYLINKConnection getSkylinkVersion]]);
62 | }
63 | - (void)backToMainMenu{
64 | [NSObject cancelPreviousPerformRequestsWithTarget:self];
65 | [_skylinkConnection unlockTheRoom:nil];
66 | [_skylinkConnection disconnect:^(NSError * _Nullable error) {
67 | if (!error) {
68 | [self.navigationController popViewControllerAnimated:YES];
69 | }
70 | }];
71 | }
72 | - (void)startLocalMediaDevice:(SKYLINKMediaDevice)mediaDevice{
73 | [_skylinkConnection createLocalMediaWithMediaDevice:mediaDevice mediaMetadata:@"" callback:^(NSError * _Nullable error) {
74 | if (error) {
75 | showAlertAutoDismiss(@"Error", error.localizedDescription, 2.0, self);
76 | }
77 | }];
78 | }
79 | - (void)joinRoom{
80 | [_skylinkConnection connectToRoomWithAppKey:APP_KEY secret:APP_SECRET roomName:roomName userData:USER_NAME callback:^(NSError * _Nullable error) {
81 | if (error) {
82 | NSLog(@"error: %@", error.localizedDescription);
83 | }
84 | }];
85 | }
86 | @end
87 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SettingCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/EncryptSecretCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/SettingsViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewController.m
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 29/08/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "SettingsViewController.h"
10 | #import "HomeViewController.h"
11 | #import "SettingCell.h"
12 | #import "EncryptSecretCell.h"
13 | #import "Constant.h"
14 |
15 | @interface SettingsViewController ()
16 |
17 | @property (nonatomic, strong) NSArray *appkey_secret_keys;
18 | @property (nonatomic, strong) NSArray *appkey_secret_values;
19 | @property (nonatomic, strong) NSArray *room_name_keys;
20 | @property (nonatomic, strong) NSArray *room_name_values;
21 | @end
22 |
23 | @implementation SettingsViewController{
24 | NSArray *_allAppKey;
25 | NSArray *_msg_encryption_secrets;
26 | }
27 |
28 | - (void)viewDidLoad {
29 | [super viewDidLoad];
30 | // Do any additional setup after loading the view.
31 |
32 | self.title = @"Settings";
33 | // UINib *nib = [UINib nibWithNibName:@"SettingCell" bundle:nil];
34 | [self.tableView registerNib:[UINib nibWithNibName:@"SettingCell" bundle:nil] forCellReuseIdentifier:CELL_IDENTIFIER];
35 | [self.tableView registerNib:[UINib nibWithNibName:@"EncryptSecretCell" bundle:nil] forCellReuseIdentifier:CELL_IDENTIFIER_ENCRYPT_SECRET];
36 | self.appkey_secret_keys = @[@"App Key", @"App Secret"];
37 | self.appkey_secret_values = @[APP_KEY, APP_SECRET];
38 | self.room_name_keys = @[@"1-1 video call", @"Multi video call", @"Audio call", @"Messages", @"File transfer", @"Data transfer"];
39 | self.room_name_values = @[ROOM_ONE_TO_ONE_VIDEO, ROOM_MULTI_VIDEO, ROOM_AUDIO, ROOM_MESSAGES, ROOM_FILE_TRANSFER, ROOM_DATA_TRANSFER];
40 | _allAppKey = [SAConstants.shared.APP_KEYS.allKeys sortedArrayUsingSelector:@selector(compare:)];
41 | _msg_encryption_secrets = [SAConstants.shared.ENCRYPTION_SECRETS.allKeys sortedArrayUsingSelector:@selector(compare:)];
42 | }
43 |
44 | // Room names should not bet set to empty
45 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
46 | {
47 | return 3;
48 | }
49 |
50 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
51 | {
52 | if (section == 0) return self.appkey_secret_keys.count + 1;
53 | else if (section == 1) return _msg_encryption_secrets.count;
54 | else return self.room_name_keys.count;
55 | }
56 |
57 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
58 | {
59 |
60 | if (indexPath.section == 0 && indexPath.row == 2) {
61 | UITableViewCell *_normalCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
62 | _normalCell.textLabel.text = @"Select App Key";
63 | _normalCell.textLabel.textColor = [UIColor whiteColor];
64 | _normalCell.backgroundColor = [UIColor colorWithRed:0.1764705926 green:0.01176470611 blue:0.5607843399 alpha:1];
65 | return _normalCell;
66 | }
67 |
68 | if (indexPath.section == 0 || indexPath.section == 2) {
69 | SettingCell *cell = [tableView dequeueReusableCellWithIdentifier:CELL_IDENTIFIER forIndexPath:indexPath];
70 | if (!cell) {
71 | cell = [[SettingCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CELL_IDENTIFIER];
72 | }
73 | (indexPath.section == 0) ?
74 | [cell setupCellWithKey:self.appkey_secret_keys[indexPath.row] value:self.appkey_secret_values[indexPath.row]] :
75 | [cell setupCellWithKey:self.room_name_keys[indexPath.row] value:self.room_name_values[indexPath.row]];
76 | return cell;
77 | }else{
78 | EncryptSecretCell *_encryptCell = [tableView dequeueReusableCellWithIdentifier:CELL_IDENTIFIER_ENCRYPT_SECRET];
79 | if (!_encryptCell) {
80 | _encryptCell = [[EncryptSecretCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CELL_IDENTIFIER_ENCRYPT_SECRET];
81 | }
82 | [_encryptCell setupCell:_msg_encryption_secrets[indexPath.row] secret:SAConstants.shared.ENCRYPTION_SECRETS[_msg_encryption_secrets[indexPath.row]]];
83 | return _encryptCell;
84 | }
85 | }
86 |
87 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
88 | {
89 | if (section == 0) {
90 | return @"Skylink developer credentials";
91 | }else if (section == 1){
92 | return @"Encrypt Secrets";
93 | } else {
94 | return @"Room names";
95 | }
96 | }
97 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
98 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
99 | if ((indexPath.section == 0) && (indexPath.row == 2)){
100 | [self selectAppKey];
101 | }
102 | }
103 | - (void)selectAppKey{
104 | UIAlertController *_alert = [UIAlertController alertControllerWithTitle:@"Choose a Secret App" message:nil preferredStyle:UIAlertControllerStyleAlert];
105 | UIAlertAction *_noAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:nil];
106 | for (NSString *appKey in _allAppKey) {
107 | UIAlertAction *_yesAction = [UIAlertAction actionWithTitle:appKey style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
108 | [self selectedAppKey:appKey];
109 | }];
110 | [_alert addAction:_yesAction];
111 | }
112 | [_alert addAction:_noAction];
113 | [self presentViewController:_alert animated:YES completion:nil];
114 | }
115 | - (void)selectedAppKey:(NSString *)key{
116 | APP_KEY = key;
117 | APP_SECRET = SAConstants.shared.APP_KEYS[key];
118 | self.appkey_secret_values = @[APP_KEY, APP_SECRET];
119 | [self.tableView reloadData];
120 | }
121 | @end
122 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # SkylinkSDK iOS SampleApp Objective C
2 |
3 | The Sample Application(SA), which uses the latest version of the Skylink SDK for iOS, demonstrates its use to provide embedded real time communication in the easiest way.
4 | Excluding 'Settings', this App has 6 distinct view controllers, each of them demonstrating how to build the following features:
5 |
6 | - One to one video call with Screen Share
7 | - Multi party video call
8 | - Multi party audio call
9 | - Chatroom and custom messages
10 | - File transfer
11 | - Data transfer
12 |
13 | ## Code introduction
14 | The code should be self explanatory. Each view controller works by itself and there is minimal UI code due to to Storyboard usage.
15 | In each view controller, the main idea is to **configure and instantiate a connection to a room with the Skylink iOS SDK**.
16 | You will then be able to communicate with another peer joining the same room.
17 |
18 | ##### Sample Code with Video and Audio
19 |
20 | // Creating configuration
21 | SKYLINKConnectionConfig *config = [SKYLINKConnectionConfig new];
22 | [config setAudioVideoReceiveConfig:AudioVideoConfig_AUDIO_AND_VIDEO];
23 | [config setAudioVideoSendConfig:AudioVideoConfig_AUDIO_AND_VIDEO];
24 |
25 | // Creating SKYLINKConnection
26 | SKYLINKConnection *skylinkConnection = [[SKYLINKConnection alloc] initWithConfig:config callback:nil];
27 | self.skylinkConnection.lifeCycleDelegate = self;
28 | self.skylinkConnection.mediaDelegate = self;
29 | self.skylinkConnection.remotePeerDelegate = self;
30 | self.skylinkConnection = skylinkConnection;
31 |
32 | // Coonnecting to room
33 | [skylinkConnection connectToRoomWithAppKey:self.skylinkApiKey secret:self.skylinkApiSecret roomName:ROOM_NAME userData:nil callback:nil];
34 |
35 |
36 | You can then control what happens in the room by **sending messages to the `SKYLINKConnection` instance** (like triggering a file transfer request for example), and **respond to events by implementing the delegate methods** from the 6 protocols.
37 | Always set at least the [lifeCycleDelegate](https://cdn.temasys.io/skylink/skylinksdk/ios/latest/docs/html/Protocols/SKYLINKConnectionLifeCycleDelegate.html). For a list of all protocols, see [here](https://cdn.temasys.io/skylink/skylinksdk/ios/latest/docs/html/index.html)
38 |
39 |
40 | Aditionally, in each view controller example's viewDidLoad/initWithCoder method, some properties are initialized.
41 | A disconnect button is set in the navigation bar (left corner) as well as its selector implementation (called disconnect). An info button is set on the right corner, as well as its implementation (called showInfos). Those 2 navigation bar buttons selectors are the same in every View Controller example.
42 |
43 | The rest of the example view controllers gives you 6 example usages of the Temasys iOS SDK.
44 |
45 | ## How to run the sample project
46 |
47 | ### Step-by-step guide
48 |
49 | ##### Prerequisites
50 | Please use Xcode 11
51 |
52 | ##### STEP 1
53 | It is recommended to install the SkylinkSDK for iOS via [cocoapods](http://cocoapods.org). If you do not have it installed, follow the below steps:
54 |
55 | ###### Installing Cocoapods
56 | Check that you have Xcode command line tools installed (Xcode > Preferences > Locations > Command line tools([?](http://osxdaily.com/2014/02/12/install-command-line-tools-mac-os-x/)). If not, open the terminal and run `xcode-select --install`.
57 | Install cocoa pods in the terminal: `$ sudo gem install cocoapods`
58 |
59 | ##### STEP 2
60 | Clone the repo or download the project.
61 |
62 | ##### STEP 3
63 | Navigate to the Sample App and Run `pod install`
64 |
65 | ##### STEP 4
66 | Open the .xcworkspace file
67 |
68 | ##### STEP 5
69 | Follow the instructions [here](https://temasys.io/creating-an-account-generating-a-key/) to create an App and a key on the Temasys Console.
70 |
71 | ##### STEP 6
72 | Set your App Key and secret in Constant.h. You may also alter the room names here.
73 |
74 | NSString *APP_KEY = @"ENTER APP KEY HERE";
75 | NSString *APP_SECRET = @"ENTER SECRET HERE";
76 |
77 | NSString *ROOM_ONE_TO_ONE_VIDEO = @"ROOM_ONE_TO_ONE_VIDEO";
78 | NSString *ROOM_MULTI_VIDEO = @"ROOM_MULTI_VIDEO";
79 | NSString *ROOM_AUDIO = @"ROOM_AUDIO";
80 | NSString *ROOM_MESSAGES = @"MESSAGES-ROOM";
81 | NSString *ROOM_FILE_TRANSFER = @"ROOM_FILE_TRANSFER";
82 | NSString *ROOM_DATA_TRANSFER = @"ROOM_DATA_TRANSFER";
83 |
84 |
85 | ##### STEP 7
86 | Build and Run. You're good to go!
87 |
88 | ##### Please Note
89 | The XCode Simulator does not support video calls.
90 | If you have connected a phone, ensure it is unlocked and the appropriate team is selected under Signing & Capabilities.
91 |
92 | ### Resources
93 |
94 |
95 | ##### SDK documentation
96 | For more information on the usage of the SkylinkSDK for iOS, please refer to [SkylinkSDK for iOS Readme](https://github.com/Temasys/SKYLINK-iOS/blob/master/README.md)
97 |
98 | ##### Subscribe
99 | Star this repo to be notified of new release tags. You can also view release notes on our [support portal](http://support.temasys.com.sg/en/support/solutions/folders/12000009706)
100 |
101 | ##### Feedback
102 | Please do not hesitate to reach get in touch with us if you encounter any issue or if you have any feedback or suggestions on how we can improve the Skylink SDK for iOS or Sample Applications. You can raise tickets on our [support portal](http://support.temasys.io/).
103 |
104 | ##### Copyright and License
105 | Copyright 2019 Temasys Communications Pte Ltd Licensed under APACHE 2.0
106 |
107 |
108 | #### Tutorials and FAQs
109 |
110 | [Getting started with Temasys iOS SDK for iOS](http://temasys.io/getting-started-skylinksdk-ios/)
111 | [Handle the video view stretching](http://temasys.io/a-simple-solution-for-video-stretching/)
112 | [FAQs](http://support.temasys.com.sg/support/solutions/12000000562)
113 |
114 |
115 | Also checkout our Skylink SDKs for [Web](http://skylink.io/web/) and [Android](http://skylink.io/android)
116 |
117 | *This document was edited for Temasys iOS SDK version 2.0.0*
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/HomeViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.m
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 11/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import "HomeViewController.h"
10 | #import "Constant.h"
11 |
12 | /// ====== SET YOUR KEY / SECRET HERE TO HAVE IT BY DEFAULT. ======
13 | #define SKYLINK_APP_KEY APP_KEY
14 | #define SKYLINK_SECRET APP_SECRET
15 | /// ===============================================================
16 |
17 |
18 |
19 |
20 | // Just the NSUserDefaults keys.
21 | #define USERDEFAULTS_KEY_SKYLINK_APP_KEY @"SKYLINK_APP_KEY"
22 | #define USERDEFAULTS_KEY_SKYLINK_SECRET @"SKYLINK_SECRET"
23 |
24 |
25 |
26 | @interface HomeViewController ()
27 |
28 | @property (weak, nonatomic) IBOutlet UITextField *roomNameTxt;
29 | @end
30 |
31 |
32 | @implementation HomeViewController
33 |
34 | - (void)viewDidLoad {
35 | [super viewDidLoad];
36 | _roomNameTxt.text = ROOM_NAME;
37 | }
38 |
39 | - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender {
40 | BOOL shouldPerform = true;// = (self.keyTextField.text.length == 36 && self.secretTextField.text.length == 13);
41 | if (!shouldPerform) {
42 | [UIAlertController showAlertWithAutoDisappearTitle:@"Wrong Key / Secret" message:@"\nYou haven't correctly set your \nSkylink API Key (36 characters) or Secret (13 characters)\n\nIf you don't have access to the API yet, enroll at \nconsole.temasys.io" duration:3 onViewController:self];
43 | } else {
44 | // [[NSUserDefaults standardUserDefaults] setObject:self.keyTextField.text forKey:USERDEFAULTS_KEY_SKYLINK_APP_KEY];
45 | // [[NSUserDefaults standardUserDefaults] setObject:self.secretTextField.text forKey:USERDEFAULTS_KEY_SKYLINK_SECRET];
46 | // [[NSUserDefaults standardUserDefaults] synchronize];
47 | }
48 | return shouldPerform;
49 | }
50 |
51 | #pragma clang diagnostic ignored "-Wundeclared-selector"
52 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
53 | // if ([segue.destinationViewController respondsToSelector:@selector(setSkylinkApiKey:)] && [segue.destinationViewController respondsToSelector:@selector(setSkylinkApiSecret:)]) {
54 | // [segue.destinationViewController performSelector:@selector(setSkylinkApiKey:) withObject:self.keyTextField.text];
55 | // [segue.destinationViewController performSelector:@selector(setSkylinkApiSecret:) withObject:self.secretTextField.text];
56 | // }
57 | }
58 |
59 | #pragma mark - IBActions (info boxes)
60 | // Information alerts only
61 | - (IBAction)vcInfoClicked:(UIButton *)sender{
62 | NSArray *alertTitles = @[@"Audio", @"One to one Media", @"Chat", @"Mutiparty", @"File Transfer", @"Data Streaming"];
63 | NSArray *alertMessages = @[@"\nEnter the room to make an audio call with the other peers inside the same room. Tap the button on top to mute/unmute your microphone.\n\nRefer to the view controller's code and to the documentation for more infos.",
64 | @"\nOne to one video call sample\n\nThe first 2 people to enter the room will be able to have a video call. The bottom bar contains buttons to refresh the peer connection, mute/unmute the camera, mute/unmute the mic and switch the device camera if available.\n\nRefer to the view controller's code and to the documentation for more infos.\n",
65 | @"\nEnter the room to chat with the peers in the same room. The first text field allows you to edit your nickname, the yellow button indicates the number of peers in the room: tap it to display theirs ID and nickname if available, tap the icon to hide the keyboard if needed. There is also a button to select the type of messages you want to test (P2P, signeling server or binary data), and another one to choose if you want to send your messages to all the peers in the room (public) or privatly. If not public, you will be ask what peer you want to send your private message to when tapping the send button. To send a message, enter it in the second text field and tap the send button. The messages you sent appear in green.\n\nRefer to the view controller's code and to the documentation for more infos.\n",
66 | @"\nThe first 4 people to enter the room will be able to have a multi party video call (as long as the room isn't locked). The bottom bar contains buttons to change the aspect of the peer video views, lock/unlock the room, mute/unmute the camera, mute/unmute the mic and switch the device camera if available.\n\nRefer to the view controller's code and to the documentation for more infos.\n",
67 | @"\nEnter the room to send file to the peers in the same room. To send a file to all the peers, tap the main button, to send it to a particular peer, tap the peer in the list. In both cases you will be asked the king of media you want to send and to pick it if needed.\nBehaviour will be slightly different with MCU enabled.\n\nRefer to the view controller's code and to the documentation for more infos.\n",
68 | @"\nEnter the room to send data to the peers in the same room. To send a file to all the peers, tap the main button, to send it to a particular peer, tap the peer in the list. In both cases you will be asked the king of media you want to send and to pick it if needed.\nBehaviour will be slightly different with MCU enabled.\n\nRefer to the view controller's code and to the documentation for more infos.\n"];
69 | showAlertTouchDismiss(alertTitles[sender.tag - 10], alertMessages[sender.tag - 10]);
70 | }
71 | - (IBAction)homeInfoTap:(UIButton *)sender {
72 | [UIAlertController showAlertWithAutoDisappearTitle:@"HomeViewController" message:@"\nSet you Skylink API Key and secret in the appropriate text field or modify HomeViewController's code to have it by default.\nIf you don't have your Key/Secret, enroll at console.temasys.io\n\nIn all view controllers, you can tap the info button in the upper right corner to get the current room name, your current local ID, the current API key and the current SDK version. Refer to the documentation for more infos on how to use it.\n" duration:3 onViewController:self];
73 | }
74 |
75 | #pragma mark - TextField Delegate
76 | - (BOOL)textFieldShouldReturn:(UITextField *)textField{
77 | [textField resignFirstResponder];
78 | return YES;
79 | }
80 | - (void)textFieldDidChangeSelection:(UITextField *)textField{
81 | if (textField == self.roomNameTxt) {
82 | ROOM_NAME = textField.text;
83 | }
84 | }
85 | - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
86 | if (textField == self.roomNameTxt) {
87 | NSString *updateString = [textField.text stringByReplacingCharactersInRange:range withString:string];
88 | ROOM_NAME = updateString;
89 | }
90 | return YES;
91 | }
92 | - (BOOL)textFieldShouldClear:(UITextField *)textField{
93 | ROOM_NAME = nil;
94 | return YES;
95 | }
96 | @end
97 |
98 |
99 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/AudioCallViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // AudioCallViewController.m
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 07/01/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "AudioCallViewController.h"
10 | #import "Constant.h"
11 |
12 | //#define ROOM_NAME [[NSUserDefaults standardUserDefaults] objectForKey:@"ROOMNAME_AUDIOCALL"]
13 |
14 |
15 | @interface AudioCallViewController ()
16 | // IBOutlets
17 | @property (weak, nonatomic) IBOutlet UITableView *tableView;
18 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
19 |
20 | // Other properties
21 | @property (strong, nonatomic) NSMutableArray *remotePeerArray;
22 | @end
23 |
24 |
25 | @implementation AudioCallViewController
26 |
27 | - (void)viewDidLoad {
28 | [super viewDidLoad];
29 | // Do any additional setup after loading the view.
30 |
31 | self.title = @"Audio Call";
32 | self.remotePeerArray = [NSMutableArray array];
33 | // Creating configuration
34 | SKYLINKConnectionConfig *config = [SKYLINKConnectionConfig new];
35 | [config setAudioVideoSendConfig:AudioVideoConfig_AUDIO_ONLY];
36 | [config setAudioVideoReceiveConfig:AudioVideoConfig_AUDIO_ONLY];
37 |
38 | // Creating SKYLINKConnection
39 | _skylinkConnection = [[SKYLINKConnection alloc] initWithConfig:config callback:nil];
40 | _skylinkConnection.lifeCycleDelegate = self;
41 | _skylinkConnection.mediaDelegate = self;
42 | _skylinkConnection.remotePeerDelegate = self;
43 |
44 | // Connecting to a room
45 | // connectToRoomWithCredentials example
46 | // NSDictionary *credInfos = @{@"startTime" : [NSDate date], @"duration" : [NSNumber numberWithFloat:24.000f]};
47 | // NSString *credential = [SKYLINKConnection calculateCredentials:ROOM_AUDIO duration:credInfos[@"duration"] startTime:credInfos[@"startTime"] secret:APP_SECRET];
48 | // [_skylinkConnection connectToRoomWithStringURL:credential userData:USER_NAME callback:nil];
49 | [self joinRoom];
50 | }
51 |
52 | #pragma mark - SKYLINKConnectionLifeCycleDelegate
53 | - (void)connectionDidConnectToRoomSuccessful:(SKYLINKConnection *)connection
54 | {
55 | MyLog(@"Inside %s", __FUNCTION__);
56 | dispatch_async(dispatch_get_main_queue(), ^{
57 | [self.activityIndicator stopAnimating];
58 | [self startLocalAudio];
59 | });
60 | }
61 | - (void)connection:(SKYLINKConnection *)connection didConnectToRoomFailed:(NSString *)errorMessage
62 | {
63 | [UIAlertController showAlertWithAutoDisappearTitle:@"Connection failed" message:errorMessage duration:3 onViewController:self];
64 | [self.navigationController popViewControllerAnimated:YES];
65 | }
66 |
67 | - (void)connection:(SKYLINKConnection *)connection didDisconnectFromRoomWithSkylinkEvent:(NSDictionary *)skylinkEvent contextDescription:(NSString *)contextDescription
68 | {
69 | // showAlertAutoDismiss(@"Disconnected", skylinkEvent.description, 3, self);
70 | // [UIAlertController showAlertWithAutoDisappearTitle:@"Disconnected" message:skylinkEvent.description duration:3 onViewController:self];
71 | // [self.navigationController popViewControllerAnimated:YES];
72 | }
73 | #pragma mark - SKYLINKConnectionMediaDelegate
74 |
75 | - (void)connection:(SKYLINKConnection *)connection didToggleAudio:(BOOL)isMuted peerId:(NSString *)peerId {
76 | NSArray *enumarateArray = [self.remotePeerArray copy];
77 | for (NSDictionary *peerDic in enumarateArray) {
78 | if ([peerDic[@"id"] isEqualToString:peerId]) {
79 | [self.remotePeerArray removeObject:peerDic];
80 | [self.remotePeerArray addObject:@{@"id" : peerId, @"isAudioMuted" : [NSNumber numberWithBool:isMuted]}];
81 | }
82 | }
83 | [self.tableView reloadData];
84 | }
85 |
86 | #pragma mark - SKYLINKConnectionRemotePeerDelegate
87 | - (void)connection:(SKYLINKConnection *)connection didConnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel
88 | {
89 | MyLog(@"Peer with id %@ joigned the room.", remotePeerId);
90 | [self.remotePeerArray addObject:@{@"id" : remotePeerId, @"isAudioMuted" : @(NO), @"nickname" : ([userInfo isKindOfClass:[NSString class]]) ? userInfo : @""}];
91 | [self.tableView reloadData];
92 | }
93 |
94 | - (void)connection:(SKYLINKConnection *)connection didDisconnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel
95 | {
96 | MyLog(@"Peer with id %@ left the room with userInfo: %@", remotePeerId, userInfo);
97 | NSDictionary *dicToRemove;
98 | for (NSDictionary *peerDic in self.remotePeerArray)
99 | if ([peerDic[@"id"] isEqualToString:remotePeerId]) dicToRemove = peerDic;
100 | [self.remotePeerArray removeObject:dicToRemove];
101 | [self.tableView reloadData];
102 | }
103 |
104 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemotePeerUserData:(id)userData remotePeerId:(NSString *)remotePeerId
105 | {
106 |
107 | }
108 | #pragma mark - Table view data source
109 |
110 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
111 | return [NSString stringWithFormat:@"%lu peer(s) connected", (unsigned long)self.remotePeerArray.count];
112 | }
113 |
114 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
115 | return 1;
116 | }
117 |
118 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
119 | return self.remotePeerArray.count;
120 | }
121 |
122 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
123 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ACpeerCell"];
124 |
125 | NSDictionary *peerDic = [self.remotePeerArray objectAtIndex:indexPath.row];
126 | cell.textLabel.text = peerDic[@"nickname"] ? peerDic[@"nickname"] : [NSString stringWithFormat:@"Peer %ld", (long)indexPath.row];
127 | cell.detailTextLabel.text = [NSString stringWithFormat:@"ID: %@ %@", peerDic[@"id"], [peerDic[@"isAudioMuted"] boolValue] ? @" - Audio muted" : @""];
128 |
129 | cell.backgroundColor = [UIColor colorWithRed:0.35 green:0.35 blue:0.35 alpha:1.00]; // iPads does not use storyboard bg color value
130 | return cell;
131 | }
132 |
133 | #pragma mark - Table view delegate
134 |
135 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
136 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
137 | }
138 |
139 | #pragma mark - IBAction
140 |
141 | - (IBAction)switchAudioTap:(UIButton *)sender {
142 | [sender setTitle:(!_skylinkConnection.isAudioMuted ? @"Unmute microphone" : @"Mute microphone") forState:UIControlStateNormal];
143 | [_skylinkConnection muteAudio:!_skylinkConnection.isAudioMuted];
144 | }
145 |
146 | - (void)startLocalAudio
147 | {
148 | [_skylinkConnection createLocalMediaWithMediaDevice:SKYLINKMediaDeviceMicrophone mediaMetadata:nil callback:^(NSError * _Nullable error) {
149 | if (error) [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
150 | }];
151 | }
152 |
153 | - (void)connection:(SKYLINKConnection *)connection didChangeSkylinkMedia:(SKYLINKMedia *)skylinkMedia peerId:(NSString *)peerId
154 | {
155 | if (skylinkMedia.skylinkMediaType == SKYLINKMediaTypeAudio) {
156 |
157 | }
158 | }
159 | @end
160 |
161 |
162 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/DataTransferViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // DataTransferViewController.m
3 | // SkylinkSample
4 | //
5 | // Created by Temasys on 08/06/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "DataTransferViewController.h"
10 | #import "Constant.h"
11 |
12 | //#define ROOM_NAME [[NSUserDefaults standardUserDefaults] objectForKey:@"ROOMNAME_DATATRANSFER"]
13 |
14 | @interface DataTransferViewController ()
15 | @property (weak, nonatomic) IBOutlet UIView *localColorView;
16 | @property (weak, nonatomic) IBOutlet UISlider *redSlider;
17 | @property (weak, nonatomic) IBOutlet UISlider *greenSlider;
18 | @property (weak, nonatomic) IBOutlet UISlider *blueSlider;
19 | @property (weak, nonatomic) IBOutlet UITextView *infoTextView;
20 | @property (weak, nonatomic) IBOutlet UISwitch *isContinuousSwitch;
21 | @property (weak, nonatomic) IBOutlet UIButton *sendColorButton;
22 | @property (weak, nonatomic) IBOutlet UIImageView *imageView;
23 |
24 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
25 |
26 | @property (strong, nonatomic) NSTimer *timer;
27 |
28 | @end
29 |
30 | @implementation DataTransferViewController
31 |
32 | - (void)viewDidLoad {
33 | [super viewDidLoad];
34 | // Do any additional setup after loading the view.
35 |
36 | self.title = @"Data Transfer";
37 |
38 | SKYLINKConnectionConfig *config = [SKYLINKConnectionConfig new];
39 | [config setAudioVideoSendConfig:AudioVideoConfig_NO_AUDIO_NO_VIDEO];
40 | [config setAudioVideoReceiveConfig:AudioVideoConfig_NO_AUDIO_NO_VIDEO];
41 | config.hasDataTransfer = YES;
42 | _skylinkConnection = [[SKYLINKConnection alloc] initWithConfig:config callback:nil];
43 | _skylinkConnection.messagesDelegate = self;
44 | _skylinkConnection.lifeCycleDelegate = self;
45 | _skylinkConnection.remotePeerDelegate = self;
46 | [self joinRoom];
47 |
48 | [self refreshUI];
49 | }
50 |
51 | #pragma mark SKYLINKConnectionLifeCycleDelegate
52 | - (void)connectionDidConnectToRoomSuccessful:(SKYLINKConnection *)connection
53 | {
54 | [self showUIInfo:@"DID CONNECT • success "];
55 | [self.activityIndicator stopAnimating];
56 | }
57 |
58 | - (void)connection:(SKYLINKConnection *)connection didConnectToRoomFailed:(NSString *)errorMessage
59 | {
60 | [self showUIInfo:@"Failed to connect"];
61 | }
62 | #pragma mark SKYLINKConnectionRemotePeerDelegate
63 | - (void)connection:(SKYLINKConnection *)connection didConnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel
64 | {
65 | [self showUIInfo:[NSString stringWithFormat:@"👤 DID JOIN PEER •\npeerID = %@, properties = %@", remotePeerId, userInfo]];
66 | }
67 |
68 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemotePeerLeaveRoom:(NSString *)remotePeerId userInfo:(id)userInfo skylinkInfo:(NSDictionary *)skylinkInfo
69 | {
70 | [self showUIInfo:[NSString stringWithFormat:@"✋🏼 DID LEAVE PEER • peerID = %@, message = \n%@", remotePeerId, userInfo]];
71 | }
72 | #pragma mark SKYLINKConnectionMessagesDelegate
73 | - (void)connection:(SKYLINKConnection *)connection didReceiveData:(NSData *)data remotePeerId:(NSString *)remotePeerId
74 | {
75 | if (data != nil) {
76 | NSString *dataType;
77 | id unarchivedData = [NSKeyedUnarchiver unarchiveObjectWithData:data];
78 | if ([unarchivedData isKindOfClass:[UIColor class]]) {
79 | dataType = @"UIColor";
80 | self.view.backgroundColor = unarchivedData;
81 | } else if ([unarchivedData isKindOfClass:[UIImage class]]) {
82 | dataType = @"UIImage";
83 | self.imageView.image = unarchivedData;
84 | [UIView animateWithDuration:1 delay:3 options:0 animations:^(void) {
85 | self.imageView.alpha = 0;
86 | } completion:^(BOOL finished) {
87 | self.imageView.image = nil;
88 | self.imageView.alpha = 1;
89 | }];
90 | } else {
91 | dataType = @"OTHER";
92 | }
93 | [self showUIInfo:[NSString stringWithFormat:@"Received data of type '%@' and lenght: %lu", dataType, (unsigned long)data.length]];
94 | }
95 | }
96 |
97 | #pragma mark IBActions
98 |
99 | - (IBAction)sendDataTap:(UIButton*)sender {
100 | [self sendCurrentColor];
101 | }
102 | - (IBAction)onAnySliderChange:(UISlider *)sender {
103 | if (self.isContinuousSwitch.isOn) [self sendCurrentColor];
104 | [self refreshUI];
105 | }
106 | - (IBAction)isContinuousSwitchChanged:(UISwitch *)sender {
107 | if (sender.isOn) [self sendCurrentColor];
108 | [self refreshUI];
109 | }
110 | - (IBAction)sendImageTap:(UIButton *)sender {
111 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"dataTransferImage" ofType:@"png" inDirectory:@"DataTransferSamples"];
112 | UIImage *sampleImage = [UIImage imageWithContentsOfFile:filePath];
113 | UIImage *sampleImage2 = [UIImage imageWithCGImage:sampleImage.CGImage scale:sampleImage.scale orientation:sampleImage.imageOrientation];
114 | [self showUIInfo:@"Sending sample image..."];
115 | [_skylinkConnection sendData:[NSKeyedArchiver archivedDataWithRootObject:sampleImage2] toRemotePeerId:nil callback:^(NSError * _Nullable error) {
116 | if (error) [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
117 | }];
118 | }
119 | - (IBAction)autoChangeColorSwitchChanged:(UISwitch *)sender {
120 | if (sender.isOn) {
121 | NSDate *d = [NSDate dateWithTimeIntervalSinceNow:0.0];
122 | self.timer = [[NSTimer alloc] initWithFireDate: d interval: 0.04 target: self selector:@selector(onTick:) userInfo:nil repeats:YES];
123 | NSRunLoop *runner = [NSRunLoop currentRunLoop];
124 | [runner addTimer:self.timer forMode:NSDefaultRunLoopMode];
125 | } else {
126 | [self.timer invalidate];
127 | self.timer = nil;
128 | }
129 | }
130 |
131 | #pragma mark Utils
132 |
133 | - (void)refreshUI {
134 | self.localColorView.backgroundColor = [self slidersUIColor];
135 | self.sendColorButton.hidden = self.isContinuousSwitch.isOn;
136 | }
137 |
138 | - (void)onTick:(NSTimer *)timer {
139 | float increment = 0.004;
140 | self.redSlider.value = (self.redSlider.value + increment > 1) ? 0 : self.redSlider.value + increment;
141 | self.greenSlider.value = (self.greenSlider.value + 1.9 * increment > 1) ? 0 : self.greenSlider.value + 1.9 * increment;
142 | self.blueSlider.value = (self.blueSlider.value + 3.1 * increment > 1) ? 0 : self.blueSlider.value + 3.1 * increment;
143 | if (self.isContinuousSwitch.isOn) [self sendCurrentColor];
144 | [self refreshUI];
145 | }
146 |
147 | - (void)sendCurrentColor {
148 | [self showUIInfo:@"Sending current local color..."];
149 | NSData *colorData = [NSKeyedArchiver archivedDataWithRootObject:[self slidersUIColor]];
150 | [_skylinkConnection sendData:colorData toRemotePeerId:nil callback:^(NSError * _Nullable error) {
151 | if (error) [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
152 | }];
153 | }
154 |
155 | - (void)showUIInfo:(NSString *)infoMessage {
156 | self.infoTextView.text = [NSString stringWithFormat:@"[%.3f] %@\n%@", CFAbsoluteTimeGetCurrent(), infoMessage, self.infoTextView.text];
157 | }
158 |
159 | - (UIColor *)slidersUIColor {
160 | return [UIColor colorWithRed:self.redSlider.value green:self.greenSlider.value blue:self.blueSlider.value alpha:1.0];
161 | }
162 |
163 |
164 | @end
165 |
166 |
167 |
168 |
169 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/VideoCallViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // VideCallViewController.m
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 11/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import "VideoCallViewController.h"
10 | #import
11 |
12 | //#define ROOM_NAME [[NSUserDefaults standardUserDefaults] objectForKey:@"ROOMNAME_ONETOONEVIDEOCALL"]
13 |
14 | @interface VideoCallViewController ()
15 |
16 | // IBOutlets
17 | @property (weak, nonatomic) IBOutlet UIView *localVideoContainerView; // note: .clipsToBounds property set to YES via storyboard;
18 | @property (weak, nonatomic) IBOutlet UIView *localVideoContainerView2;
19 | @property (weak, nonatomic) IBOutlet UIView *remotePeerVideoContainerView;
20 | @property (weak, nonatomic) IBOutlet UIView *remotePeerVideoContainerView2;
21 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
22 | @property (weak, nonatomic) IBOutlet UIButton *callButton;
23 | @property (strong, nonatomic) IBOutletCollection(UIView) NSArray *videoViews;
24 |
25 | @end
26 |
27 | @implementation VideoCallViewController
28 | {
29 | BOOL _isJoinRoom;
30 | NSMutableArray *_localMedias;
31 | NSMutableArray *_remoteMedias;
32 | NSString *_remotePeerId;
33 | }
34 | #pragma mark - INIT
35 | - (void)viewDidLoad {
36 | [super viewDidLoad];
37 | self.title = @"1-1 Video Call";
38 | [self initData];
39 | }
40 | - (void)initData{
41 | _isJoinRoom = NO;
42 | _localMedias = [NSMutableArray new];
43 | _remoteMedias = [NSMutableArray new];
44 | APP_KEY = APP_KEY;
45 | APP_SECRET = APP_SECRET;
46 | // Creating configuration
47 | SKYLINKConnectionConfig *config = [SKYLINKConnectionConfig new];
48 | [config setAudioVideoReceiveConfig:AudioVideoConfig_AUDIO_AND_VIDEO];
49 | [config setAudioVideoSendConfig:AudioVideoConfig_AUDIO_AND_VIDEO];
50 | config.isMultiTrackCreateEnable = YES;
51 | config.roomSize = SKYLINKRoomSizeSmall;
52 | config.isMirrorLocalFrontCameraView = true;
53 |
54 | // Creating SKYLINKConnection
55 | _skylinkConnection = [[SKYLINKConnection alloc] initWithConfig:config callback:nil];
56 | _skylinkConnection.lifeCycleDelegate = self;
57 | _skylinkConnection.mediaDelegate = self;
58 | _skylinkConnection.remotePeerDelegate = self;
59 | _skylinkConnection.enableLogs = YES;
60 | }
61 |
62 | #pragma mark - SKYLINKConnectionLifeCycleDelegate
63 | - (void)connectionDidConnectToRoomSuccessful:(SKYLINKConnection *)connection{
64 | // skylinkLog("Inside \(#function)");
65 | self.title = roomName;
66 | [self.callButton setBackgroundImage:[UIImage imageNamed:@"call_off"] forState:UIControlStateNormal];
67 | // __weak __typeof(self)weakSelf = self;
68 | // dispatch_async(dispatch_get_main_queue(), ^{
69 | // __strong __typeof(weakSelf)strongSelf = weakSelf;
70 | //
71 | // });
72 | [self.activityIndicator stopAnimating];
73 | }
74 | - (void)connection:(SKYLINKConnection *)connection didConnectToRoomFailed:(NSString *)errorMessage{
75 | showAlert(@"Connection failed!", errorMessage);
76 | }
77 | - (void)connection:(SKYLINKConnection *)connection didDisconnectFromRoomWithSkylinkEvent:(NSDictionary *)skylinkEvent contextDescription:(NSString *)contextDescription{
78 | [self.callButton setBackgroundImage:[UIImage imageNamed:@"call_on"] forState:UIControlStateNormal];
79 | [self.activityIndicator stopAnimating];
80 | }
81 | #pragma mark - SKYLINKConnectionMediaDelegate
82 | - (void)connection:(SKYLINKConnection *)connection didCreateLocalMedia:(SKYLINKMedia *)localMedia{
83 | if (!localMedia) {
84 | return;
85 | }
86 | [_localMedias addObject:localMedia];
87 | [self reloadVideoView];
88 | [self.activityIndicator stopAnimating];
89 | }
90 | - (void)connection:(SKYLINKConnection *)connection didChangeLocalMedia:(SKYLINKMedia *)localMedia{
91 | NSLog(@"changed local media %d", localMedia.skylinkMediaState);
92 | [self.activityIndicator stopAnimating];
93 | }
94 |
95 | - (void)connection:(SKYLINKConnection *)connection didChangeRemoteMedia:(SKYLINKMedia *)remoteMedia remotePeerId:(NSString *)remotePeerId{
96 | NSInteger _index = -1;
97 | for (SKYLINKMedia *media in _remoteMedias) {
98 | if (media.skylinkMediaID == remotePeerId) {
99 | _index = [_remoteMedias indexOfObject:media];
100 | }
101 | }
102 | if (_index>=0) {
103 | [_remoteMedias replaceObjectAtIndex:_index withObject:remoteMedia];
104 | [self reloadVideoView];
105 | }
106 | }
107 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemoteMedia:(SKYLINKMedia *)remoteMedia remotePeerId:(NSString *)remotePeerId{
108 | if (!remoteMedia || !remotePeerId) {
109 | return;
110 | }
111 | [_remoteMedias addObject:remoteMedia];
112 | [self reloadVideoView];
113 | }
114 | - (void)connection:(SKYLINKConnection *)connection didChangeVideoSize:(CGSize)videoSize videoView:(UIView *)videoView peerId:(NSString *)peerId{
115 | if (videoSize.height > 0 && videoSize.width > 0) {
116 | for (UIView* container in @[self.localVideoContainerView, self.localVideoContainerView2, self.remotePeerVideoContainerView, self.remotePeerVideoContainerView2]) {
117 | if ([videoView isDescendantOfView:container]) {
118 | [videoView aspectFitRectForSize:videoSize container:container];
119 | }
120 | }
121 | }
122 | }
123 | - (void)connection:(SKYLINKConnection *)connection didDestroyLocalMedia:(SKYLINKMedia *)localMedia{
124 | // NSInteger _index = -1;
125 | for (SKYLINKMedia *media in _localMedias ) {
126 | if ([media isEqual:localMedia]) {
127 | dispatch_async(dispatch_get_main_queue(), ^{
128 | [self->_localMedias removeObject:media];
129 | [self reloadVideoView];
130 | return;
131 | });
132 | }
133 | }
134 | // if (_index>=0) {
135 | // dispatch_async(dispatch_get_main_queue(), ^{
136 | // [self->_localMedias removeObjectAtIndex:_index];
137 | // [self reloadVideoView];
138 | // });
139 | // }
140 | }
141 |
142 | #pragma mark - SKYLINKConnectionRemotePeerDelegate
143 | - (void)connection:(SKYLINKConnection *)connection didConnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel{
144 | showAlertAutoDismiss(nil, [NSString stringWithFormat:@"%@ has joined room\nUserData:%@", remotePeerId, userInfo[@"userData"]], 2, self);
145 | _remotePeerId = remotePeerId;
146 | [self.activityIndicator stopAnimating];
147 | }
148 | - (void)connection:(SKYLINKConnection *)connection didDisconnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel{
149 | [_remoteMedias removeAllObjects];
150 | [self reloadVideoView];
151 | [self.activityIndicator stopAnimating];
152 | }
153 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemotePeerLeaveRoom:(NSString *)remotePeerId userInfo:(id)userInfo skylinkInfo:(NSDictionary *)skylinkInfo{
154 |
155 | }
156 |
157 | #pragma mark - Private functions
158 | - (void)addRenderedVideo:(UIView *)videoView insideContainer:(UIView *)containerView mirror:(BOOL)shouldMirror {
159 | [videoView aspectFitRectForSize:videoView.frame.size container:containerView];
160 | [containerView removeSubviews];
161 | [containerView insertSubview:videoView atIndex:0];
162 | }
163 | - (void)changeLocalMediaStateWithMediaId:(NSString*)mediaId state:(SKYLINKMediaState)state{
164 | [self.activityIndicator startAnimating];
165 | [_skylinkConnection changeLocalMediaStateWithMediaId:mediaId mediaState:state callback:^(NSError * _Nullable error) {
166 | if (error) {
167 | [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
168 | }
169 | [self.activityIndicator stopAnimating];
170 | }];
171 | }
172 |
173 | - (void)destroyLocalMedia{
174 | while (_localMedias.count>0) {
175 | SKYLINKMedia *media = _localMedias.firstObject;
176 | [_skylinkConnection destroyLocalMediaWithMediaId:media.skylinkMediaID callback:nil];
177 | }
178 | }
179 | - (void)reloadVideoView{
180 | for (UIView *container in _videoViews) {
181 | [container removeSubviews];
182 | }
183 | for (SKYLINKMedia *localMedia in _localMedias) {
184 | if (localMedia.skylinkMediaType == SKYLINKMediaTypeVideoCamera) {
185 | [self addRenderedVideo:localMedia.skylinkVideoView insideContainer:self.localVideoContainerView mirror:true];
186 | }else if(localMedia.skylinkMediaType == SKYLINKMediaTypeVideoScreen){
187 | [self addRenderedVideo:localMedia.skylinkVideoView insideContainer:self.localVideoContainerView2 mirror:true];
188 | }
189 | }
190 | for (SKYLINKMedia *remoteMedia in _remoteMedias) {
191 | if (remoteMedia.skylinkMediaType == SKYLINKMediaTypeVideoCamera) {
192 | [self addRenderedVideo:remoteMedia.skylinkVideoView insideContainer:self.remotePeerVideoContainerView mirror:false];
193 | }else if(remoteMedia.skylinkMediaType == SKYLINKMediaTypeVideoScreen){
194 | [self addRenderedVideo:remoteMedia.skylinkVideoView insideContainer:self.remotePeerVideoContainerView2 mirror:false];
195 | }
196 | }
197 | }
198 |
199 |
200 | #pragma mark - IBActions
201 | - (IBAction)joinRoom{
202 | [self.activityIndicator startAnimating];
203 | if (_isJoinRoom) {
204 | [NSObject cancelPreviousPerformRequestsWithTarget:self];
205 | [_skylinkConnection unlockTheRoom:nil];
206 | [_skylinkConnection disconnect:^(NSError * _Nullable error) {
207 | if (error) {
208 | [self.remotePeerVideoContainerView removeSubviews];
209 | [self.remotePeerVideoContainerView2 removeSubviews];
210 | // self.localVideoContainerView.removeSubviews()
211 | [self destroyLocalMedia];
212 | [self.callButton setBackgroundImage:[UIImage imageNamed:@"call_on"] forState:UIControlStateNormal];
213 | [self.activityIndicator stopAnimating];
214 | }
215 | }];
216 | }else{
217 | [super joinRoom];
218 | }
219 | _isJoinRoom = !_isJoinRoom;
220 | }
221 |
222 | - (IBAction)startCamera{
223 | [self startLocalMediaDevice:SKYLINKMediaDeviceCameraFront];
224 | }
225 |
226 | - (IBAction)startAudio{
227 | [self startLocalMediaDevice:SKYLINKMediaDeviceMicrophone];
228 | }
229 |
230 | - (IBAction)startScreen{
231 | [self startLocalMediaDevice:SKYLINKMediaDeviceScreen];
232 | }
233 |
234 | - (IBAction)videoStateChanged:(UISegmentedControl*)sender{
235 | if (!_isJoinRoom) {
236 | sender.selectedSegmentIndex = 0;
237 | return;
238 | }
239 | for(SKYLINKMedia *media in _localMedias){
240 | if (media.skylinkMediaType == SKYLINKMediaTypeVideoCamera){
241 | [self changeLocalMediaStateWithMediaId:media.skylinkMediaID state:(int)sender.selectedSegmentIndex+1];
242 | }
243 | }
244 | }
245 |
246 | - (IBAction)audioStateChanged:(UISegmentedControl*)sender{
247 | if (!_isJoinRoom){
248 | sender.selectedSegmentIndex = 0;
249 | return;
250 | }
251 | for(SKYLINKMedia *media in _localMedias){
252 | if (media.skylinkMediaType == SKYLINKMediaTypeAudio){
253 | [self changeLocalMediaStateWithMediaId:media.skylinkMediaID state:(int)sender.selectedSegmentIndex+1];
254 | }
255 | }
256 | }
257 |
258 | - (IBAction)screenStateChanged:(UISegmentedControl*)sender{
259 | if (!_isJoinRoom){
260 | sender.selectedSegmentIndex = 0;
261 | return;
262 | }
263 | for(SKYLINKMedia *media in _localMedias){
264 | if (media.skylinkMediaType == SKYLINKMediaTypeVideoScreen){
265 | [self changeLocalMediaStateWithMediaId:media.skylinkMediaID state:(int)sender.selectedSegmentIndex+1];
266 | }
267 | }
268 | }
269 | - (IBAction)removeTrack:(UIButton*)sender{
270 | for (SKYLINKMedia *media in _localMedias) {
271 | if (media.skylinkMediaType == sender.tag) {
272 | [_skylinkConnection destroyLocalMediaWithMediaId:media.skylinkMediaID callback:^(NSError * _Nullable error) {
273 | if (error) {
274 | NSLog(@"failed to remove track %@", error);
275 | }
276 | }];
277 | }
278 | }
279 | }
280 | @end
281 |
282 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/MultiVideoCallViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MultiVideoCallViewController.m
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 11/01/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "MultiVideoCallViewController.h"
10 | #import
11 | #import "Constant.h"
12 | #import "Peer.h"
13 | //#define ROOM_NAME [[NSUserDefaults standardUserDefaults] objectForKey:@"ROOMNAME_MULTIVIDEOCALL"]
14 | #define MY_PEER_ID @"myPeerId"
15 |
16 | @interface MultiVideoCallViewController ()
17 | // IBOutlets
18 | @property (weak, nonatomic) IBOutlet UIView *localVideoContainerView;
19 | @property (weak, nonatomic) IBOutlet UIView *firstPeerVideoContainerView;
20 | @property (weak, nonatomic) IBOutlet UIView *secondPeerVideoContainerView;
21 | @property (weak, nonatomic) IBOutlet UIView *thirdPeerVideoContainerView;
22 | @property (weak, nonatomic) IBOutlet UILabel *firstPeerLabel;
23 | @property (weak, nonatomic) IBOutlet UILabel *secondPeerLabel;
24 | @property (weak, nonatomic) IBOutlet UILabel *thirdPeerLabel;
25 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
26 | @property (weak, nonatomic) IBOutlet UIButton *lockButton;
27 | @property (weak, nonatomic) IBOutlet UISegmentedControl *videoAspectSegmentControl;
28 | @property (weak, nonatomic) IBOutlet UIPickerView *pickerView;
29 | @property (weak, nonatomic) IBOutlet UIView *pickerViewContainer;
30 | @property (weak, nonatomic) IBOutlet UIViewController *partipationsVC;
31 | @property (weak, nonatomic) IBOutlet UIButton *restartButton;
32 | @property (weak, nonatomic) IBOutlet UIButton *toggleCameraButton;
33 | // Other properties
34 | //@property (strong, nonatomic) NSMutableArray *peerIds;
35 | //@property (strong, nonatomic) NSMutableDictionary *peersInfos;
36 | //@property (nonatomic, copy) NSString *cameraMediaID;
37 | //@property (nonatomic, copy) NSString *audioMediaID;
38 | //@property (nonatomic, strong) NSMutableArray *peerObjects;
39 | @property (nonatomic, strong) IBOutletCollection(UIView) NSArray *videoContainers;
40 | @end
41 |
42 | @implementation MultiVideoCallViewController {
43 | BOOL isRoomLocked;
44 | BOOL isRecording;
45 | BOOL isLocalCameraRunning;
46 | NSMutableArray *_peers;
47 | }
48 | #pragma mark - Init
49 |
50 | - (void)viewDidLoad {
51 | [super viewDidLoad];
52 | MyLog(@"SKYLINKConnection version = %@", [SKYLINKConnection getSkylinkVersion]);
53 | self.title = @"Multi Party Video Call";
54 | [self setupData];
55 | }
56 |
57 | - (void)setupData{
58 | //creating SKYLINKConnectionConfig
59 | SKYLINKConnectionConfig *config = [SKYLINKConnectionConfig new];
60 | [config setAudioVideoReceiveConfig:AudioVideoConfig_AUDIO_AND_VIDEO];
61 | [config setAudioVideoSendConfig:AudioVideoConfig_AUDIO_AND_VIDEO];
62 | config.isMultiTrackCreateEnable = YES;
63 | config.isMirrorLocalFrontCameraView = YES;
64 | //creating SKYLINKConnection
65 | _skylinkConnection = [[SKYLINKConnection alloc] initWithConfig:config callback:nil];
66 | _skylinkConnection.lifeCycleDelegate = self;
67 | _skylinkConnection.mediaDelegate = self;
68 | _skylinkConnection.remotePeerDelegate = self;
69 | _skylinkConnection.enableLogs = YES;
70 | //init variables
71 | _peers = [NSMutableArray new];
72 |
73 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
74 | [self startLocalMediaDevice:SKYLINKMediaDeviceCameraFront];
75 | [self startLocalMediaDevice:SKYLINKMediaDeviceMicrophone];
76 | });
77 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
78 | [self joinRoom];
79 | });
80 | }
81 |
82 | #pragma mark - SKYLINKConnectionLifeCycleDelegate
83 | - (void)connectionDidConnectToRoomSuccessful:(SKYLINKConnection *)connection{
84 | [_videoContainers.firstObject setAlpha:1.0];
85 | [self.activityIndicator stopAnimating];
86 | }
87 | - (void)connection:(SKYLINKConnection *)connection didConnectToRoomFailed:(NSString *)errorMessage{
88 | showAlert(@"Connection failed!", errorMessage);
89 | }
90 | - (void)connection:(SKYLINKConnection *)connection didDisconnectFromRoomWithSkylinkEvent:(NSDictionary *)skylinkEvent contextDescription:(NSString *)contextDescription{
91 | [self.activityIndicator stopAnimating];
92 | }
93 |
94 | #pragma mark - SKYLINKConnectionMediaDelegate
95 | - (void)connection:(SKYLINKConnection *)connection didCreateLocalMedia:(SKYLINKMedia *)localMedia{
96 | NSLog(@"didCreateLocalMedia");
97 | //add media which is video only
98 | if (!localMedia) {
99 | return;
100 | }
101 | if (localMedia.skylinkMediaType != SKYLINKMediaTypeAudio) {
102 | [self addMedia:localMedia peerId:MY_PEER_ID];
103 | [self reloadVideoView];
104 | }
105 | }
106 | - (void)connection:(SKYLINKConnection *)connection didChangeLocalMedia:(SKYLINKMedia *)localMedia{
107 | NSLog(@"changed local media %d", localMedia.skylinkMediaState);
108 | [self.activityIndicator stopAnimating];
109 | }
110 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemoteMedia:(SKYLINKMedia *)remoteMedia remotePeerId:(NSString *)remotePeerId{
111 | NSLog(@"didReceiveRemoteMedia: %u", remoteMedia.skylinkMediaType);
112 | if (!remoteMedia || !remotePeerId) {
113 | return;
114 | }
115 | //add media which is video only
116 | if (remoteMedia.skylinkMediaType != SKYLINKMediaTypeAudio) {
117 | [self addMedia:remoteMedia peerId:remotePeerId];
118 | [self reloadVideoView];
119 | }
120 | }
121 | - (void)connection:(SKYLINKConnection *)connection didChangeRemoteMedia:(SKYLINKMedia *)remoteMedia remotePeerId:(NSString *)remotePeerId{
122 | NSLog(@"didChangeRemoteMedia: %u", remoteMedia.skylinkMediaType);
123 | }
124 | - (void)connection:(SKYLINKConnection *)connection didChangeVideoSize:(CGSize)videoSize videoView:(UIView *)videoView peerId:(NSString *)peerId{
125 | NSLog(@"changed video size!");
126 | NSLog(@"peerId: %@", peerId);
127 | if (videoSize.height > 0 && videoSize.width > 0) {
128 | for (UIView* container in self.videoContainers) {
129 | if ([videoView isDescendantOfView:container]) {
130 | NSInteger _index = [self.videoContainers indexOfObject:container];
131 | if (_index==0) {
132 | break;
133 | }
134 | _peers[_index].videoSize = videoSize;
135 | [videoView aspectFitRectForSize:videoSize container:container];
136 | }
137 | }
138 | }
139 | }
140 | #pragma mark - SKYLINKConnectionRemotePeerDelegate
141 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemotePeerInRoomWithRemotePeerId:(NSString *)remotePeerId userInfo:(id)userInfo{
142 | NSLog(@"didReceiveRemotePeerInRoomWithRemotePeerId: %@ \nuserInfo: %@", remotePeerId, userInfo);
143 | showAlertAutoDismiss(nil, [NSString stringWithFormat:@"%@ has joined room\n\nUserData:%@", remotePeerId, userInfo[@"userData"]], 2, self);
144 | if (remotePeerId){
145 | [self addMedia:nil peerId:remotePeerId];
146 | [self.pickerView reloadAllComponents];
147 | }
148 | }
149 | - (void)connection:(SKYLINKConnection *)connection didConnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel{
150 | // if (remotePeerId) {
151 | // showAlertAutoDismiss(nil, [NSString stringWithFormat:@"%@ has joined room\n\nUserData:%@", remotePeerId, userInfo[@"userData"]], 2, self);
152 | // [self addMedia:nil peerId:remotePeerId];
153 | //// [_peers addObject:[[Peer alloc] initWithPeerID:remotePeerId]];
154 | // [self reloadVideoView];
155 | // [self.pickerView reloadAllComponents];
156 | // }
157 | // [self.activityIndicator stopAnimating];
158 | }
159 | - (void)connection:(SKYLINKConnection *)connection didDisconnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel{
160 | if (remotePeerId) {
161 | [self removePeer:remotePeerId];
162 | [self reloadVideoView];
163 | }
164 | [self.activityIndicator stopAnimating];
165 | }
166 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemotePeerLeaveRoom:(NSString *)remotePeerId userInfo:(id)userInfo skylinkInfo:(NSDictionary *)skylinkInfo{
167 |
168 | }
169 | //- (void)connection:(SKYLINKConnection *)connection didChangeRemoteMedia:(SKYLINKMedia *)remoteMedia remotePeerId:(NSString *)remotePeerId{
170 | // if (remoteMedia.skylinkMediaType == SKYLINKMediaTypeVideoCamera) {
171 | // if (remoteMedia.skylinkMediaState == SKYLINKMediaStateUnavailable) {
172 | // <#statements#>
173 | // }
174 | // }
175 | //}
176 |
177 | #pragma mark - UIPickerView Delegate
178 | - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
179 | return 1;
180 | }
181 |
182 | - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
183 | return _peers.count;
184 | }
185 |
186 | - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
187 | if (row == 0) return @"All";
188 | return [NSString stringWithFormat:@"%@:%@", _peers[row].userName, _peers[row].peerId];
189 | }
190 | #pragma mark - Private functions
191 | - (void)removePeer:(NSString*)peerId{
192 | for (Peer *_peer in _peers) {
193 | if ([_peer.peerId isEqualToString:peerId]) {
194 | [_peers removeObject:_peer];
195 | return;
196 | }
197 | }
198 | }
199 | - (void)addMedia:(SKYLINKMedia *)media peerId:(NSString *)peerId{
200 | if (!peerId) {
201 | return;
202 | }
203 | Peer *_peer = nil;
204 | for (Peer *peer in _peers) {
205 | if ([peer.peerId isEqualToString:peerId]) {
206 | _peer = peer;
207 | break;
208 | }
209 | }
210 | if (!_peer) {
211 | _peer = [[Peer alloc] initWithPeerID:peerId];
212 | if ([peerId isEqualToString:MY_PEER_ID]) {
213 | [_peers insertObject:_peer atIndex:0];
214 | }else{
215 | [_peers addObject:_peer];
216 | }
217 | }
218 | if (media) {
219 | [_peer.medias addObject:media];
220 | }
221 | // NSLog(@"ccc===peers count: %lu", (unsigned long)_peers.count);
222 | // for (Peer *_peer in _peers) {
223 | // NSLog(@"peer: %@", _peer.peerId);
224 | // NSLog(@"medias count: %lu", (unsigned long)_peer.medias.count);
225 | // NSLog(@"first media: %d", _peer.medias.firstObject.skylinkMediaType);
226 | // }
227 | }
228 | - (void)reloadVideoView{
229 | //remove all videos
230 | for (UIView *view in _videoContainers) {
231 | [view removeSubviews];
232 | }
233 | //add videos from peers array
234 | NSLog(@"reload video. Peers count: %lu", (unsigned long)_peers.count);
235 | for (Peer *peer in _peers) {
236 | if ([peer.peerId isEqualToString:MY_PEER_ID] && peer.medias.firstObject) {
237 | [self addRenderedVideo:peer.medias.firstObject.skylinkVideoView insideContainer:_videoContainers.firstObject mirror:YES];
238 | }else{
239 | if (peer.medias.firstObject.skylinkMediaState!=SKYLINKMediaStateUnavailable) {
240 | NSInteger _index = MIN([_peers indexOfObject:peer], 3);
241 | NSLog(@"addview index: %ld", (long)_index);
242 | [self addRenderedVideo:peer.medias.firstObject.skylinkVideoView insideContainer:_videoContainers[_index] mirror:NO];
243 | }
244 | }
245 | }
246 | }
247 | - (void)addRenderedVideo:(UIView *)videoView insideContainer:(UIView *)containerView mirror:(BOOL)shouldMirror {
248 | [videoView aspectFitRectForSize:videoView.frame.size container:containerView];
249 | // [containerView addSubview:videoView];
250 | [containerView insertSubview:videoView atIndex:0];
251 | }
252 | - (void)startRecording {
253 | if (!_skylinkConnection.isRecording) {
254 | [_skylinkConnection startRecording:^(NSError * _Nullable error) {
255 | if (error) MyLog(@"%@", error.localizedDescription);
256 | showAlertAutoDismiss(nil, @"You recording is started", 3, self);
257 | }];
258 | }
259 | }
260 |
261 | - (void)stopRecording {
262 | if (_skylinkConnection.isRecording) {
263 | [_skylinkConnection stopRecording:^(NSError * _Nullable error) {
264 | if (error) MyLog(@"%@", error.localizedDescription);
265 | showAlertAutoDismiss(nil, @"You recording is stopped", 3, self);
266 | }];
267 | }
268 | }
269 |
270 | #pragma mark - IBActions
271 | - (IBAction)toogleVideoTap:(UIButton *)sender {
272 | [_skylinkConnection muteVideo:!_skylinkConnection.isVideoMuted];
273 | [sender setImage:[UIImage imageNamed:( (_skylinkConnection.isVideoMuted) ? @"NoVideoFilled.png" : @"VideoCall.png")] forState:UIControlStateNormal];
274 | }
275 | - (IBAction)toogleSoundTap:(UIButton *)sender {
276 | [_skylinkConnection muteAudio:!_skylinkConnection.isAudioMuted];
277 | [sender setImage:[UIImage imageNamed:( (_skylinkConnection.isAudioMuted) ? @"NoMicrophoneFilled.png" : @"Microphone.png")] forState:UIControlStateNormal];
278 | }
279 | - (IBAction)switchCameraTap:(UIButton *)sender {
280 | [_skylinkConnection switchCamera:nil];
281 | }
282 | - (IBAction)lockRoom:(UIButton *)sender {
283 | isRoomLocked ? [_skylinkConnection unlockTheRoom:nil] : [_skylinkConnection lockTheRoom:nil];
284 | isRoomLocked = !isRoomLocked;
285 | [self.lockButton setImage:[UIImage imageNamed:isRoomLocked ? @"LockFilled" : @"Unlock.png"] forState:UIControlStateNormal];
286 | }
287 |
288 | - (IBAction)videoAspectSegmentControlChanged:(UISegmentedControl *)sender {
289 | // [self updatePeersVideosFrames];
290 | }
291 |
292 | - (IBAction)recording:(UISwitch *)sender {
293 | sender.isOn ? [self startRecording] : [self stopRecording];
294 | }
295 |
296 | - (IBAction)restart {
297 | [self.pickerViewContainer setHidden:NO];
298 | }
299 |
300 | - (IBAction)toolbarDone {
301 | [self.pickerViewContainer setHidden:YES];
302 | }
303 |
304 | - (IBAction)toolbarSend {
305 | [self.pickerViewContainer setHidden:YES];
306 | if ([self.pickerView selectedRowInComponent:0] == 0) {
307 | [_skylinkConnection refreshConnectionWithRemotePeerId:nil doIceRestart:YES callback:nil];
308 | }else{
309 | NSString *_selectedPeerId = _peers[[_pickerView selectedRowInComponent:0]].peerId;
310 | [_skylinkConnection refreshConnectionWithRemotePeerId:_selectedPeerId doIceRestart:YES callback:nil];
311 | }
312 | }
313 | @end
314 |
315 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/MessagesViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // MessagesViewController.m
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 04/01/2016.
6 | // Copyright © 2016 Temasys. All rights reserved.
7 | //
8 |
9 | #import "MessagesViewController.h"
10 | #import "Constant.h"
11 | #import "SAMessage.h"
12 |
13 | //#define ROOM_NAME [[NSUserDefaults standardUserDefaults] objectForKey:@"ROOMNAME_MESSAGES"]
14 |
15 |
16 | @interface MessagesViewController ()
17 | // IBOutlets
18 | @property (weak, nonatomic) IBOutlet UITextField *messageTextField;
19 | @property (weak, nonatomic) IBOutlet UITableView *tableView;
20 | @property (weak, nonatomic) IBOutlet UISwitch *isPublicSwitch;
21 | @property (weak, nonatomic) IBOutlet UISegmentedControl *messageTypeSegmentControl;
22 | @property (weak, nonatomic) IBOutlet UITextField *nicknameTextField;
23 | @property (weak, nonatomic) IBOutlet UIButton *peersButton;
24 | @property (weak, nonatomic) IBOutlet UIButton *sendButton;
25 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
26 | @property (weak, nonatomic) IBOutlet UITextField *encryptKeyTextField;
27 | @property (weak, nonatomic) IBOutlet UIView *pickerViewContainer;
28 | @property (weak, nonatomic) IBOutlet UIPickerView *pickerView;
29 | @property (weak, nonatomic) IBOutlet UISwitch *persistSwitch;
30 |
31 | // Properties
32 | @property (strong, nonatomic) NSMutableArray *messages;
33 | @property (strong, nonatomic) NSMutableDictionary *peers;
34 |
35 | @end
36 |
37 |
38 | @implementation MessagesViewController{
39 | NSArray *_encryptSecretIds;
40 | }
41 |
42 | - (void)viewDidLoad {
43 | [super viewDidLoad];
44 | // Do any additional setup after loading the view.
45 |
46 | self.title = @"Messages";
47 | self.messages = [NSMutableArray new];
48 | self.peers = [NSMutableDictionary new];
49 | [self updatePeersButtonTitle];
50 | [self loadStoredMessage];
51 | // Creating configuration
52 | SKYLINKConnectionConfig *config = [SKYLINKConnectionConfig new];
53 | [config setAudioVideoSendConfig:AudioVideoConfig_NO_AUDIO_NO_VIDEO];
54 | [config setAudioVideoReceiveConfig:AudioVideoConfig_NO_AUDIO_NO_VIDEO];
55 | config.hasP2PMessaging = YES;
56 | // Creating SKYLINKConnection
57 | _skylinkConnection = [[SKYLINKConnection alloc] initWithConfig:config callback:nil];
58 | _skylinkConnection.lifeCycleDelegate = self;
59 | _skylinkConnection.messagesDelegate = self;
60 | _skylinkConnection.remotePeerDelegate = self;
61 | _skylinkConnection.encryptSecrets = SAConstants.shared.ENCRYPTION_SECRETS;
62 | // Connecting to a room
63 | [self joinRoom];
64 | _encryptSecretIds = [@[@"No Key"] arrayByAddingObjectsFromArray:[SAConstants.shared.ENCRYPTION_SECRETS.allKeys sortedArrayUsingSelector:@selector(compare:)]];
65 | _encryptKeyTextField.text = _encryptSecretIds.firstObject;
66 | }
67 |
68 | #pragma mark - SKYLINKConnectionLifeCycleDelegate
69 | - (void)connectionDidConnectToRoomSuccessful:(SKYLINKConnection *)connection
70 | {
71 | MyLog(@"Inside %s", __FUNCTION__);
72 | dispatch_async(dispatch_get_main_queue(), ^{
73 | self.messageTextField.enabled = YES;
74 | self.messageTextField.hidden = NO;
75 | self.nicknameTextField.enabled = YES;
76 | self.nicknameTextField.hidden = NO;
77 | self.sendButton.enabled = YES;
78 | self.sendButton.hidden = NO;
79 | [self.messageTextField becomeFirstResponder];
80 | [self.activityIndicator stopAnimating];
81 | });
82 | }
83 |
84 | - (void)connection:(SKYLINKConnection *)connection didConnectToRoomFailed:(NSString *)errorMessage
85 | {
86 | [UIAlertController showAlertWithAutoDisappearTitle:@"Connection failed" message:errorMessage duration:3 onViewController:self];
87 | [self.navigationController popViewControllerAnimated:YES];
88 | }
89 |
90 | - (void)connection:(SKYLINKConnection *)connection didDisconnectFromRoomWithSkylinkEvent:(NSDictionary *)skylinkEvent contextDescription:(NSString *)contextDescription
91 | {
92 | // [UIAlertController showAlertWithAutoDisappearTitle:@"Disconnected" message:contextDescription duration:3 onViewController:self];
93 | // [self.navigationController popViewControllerAnimated:YES];
94 | }
95 |
96 |
97 | #pragma mark - SKYLINKConnectionMessagesDelegate
98 | - (void)connection:(SKYLINKConnection *)connection didReceiveServerMessage:(id)message isPublic:(BOOL)isPublic timeStamp:(long long)timeStamp remotePeerId:(NSString *)remotePeerId{
99 | NSLog(@"SIG message");
100 | if ([message isKindOfClass:[NSString class]]) {
101 | SAMessage *receivedMsg = [[SAMessage alloc] initWithData:message timeStamp:timeStamp sender:[self getUserNameFrom:remotePeerId] target:(isPublic ? nil : _skylinkConnection.localPeerId) type:SAMessageTypeP2P];
102 | [_messages addObject:receivedMsg];
103 | [_tableView reloadData];
104 | }
105 | }
106 | - (void)connection:(SKYLINKConnection *)connection didReceiveP2PMessage:(id)message isPublic:(BOOL)isPublic timeStamp:(long long)timeStamp remotePeerId:(NSString *)remotePeerId{
107 | NSLog(@"P2P message");
108 | if ([message isKindOfClass:[NSString class]]) {
109 | SAMessage *receivedMsg = [[SAMessage alloc] initWithData:message timeStamp:timeStamp sender:[self getUserNameFrom:remotePeerId] target:(isPublic ? nil : _skylinkConnection.localPeerId) type:SAMessageTypeP2P];
110 | [_messages addObject:receivedMsg];
111 | [_tableView reloadData];
112 | }
113 | }
114 |
115 | - (void)connection:(SKYLINKConnection *)connection didReceiveP2PMessage:(id)message isPublic:(BOOL)isPublic remotePeerId:(NSString *)remotePeerId
116 | {
117 | [self.messages insertObject:@{@"message" : message, @"isPublic" : @(isPublic), @"peerId" : remotePeerId, @"type" : @"P2P"} atIndex:0];
118 | [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
119 | }
120 |
121 | #pragma mark - SKYLINKConnectionRemotePeerDelegate
122 | - (void)connection:(SKYLINKConnection *)connection didConnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel
123 | {
124 | NSString *displayNickname = (userInfo && [userInfo isKindOfClass:[NSDictionary class]] && userInfo[@"nickname"]) ? userInfo[@"nickname"] : [NSString stringWithFormat:@"ID: %@", remotePeerId];
125 | [self.peers addEntriesFromDictionary:@{remotePeerId:displayNickname}];
126 | [self updatePeersButtonTitle];
127 | [self.tableView reloadData];
128 | [self.activityIndicator stopAnimating];
129 | }
130 |
131 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemotePeerLeaveRoom:(NSString *)remotePeerId userInfo:(id)userInfo skylinkInfo:(NSDictionary *)skylinkInfo
132 | {
133 | MyLog(@"Peer with ID %@ left with skylinkInfo: %@", remotePeerId, skylinkInfo);
134 | [self.peers removeObjectForKey:remotePeerId];
135 | [self updatePeersButtonTitle];
136 | }
137 | #pragma mark - Table view data source
138 |
139 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
140 | return 1;
141 | }
142 |
143 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
144 | return self.messages.count;
145 | }
146 |
147 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
148 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"messageCell"];
149 |
150 | SAMessage *message = [_messages objectAtIndex: _messages.count - indexPath.row - 1];
151 |
152 | cell.textLabel.text = [NSString stringWithFormat:@"%@~~~%@", [message timeStampString], message.data];
153 | cell.detailTextLabel.text = [NSString stringWithFormat:@"From %@ via %@ • %@", message.sender, message.typeToString, [message isPublicString]];
154 | return cell;
155 | }
156 |
157 | #pragma mark - Table view delegate
158 |
159 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
160 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
161 | SAMessage *message = [self.messages objectAtIndex:_messages.count - indexPath.row - 1];
162 | NSString *messageDetails = [NSString stringWithFormat:@"Message:\n%@\n\nFrom :\n%@\n\n%@", message.data, ([message.sender isEqualToString:USER_NAME] ? @"me" : message.sender), [message isPublicString]];
163 | showAlert(@"Message Detail", messageDetails);
164 | }
165 |
166 | #pragma mark - IBActions
167 |
168 | - (IBAction)sendTap:(UIButton *)sender {
169 | _skylinkConnection.messagePersist = _persistSwitch.isOn;
170 | NSString *message = _messageTextField.text;
171 | if (_peers.count<=0) {
172 | showAlert(@"No peer connected", @"\nYou can't define a private recipient since there is no peer connected.");
173 | return;
174 | }
175 |
176 | if (![message isNotEmpty]) {
177 | showAlert(@"Empty Message", @"\nType the message to be sent.");
178 | return;
179 | }
180 |
181 | if (_isPublicSwitch.isOn) {
182 | //send public
183 | [self sendMessage:message forPeerId:nil];
184 | }else{
185 | //send private
186 | UIAlertController *_alert = [UIAlertController alertControllerWithTitle:@"Choose a private recipient." message:@"\nYou're about to send a private message\nWho do you want to send it to ?" preferredStyle:UIAlertControllerStyleAlert];
187 | UIAlertAction *_cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:nil];
188 | __weak __typeof(self)weakSelf = self;
189 | for (NSString *peerDicKey in _peers.allKeys) {
190 | UIAlertAction *_peerAction = [UIAlertAction actionWithTitle:_peers[peerDicKey] style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
191 | [weakSelf sendMessage:message forPeerId:peerDicKey];
192 | // [weakSelf alert:self->_peers[peerDicKey] message:message];
193 | }];
194 | [_alert addAction:_peerAction];
195 | }
196 | [_alert addAction:_cancelAction];
197 | [self presentViewController:_alert animated:YES completion:nil];
198 | }
199 | }
200 |
201 | - (IBAction)dismissKeyboardTap:(UIButton *)sender {
202 | [self hideKeyboardIfNeeded];
203 | }
204 |
205 | - (IBAction)peersTap:(UIButton *)sender {
206 | [UIAlertController showAlertWithAutoDisappearTitle:sender.titleLabel.text message:[self.peers description] duration:3 onViewController:self];
207 | }
208 | - (IBAction)doneEncryptSecret:(UIButton *)sender{
209 | [self.pickerViewContainer setHidden:YES];
210 | }
211 |
212 |
213 | #pragma mark - Utils
214 | -(void)alert:(NSString *)title message:(NSString*)message{
215 | showAlert(title, message);
216 | [self sendMessage:message forPeerId:[title stringByReplacingOccurrencesOfString:@"ID: " withString:@""]];
217 | }
218 |
219 | - (void)sendMessage:(NSString *)message forPeerId:(NSString *)peerId { // nil peerId means public message
220 | void (^processResponse)(NSError *, SAMessageType) = ^(NSError *error, SAMessageType type){
221 | if (error) {
222 | showAlert([NSString stringWithFormat:@"ERROR: %ld", (long)error.code], error.localizedDescription);
223 | }else{
224 | SAMessage *msg = [[SAMessage alloc] initWithData:message timeStamp:[[NSDate date] toTimeStamp] sender:USER_NAME target:peerId type:type];
225 | [self->_messages addObject:msg];
226 | self.messageTextField.text = @"";
227 | [self.tableView reloadData];
228 | showAlert(message, peerId ? peerId : @"All");
229 | }
230 | };
231 | if (_messageTypeSegmentControl.selectedSegmentIndex) {
232 | [_skylinkConnection sendServerMessage:message toRemotePeerId:peerId callback:^(NSError * _Nullable error) {
233 | processResponse(error, SAMessageTypeSignaling);
234 | }];
235 | }else{
236 | [_skylinkConnection sendP2PMessage:message toRemotePeerId:peerId callback:^(NSError * _Nullable error) {
237 | processResponse(error, SAMessageTypeP2P);
238 | }];
239 | }
240 |
241 | }
242 |
243 | - (void)updateNickname {
244 | if (self.nicknameTextField.text.length > 0) [_skylinkConnection sendLocalUserData:@{@"nickname" : self.nicknameTextField.text} callback:^(NSError * _Nullable error) {
245 | if (error) [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
246 | }];
247 | else [UIAlertController showAlertWithAutoDisappearTitle:@"Empty nickname" message:@"\nType the nickname to set." duration:3 onViewController:self];
248 | }
249 |
250 | - (void)updatePeersButtonTitle {
251 | NSUInteger peersCount = self.peers.count;
252 | if (peersCount == 0) [self.peersButton setTitle:@"No peer" forState:UIControlStateNormal];
253 | else [self.peersButton setTitle:[NSString stringWithFormat:@"%lu peer%@", (unsigned long)peersCount, (peersCount > 1) ? @"s" : @""] forState:UIControlStateNormal];
254 | }
255 |
256 | - (void)hideKeyboardIfNeeded {
257 | [self.messageTextField resignFirstResponder];
258 | [self.nicknameTextField resignFirstResponder];
259 | [self.encryptKeyTextField resignFirstResponder];
260 | }
261 | - (void)loadStoredMessage{
262 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
263 | [self->_skylinkConnection getStoredMessages:^(NSArray * _Nullable storedMessages, NSDictionary * _Nullable errors) {
264 | if (!self.view.window) {
265 | return;
266 | }
267 | if (errors) {
268 | showAlert(@"Error map", errors.description);
269 | }
270 | for (NSDictionary *item in storedMessages) {
271 | if ([item isKindOfClass:[NSDictionary class]]) {
272 | SAMessage *message = [[SAMessage alloc] initWithData:item[@"data"] timeStamp:[item[@"timeStamp"] longLongValue] sender:[self getUserNameFrom:item[@"peerId"]] target:nil type:SAMessageTypeSignaling];
273 | [self.messages addObject:message];
274 | }
275 | }
276 | [self.tableView reloadData];
277 | }];
278 | });
279 | }
280 | - (NSString *)getUserNameFrom:(NSString *)peerId{
281 | NSDictionary *userInfo = [_skylinkConnection getUserInfo:peerId];
282 | if (userInfo) {
283 | return userInfo[@"userData"];
284 | }
285 | return peerId;
286 | }
287 |
288 | #pragma mark - UITextField delegate
289 |
290 | - (BOOL)textFieldShouldReturn:(UITextField *)textField {
291 | if ([textField isEqual:self.nicknameTextField]) [self updateNickname];
292 | [self hideKeyboardIfNeeded];
293 | return YES;
294 | }
295 | - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField{
296 | if (textField == self.encryptKeyTextField) {
297 | [self hideKeyboardIfNeeded];
298 | [_pickerViewContainer setHidden:NO];
299 | return NO;
300 | }
301 | return YES;
302 | }
303 | #pragma mark - PICKER VIEW
304 | - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
305 | return 1;
306 | }
307 | - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
308 | return _encryptSecretIds.count;
309 | }
310 |
311 | - (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
312 | return _encryptSecretIds[row];
313 | }
314 |
315 | - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
316 | NSString *selectedEncryptSecret = (row == 0) ? nil : _encryptSecretIds[row];
317 | _skylinkConnection.selectedSecretId = selectedEncryptSecret;
318 | _encryptKeyTextField.text = _encryptSecretIds[row];
319 | }
320 | @end
321 |
322 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/FileTransferViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // FileTransferViewController.m
3 | // Skylink_Examples
4 | //
5 | // Created by Temasys on 18/12/2015.
6 | // Copyright © 2015 Temasys. All rights reserved.
7 | //
8 |
9 | #import "FileTransferViewController.h"
10 | #import
11 | #import
12 | #import
13 | #import
14 | #import "Constant.h"
15 |
16 | //#define ROOM_NAME [[NSUserDefaults standardUserDefaults] objectForKey:@"ROOMNAME_FILETRANSFER"]
17 |
18 |
19 | @interface FileTransferViewController ()
20 | // IBOutlets
21 | @property (weak, nonatomic) IBOutlet UITableView *peersTableView;
22 | @property (weak, nonatomic) IBOutlet UITableView *fileTransferTableView;
23 | @property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
24 |
25 |
26 | // Other properties
27 | @property (strong, nonatomic) NSMutableArray *remotePeerArray; // array holding the ids (strings) of the peers connected to the room
28 | @property (strong, nonatomic) NSMutableArray *transfersArray; // array of dictionnaries holding infos about started (and finished) file transfers
29 |
30 | @property (strong, nonatomic) AVAudioPlayer *musicPlayer;
31 | @property (assign, nonatomic) NSNumber *selectedRow;
32 |
33 | @property (strong, nonatomic) UIImagePickerController *pickerController;
34 | @end
35 |
36 |
37 |
38 | @implementation FileTransferViewController
39 |
40 | - (void)viewDidLoad {
41 | [super viewDidLoad];
42 | // Do any additional setup after loading the view.
43 |
44 | self.title = @"File Transfer";
45 | self.remotePeerArray = [[NSMutableArray alloc] init];
46 | self.transfersArray = [[NSMutableArray alloc] init];
47 |
48 | SKYLINKConnectionConfig *config = [SKYLINKConnectionConfig new];
49 | [config setAudioVideoSendConfig:AudioVideoConfig_NO_AUDIO_NO_VIDEO];
50 | [config setAudioVideoReceiveConfig:AudioVideoConfig_NO_AUDIO_NO_VIDEO];
51 | config.hasFileTransfer = YES;
52 | [config setTimeout:30 skylinkAction:SkylinkAction_FILE_SEND_REQUEST];
53 | // Creating SKYLINKConnection
54 | _skylinkConnection = [[SKYLINKConnection alloc] initWithConfig:config callback:nil];
55 | _skylinkConnection.lifeCycleDelegate = self;
56 | _skylinkConnection.fileTransferDelegate = self;
57 | _skylinkConnection.remotePeerDelegate = self;
58 | // Connecting to a room
59 | [self joinRoom];
60 | }
61 |
62 |
63 | // Table View
64 | #pragma mark - Table view data source
65 |
66 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
67 | NSString *title;
68 | if ([tableView isEqual:self.peersTableView]) {
69 | if (section == 0) title = (self.remotePeerArray.count > 0) ? @"Or select a connected peer recipient:" : @"No peer connected yet";
70 | } else if ([tableView isEqual:self.fileTransferTableView]) title = [NSString stringWithFormat:@"File transfers (%lu)", (unsigned long)self.transfersArray.count];
71 | return title;
72 | }
73 |
74 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
75 | return 1;
76 | }
77 |
78 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
79 | NSInteger rowCount;
80 | if ([tableView isEqual:self.peersTableView]) rowCount = self.remotePeerArray.count;
81 | else rowCount = self.transfersArray.count;
82 | return rowCount;
83 | }
84 |
85 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
86 | UITableViewCell *cell;
87 | if ([tableView isEqual:self.peersTableView]) {
88 | cell = [tableView dequeueReusableCellWithIdentifier:@"peerCell"];
89 | cell.textLabel.text = [NSString stringWithFormat:@"Peer %ld, ID: %@", (long)indexPath.row + 1, [self.remotePeerArray objectAtIndex:indexPath.row]];
90 | } else if ([tableView isEqual:self.fileTransferTableView]) {
91 | cell = [tableView dequeueReusableCellWithIdentifier:@"fileTransferCell"];
92 | NSDictionary *trInfos = [self.transfersArray objectAtIndex:indexPath.row];
93 | cell.textLabel.text = [NSString stringWithFormat:@"%@ %.0f%% • %@", [trInfos[@"isOutgoing"] boolValue] ? @"⬆️" : @"⬇️", ([trInfos[@"percentage"] floatValue] * 100), trInfos[@"state"]];
94 | cell.detailTextLabel.text = [NSString stringWithFormat:@"File: %@ • Peer: %@", trInfos[@"filename"], trInfos[@"peerId"]];
95 | }
96 | return cell;
97 | }
98 |
99 | #pragma mark Table view delegate
100 |
101 | - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section {
102 | if ([tableView isEqual:self.peersTableView] && [view isKindOfClass:[UITableViewHeaderFooterView class]]) ((UITableViewHeaderFooterView *)view).textLabel.textColor = [UIColor lightGrayColor];
103 | }
104 |
105 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
106 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
107 | if ([tableView isEqual:self.peersTableView]) {
108 | self.selectedRow = [NSNumber numberWithInteger:indexPath.row];
109 | [self showTransferFormForRecipient:self.remotePeerArray[indexPath.row]];
110 | } else if ([tableView isEqual:self.fileTransferTableView]) {
111 | NSDictionary *transferInfos = self.transfersArray[indexPath.row];
112 | if ([transferInfos[@"state"] isEqualToString:@"In progress"]) { // then ask confirmation for transfer drop
113 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Cancel file transfer ?" message:[NSString stringWithFormat:@"\nCancel file transfer for filename:\n'%@'\npeer ID:\n%@", transferInfos[@"filename"], transferInfos[@"peerId"]] preferredStyle:UIAlertControllerStyleAlert];
114 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Drop transfer" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
115 | // cancel transfer if not finished
116 | if ([self.transfersArray[indexPath.row][@"state"] isEqualToString:@"In progress"]) { // because transfer could be completed after alert showed up
117 | [_skylinkConnection cancelFileTransferWithRemotePeerId:transferInfos[@"peerId"] forSending:NO callback:^(NSError * _Nullable error) {
118 | }];
119 | [self updateFileTranferInfosForFilename:transferInfos[@"filename"] peerId:transferInfos[@"peerId"] withState:@"Cancelled" progress:[transferInfos[@"progress"] floatValue] isOutgoing:[transferInfos[@"isOutgoing"] boolValue]];
120 | } else [UIAlertController showAlertWithAutoDisappearTitle:@"Can not cancel" message:@"Transfer already completed" duration:3 onViewController:self];
121 | }];
122 | UIAlertAction *continueAction = [UIAlertAction actionWithTitle:@"Continue transfer" style:UIAlertActionStyleCancel handler:nil];
123 | [alertController addAction:cancelAction];
124 | [alertController addAction:continueAction];
125 | [self presentViewController:alertController animated:YES completion:^{
126 | }];
127 | } else [UIAlertController showAlertWithAutoDisappearTitle:@"Transfer details" message:transferInfos.description duration:3 onViewController:self];
128 | }
129 | }
130 |
131 | #pragma mark - UIImagePickerControllerDelegate
132 |
133 | - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
134 | NSString *peerId = nil;
135 | if (self.selectedRow && [self.selectedRow intValue] < self.remotePeerArray.count) peerId = self.remotePeerArray[[self.selectedRow intValue]];
136 | [self startFileTransfer:peerId url:info[UIImagePickerControllerReferenceURL] type:SKYLINKAssetTypePhoto];
137 | [picker dismissViewControllerAnimated:YES completion:nil];
138 | }
139 |
140 | #pragma mark - MPMediaPickerControllerDelegate
141 |
142 | - (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection {
143 | [mediaPicker dismissViewControllerAnimated:YES completion:nil];
144 | NSString *peerId = nil;
145 | if (self.selectedRow && [self.selectedRow intValue] < self.remotePeerArray.count) peerId = self.remotePeerArray[[self.selectedRow intValue]];
146 | [self startFileTransfer:peerId url:[mediaItemCollection.representativeItem valueForProperty:MPMediaItemPropertyAssetURL] type:SKYLINKAssetTypeMusic];
147 | }
148 |
149 | - (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker {
150 | [mediaPicker dismissViewControllerAnimated:YES completion:nil];
151 | }
152 |
153 |
154 | // SKYLINK Delegate methods implementations
155 | #pragma mark - SKYLINKConnectionLifeCycleDelegate
156 | - (void)connectionDidConnectToRoomSuccessful:(SKYLINKConnection *)connection
157 | {
158 | MyLog(@"Connection success :D");
159 | [self.activityIndicator stopAnimating];
160 | }
161 |
162 | - (void)connection:(SKYLINKConnection *)connection didConnectToRoomFailed:(NSString *)errorMessage
163 | {
164 | [UIAlertController showAlertWithAutoDisappearTitle:@"Connection failed" message:errorMessage duration:3 onViewController:self];
165 | [self.navigationController popViewControllerAnimated:YES];
166 | }
167 |
168 | - (void)connection:(SKYLINKConnection *)connection didDisconnectFromRoomWithSkylinkEvent:(NSDictionary *)skylinkEvent contextDescription:(NSString *)contextDescription
169 | {
170 | // [UIAlertController showAlertWithAutoDisappearTitle:@"Disconnected" message:contextDescription duration:3 onViewController:self];
171 | // [self.navigationController popViewControllerAnimated:YES];
172 | }
173 | #pragma mark SKYLINKConnectionRemotePeerDelegate
174 | - (void)connection:(SKYLINKConnection *)connection didConnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel
175 | {
176 | MyLog(@"Peer with id %@ joigned the room, properties: %@", remotePeerId, userInfo);
177 | [self.remotePeerArray addObject:remotePeerId];
178 | [self.peersTableView reloadData];
179 | }
180 |
181 | - (void)connection:(SKYLINKConnection *)connection didDisconnectWithRemotePeer:(NSString *)remotePeerId userInfo:(id)userInfo hasDataChannel:(BOOL)hasDataChannel
182 | {
183 | MyLog(@"Peer with id %@ left the room with message: %@", remotePeerId, userInfo);
184 | [self.remotePeerArray removeObject:remotePeerId];
185 | [self.peersTableView reloadData];
186 | }
187 |
188 | - (void)connection:(SKYLINKConnection *)connection didReceiveRemotePeerUserData:(id)userData remotePeerId:(NSString *)remotePeerId
189 | {
190 | MyLog(@"Peer with id %@ left the room with message: %@", remotePeerId, userData);
191 | [self.remotePeerArray removeObject:remotePeerId];
192 | [self.peersTableView reloadData];
193 | }
194 |
195 | - (void)connection:(SKYLINKConnection *)connection didErrorForRemotePeerConnection:(NSError *)error remotePeerId:(NSString *)remotePeerId
196 | {
197 | MyLog(@"Peer with id %@ left the room with message: %@", remotePeerId, error);
198 | [self.remotePeerArray removeObject:remotePeerId];
199 | [self.peersTableView reloadData];
200 | }
201 |
202 |
203 | #pragma mark SKYLINKConnectionFileTransferDelegate
204 | - (void)connection:(SKYLINKConnection *)connection didReceiveFileTransferRequest:(NSString *)fileName isPublic:(BOOL)isPublic remotePeerId:(NSString *)remotePeerId
205 | {
206 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Accept file transfer ?" message:[NSString stringWithFormat:@"\nA user wants to send you a file named:\n'%@'", fileName] preferredStyle:UIAlertControllerStyleAlert];
207 | UIAlertAction *declineAction = [UIAlertAction actionWithTitle:@"Decline" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
208 | [_skylinkConnection rejectFileTransferFromRemotePeerId:remotePeerId callback:^(NSError * _Nullable error) {
209 | if (error) [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
210 | }];
211 | }];
212 | UIAlertAction *acceptAction = [UIAlertAction actionWithTitle:@"Accept" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
213 | [_skylinkConnection acceptFileTransferWithFileName:fileName fromRemotePeerId:remotePeerId callback:^(NSError * _Nullable error) {
214 | if (error) [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
215 | }];
216 | }];
217 | [alertController addAction:declineAction];
218 | [alertController addAction:acceptAction];
219 | [self presentViewController:alertController animated:YES completion:^{
220 | }];
221 | }
222 |
223 | - (void)connection:(SKYLINKConnection *)connection didReceiveFileTransferResponse:(BOOL)wasAccepted fileName:(NSString *)fileName remotePeerId:(NSString *)remotePeerId
224 | {
225 | if (!wasAccepted) [UIAlertController showAlertWithAutoDisappearTitle:@"File refused" message:[NSString stringWithFormat:@"The peer user has refused your '%@' file sending request", fileName] duration:3 onViewController:self];
226 | }
227 |
228 | - (void)connection:(SKYLINKConnection *)connection didUpdateFileTransferSendingProgress:(CGFloat)percentage fileName:(NSString *)fileName remotePeerId:(NSString *)remotePeerId
229 | {
230 | [self updateFileTranferInfosForFilename:fileName peerId:((remotePeerId) ? remotePeerId : @"all") withState:@"In progress" progress:percentage isOutgoing:YES];
231 | }
232 |
233 | - (void)connection:(SKYLINKConnection *)connection didUpdateFileTransferReceivingProgress:(CGFloat)percentage fileName:(NSString *)fileName remotePeerId:(NSString *)remotePeerId
234 | {
235 | [self updateFileTranferInfosForFilename:fileName peerId:((remotePeerId) ? remotePeerId : @"all") withState:@"In progress" progress:percentage isOutgoing:NO];
236 | }
237 |
238 | - (void)connection:(SKYLINKConnection *)connection didDropFileTransfer:(NSString *)fileName message:(NSString *)message isExplicit:(BOOL)isExplicit remotePeerId:(NSString *)remotePeerId
239 | {
240 | [self updateFileTranferInfosForFilename:fileName peerId:((remotePeerId) ? remotePeerId : @"all") withState:((message.length) ? message : @"Dropped by sender") progress:0 isOutgoing:isExplicit];
241 | }
242 |
243 | - (void)connection:(SKYLINKConnection *)connection didCompleteFileTransferReceiving:(NSString *)fileName fileData:(NSData *)fileData fileSavePath:(NSString *)fileSavePath remotePeerId:(NSString *)remotePeerId
244 | {
245 | [self updateFileTranferInfosForFilename:fileName peerId:((remotePeerId) ? remotePeerId : @"all") withState:@"Completed ✓" progress:1 isOutgoing:NO];
246 | if (fileData) {
247 | NSString *fileExtension = [[fileName componentsSeparatedByString:@"."] lastObject];
248 | fileName = [fileName stringByReplacingOccurrencesOfString:@" " withString:@"_"];
249 | if ([self isImage:fileExtension] && [UIImage imageWithData:fileData]) {
250 | UIImageWriteToSavedPhotosAlbum([UIImage imageWithData:fileData], self, @selector(image:didFinishSavingWithError:contextInfo:), (__bridge void *)(fileName));
251 | } else if ([fileExtension isEqualToString:@"mp3"] || [fileExtension isEqualToString:@"m4a"]) {
252 | NSError *pError;
253 | self.musicPlayer = [fileExtension isEqualToString:@"mp3"] ? [[AVAudioPlayer alloc] initWithData:fileData fileTypeHint:AVFileTypeMPEGLayer3 error:&pError] : [[AVAudioPlayer alloc] initWithData:fileData error:&pError];
254 | if (!pError) [self.musicPlayer play];
255 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Music transfer completed" message:[NSString stringWithFormat:@"File transfer success.\nPEER: %@\n\nPlaying the received music file:\n'%@'", remotePeerId, fileName] preferredStyle:UIAlertControllerStyleAlert];
256 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Stop playing" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
257 | [self.musicPlayer stop];
258 | self.musicPlayer = nil;
259 | }];
260 | [alertController addAction:cancelAction];
261 | [self presentViewController:alertController animated:YES completion:^{
262 | }];
263 | } else {
264 | NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
265 | NSString *filePath = [[pathArray firstObject] stringByAppendingPathComponent:fileName];
266 | if ([[NSFileManager defaultManager] fileExistsAtPath:filePath] && ![self removeFileAtPath:filePath]) return;
267 | NSError *wError;
268 | [fileData writeToFile:filePath options:NSDataWritingAtomic error:&wError];
269 | if (wError) {
270 | MyLog(@"%s • Error while writing '%@'->%@", __FUNCTION__, filePath, wError.localizedDescription);
271 | } else {
272 | MyLog(@"File saved at %@", filePath);
273 | if ([self isMovie:fileExtension] && UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(filePath)) {
274 | [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
275 | PHAssetChangeRequest *createAssetRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:[NSURL URLWithString:filePath]];
276 | NSParameterAssert(createAssetRequest);
277 | } completionHandler:^(BOOL success, NSError * _Nullable error) {
278 | if (error) MyLog(@"%s • Error while saving '%@'->%@", __FUNCTION__, fileName, error.localizedDescription);
279 | else [self removeFileAtPath:filePath];
280 | }];
281 | }
282 | }
283 | }
284 | }
285 | }
286 |
287 | #pragma mark - other methods
288 |
289 | - (void)updateFileTranferInfosForFilename:(NSString *)filename peerId:(NSString *)peerId withState:(NSString *)state progress:(CGFloat)percentage isOutgoing:(BOOL)isOutgoing {
290 | NSInteger indexOfTransfer = [self.transfersArray indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
291 | return ([((NSDictionary *)obj)[@"filename"] isEqualToString:filename] && [((NSDictionary *)obj)[@"peerId"] isEqualToString:peerId]);
292 | }];
293 | if (indexOfTransfer == NSNotFound) { // new transfer
294 | [self.transfersArray insertObject:@{@"filename" : (filename) ? filename : @"none", @"peerId" : (peerId) ? peerId : @"No peer ID", @"isOutgoing" : @(isOutgoing), @"percentage" : @(percentage), @"state" : (state) ? state : @"Undefined"} atIndex:0];
295 | } else { // updated transfer
296 | NSMutableDictionary *transferInfos = [NSMutableDictionary dictionaryWithDictionary:self.transfersArray[indexOfTransfer]];
297 | if (filename) [transferInfos setObject:filename forKey:@"filename"];
298 | if (peerId) [transferInfos setObject:peerId forKey:@"peerId"];
299 | if (isOutgoing) [transferInfos setObject:@(isOutgoing) forKey:@"isOutgoing"];
300 | if (percentage) [transferInfos setObject:@(percentage) forKey:@"percentage"];
301 | if (state) [transferInfos setObject:state forKey:@"state"];
302 | [self.transfersArray replaceObjectAtIndex:indexOfTransfer withObject:transferInfos];
303 | }
304 | __weak __typeof(self)weakSelf = self;
305 | dispatch_async(dispatch_get_main_queue(), ^{
306 | __strong __typeof(weakSelf)strongSelf = weakSelf;
307 | [strongSelf.fileTransferTableView reloadData];
308 | });
309 | }
310 |
311 | - (void)startFileTransfer:(NSString *)userId url:(NSURL *)fileURL type:(SKYLINKAssetType)transferType {
312 |
313 | if (userId && fileURL) {
314 | @try {
315 | [_skylinkConnection sendFileTransferWithFileURL:fileURL assetType:transferType fileName:nil remotePeerId:userId callback:^(NSError * _Nullable error) {
316 | if (error) [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:error.localizedDescription duration:3 onViewController:self];
317 | }];
318 | } @catch (NSException *exception) {
319 | [UIAlertController showAlertWithAutoDisappearTitle:@"Error" message:[NSString stringWithFormat:@"%@", exception] duration:3 onViewController:self];
320 | }
321 | } else if (fileURL) {
322 | [_skylinkConnection sendFileTransferWithFileURL:fileURL assetType:transferType fileName:nil remotePeerId:nil callback:^(NSError * _Nullable error) {
323 |
324 | }];
325 | } else {
326 | [UIAlertController showAlertWithAutoDisappearTitle:@"No file URL" message:@"\nError: there is no file URL. Try another media." duration:3 onViewController:self];
327 | }
328 | }
329 |
330 | - (void)showTransferFormForRecipient:(NSString *)peerId {
331 | NSString *message;
332 | if (peerId) message = [NSString stringWithFormat:@"\nYou are about to send a tranfer request to user with ID \n%@\nWhat do you want to send ?", peerId];
333 | else message = @"\nYou are about to send a tranfer request all users\nWhat do you want to send ?";
334 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"Send a file." message:message preferredStyle:UIAlertControllerStyleAlert];
335 | UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
336 | }];
337 | UIAlertAction *acceptPhotoAction = [UIAlertAction actionWithTitle:@"Photo / Video (pick from library)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
338 | if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
339 | self.pickerController = [UIImagePickerController new];
340 | self.pickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
341 | self.pickerController.delegate = self;
342 | self.pickerController.mediaTypes = [[NSArray alloc] initWithObjects: (NSString *) kUTTypeImage, (NSString*) kUTTypeMovie, nil];
343 | [self presentViewController:self.pickerController animated:YES completion:nil];
344 | }
345 | }];
346 | UIAlertAction *acceptMusicAction = [UIAlertAction actionWithTitle:@"Music (pick from library)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
347 | MPMediaPickerController *pickerController = [MPMediaPickerController new];
348 | pickerController.delegate = self;
349 | [self presentViewController:pickerController animated:YES completion:nil];
350 | }];
351 | UIAlertAction *acceptFileAction = [UIAlertAction actionWithTitle:@"File (prepared image)" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
352 | NSString *peerId = nil;
353 | if (self.selectedRow && [self.selectedRow intValue] < self.remotePeerArray.count) peerId = self.remotePeerArray[[self.selectedRow intValue]];
354 | NSString *filePath = [[NSBundle mainBundle] pathForResource:((peerId) ? @"sampleImage_transfer" : @"sampleImage_groupTransfer") ofType:@"png" inDirectory:@"TransferFileSamples"];
355 | [self startFileTransfer:peerId url:[NSURL URLWithString:filePath] type:SKYLINKAssetTypeFile];
356 | }];
357 | [alertController addAction:cancelAction];
358 | [alertController addAction:acceptPhotoAction];
359 | [alertController addAction:acceptMusicAction];
360 | [alertController addAction:acceptFileAction];
361 | [self presentViewController:alertController animated:YES completion:^{
362 | }];
363 | }
364 |
365 | #pragma mark - IBActions
366 |
367 | - (IBAction)sendToAllTap:(UIButton *)sender {
368 | self.selectedRow = nil;
369 | if (self.remotePeerArray.count > 0) [self showTransferFormForRecipient:nil];
370 | else [UIAlertController showAlertWithAutoDisappearTitle:@"No peer connected" message:@"Wait for someone to connect before sending files." duration:3 onViewController:self];
371 | }
372 |
373 |
374 | #pragma mark - Utils
375 |
376 | - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
377 | if (error) {
378 | MyLog(@"%s • Error while saving '%@'->%@", __FUNCTION__, contextInfo, error.localizedDescription);
379 | MyLog(@"%s • Now trying to save image in the Documents Directory", __FUNCTION__);
380 | NSArray *pathArray = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
381 | NSString *filePath = [[pathArray firstObject] stringByAppendingPathComponent:(__bridge NSString *)(contextInfo)];
382 | if ([[NSFileManager defaultManager] fileExistsAtPath:filePath] && ![self removeFileAtPath:filePath]) return;
383 | NSError *wError;
384 | [UIImagePNGRepresentation(image) writeToFile:filePath options:NSDataWritingAtomic error:&wError];
385 | if (wError) MyLog(@"%s • Error while writing '%@'->%@", __FUNCTION__, filePath, wError.localizedDescription);
386 | } else {
387 | MyLog(@"%s • Image saved successfully", __FUNCTION__);
388 | }
389 | }
390 |
391 | - (BOOL)removeFileAtPath:(NSString*)filePath {
392 | BOOL succeed = NO;
393 | NSError *error;
394 | [[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];
395 | if (error) MyLog(@"%s • Error while removing '%@'->%@", __FUNCTION__, filePath, error.localizedDescription);
396 | else succeed = YES;
397 | return succeed;
398 | }
399 |
400 | - (BOOL)isImage:(NSString*)extension {
401 | return [@[@"jpg", @"jpeg", @"jpe", @"jif", @"jfif", @"jfi", @"jp2", @"j2k", @"jpf", @"jpx", @"jpm", @"tiff", @"tif", @"pict", @"pct", @"pic", @"gif", @"png", @"qtif", @"icns", @"bmp", @"bmpf", @"ico", @"cur", @"xbm"] containsObject:[extension lowercaseString]];
402 | }
403 | - (BOOL)isMovie:(NSString*)extension {
404 | return [@[@"mpg", @"mpeg", @"m1v", @"mpv", @"3gp", @"3gpp", @"sdv", @"3g2", @"3gp2", @"m4v", @"mp4", @"mov", @"qt"] containsObject:[extension lowercaseString]];
405 | }
406 |
407 |
408 | @end
409 |
410 |
411 |
--------------------------------------------------------------------------------
/SkylinkSample/SkylinkSample/StatsView.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
29 |
36 |
43 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
73 |
80 |
87 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
117 |
124 |
131 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
161 |
168 |
175 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
--------------------------------------------------------------------------------