Integrations & SDKs

iOS SDK

hotmic offers an ios sdk so you can let users inside your application watch your streams this allows you to drive users to your own application, to retain control over your users, unlike in twitch and facebook, to learn more, we recommend looking at our sample app, which is the fastest way to see the ios sdk in action inside a sample application ios sample app hotmic offers a sample app to see how to use our ios sdk in your application here is a step by step guide to downloading and running the hotmic sdk sample applications for ios requirements you can download the hotmic sdk sample application provided by hotmic https //github com/hotmic wp/hotmic media player ios https //github com/hotmic wp/hotmic media player ios an api key provided by hotmic a jwt token created using a secret provided by hotmic instructions for how to create the token can be found below a white labeled production dashboard where you will produce streams authentication with jwt token for security, when using the sdk your app controls authentication with a jwt token, which you pass to hotmic json web tokens are an open, industry standard rfc 7519 https //tools ietf org/html/rfc7519 method for representing claims securely between two parties jwt io http //jwt io/ you will receive an api secret from hotmic, which you will use to create the token the token will be used to authenticate the user, as well as provide information such as their name, an avatar, a unique id, or another information which is needed by the sdk this is the expected format of the jwt token // nodejs example const jwt = require('jsonwebtoken'); accesstoken = jwt sign({identity { user id user id, // should be consistent for the user (uuid4) display name user display name, // name displayed in chat/profiles profile pic user profile pic, // image url (https //ui avatars com/api/?name=test\&background=0dcad6\&color=fff) }}, \[api secret], { expiresin '7d'}) note, the user id field should be consistent for each invocation of the sdk for that particular user quick steps to generate a jwt token online you can also generate jwt tokens online! this is good for test purposes while getting started, or to use the sample app here is one way to do it we do not recommend using this method with any sensitive data go to https https //jwt io/ and scroll down add a payload like the following into the "payload" section paste your secret into the "verify signature" section this screenshot shows how it looks and here is a sample payload that should work for your application { "identity" { "user id" "3d5567c2 603c 4b08 b917 7e9f05c8ebb5", "display name" "tester1", "profile pic" "https //ui avatars com/api/?name=test\&background=0dcad6\&color=fff", "badge" "https //hotmic content s3 us west 1 amazonaws com/badges/10 badge png?c251fece c08f 48a0 931e 70cfe8ea7ed4" }, "iat" 1657658555, "exp" 1821724014 } then copy the code from the "encoded" section of this page, and use that in the sample app anonymous/restricted users anonymous or restricted users get created in the same way create a jwt for them using any identifier you like your anonymous id should be pseudo random (e g a uuid) to avoid collisions you can pass "user restrictions" "view and polls only" in the identity in order to restrict their access here is an example { "identity" { "user id" "3d5567c2 603c 4b08 b917 7e9f05c8ebb5", "display name" "tester1", "profile pic" "https //ui avatars com/api/?name=test\&background=0dcad6\&color=fff", "badge" "https //hotmic content s3 us west 1 amazonaws com/badges/10 badge png?c251fece c08f 48a0 931e 70cfe8ea7ed4", "user restrictions" "view and polls only" }, "iat" 1657658555, "exp" 1821724014 } ios installation unzip the hotmicmediaplayersample zip file, and open mediaplayersample xcworkspace in xcode open mediaplayersample/appdelegate, and update apikey and accesstoken, inside the call to hmmediaplayer initialize install cocoapods https //cocoapods org/ , and run pod install from the terminal in the mediaplayersample folder choose your target device or simulator in xcode, and press run you should see a few streams populate in the app tap on one of them to load the media player see the installation guide for a more detailed description of the sdk use the production dashboard https //docs hotmic io/knowledge/how do i get started with the hotmic sdk#dashboard to create and start streams you can use the account ios test1\@hotmic io/password to login, or sign up for a new account ios sdk installation here are instructions on how to install the ios sdk if you ever have issues installing the ios sdk, please contact our team the name of our sdk is hotmicmediaplayer hotmicmediaplayer allows you to integrate the hotmic player experience into your app use this framework to create a hmplayerviewcontroller for a specific stream and present it full screen requirements api key and jwt secret will be provided by hotmic ios 12+ ipados user interface is not tailored to the ipad, its just a larger iphone view a future update will expand tablet support macos apps are not supported swift uikit app integration in objective c or swiftui apps is not officially supported app is already live on the app store hotmicmediaplayer uses the uiwebview framework for youtube playback apple does not allow new apps to use uiwebview the reason uiwebview is used is due to the fact that wkwebview does not support all features needed for youtube playback this component can also be removed if youtube playback is not required for your use case app uses view controller based status bar appearance app supports portrait and landscape orientations app disables bitcode because hotmicmediaplayer uses acrcloud which was built without full bitcode support, hotmicmediaplayer must disable bitcode, and your app must as well accessibility accessibility support including dynamic type, voiceover, switch control, etc default system colors provide high contrast variants dynamic type support respects the device text size settings large content viewer is supported for small icon buttons views are marked as accessibility elements appropriately accessibility traits are set to identity headings, buttons, selection state, etc accessibility labels are added to ensure controls are titled accessibility hints are added where additional instruction is helpful app privacy when integrating hotmicmediaplayer into your app, you must disclose the data it and its dependencies collect on the app store name used for product personalization audio data used for app functionality device id used for analytics advertising data used for developer’s advertising or marketing, analytics, linked to the user’s identity other diagnostics data used for app functionality, linked to the user’s identity hotmicmediaplayer has the following dependencies acrcloudsdk bitmovinplayer truetime opentok pubnub fittedsheets kingfisher if your team is already using 1 or more of these dependencies, please contact us so we can ensure to get you the right version of the hotmicmediaplayer sdk that matches your dependencies cocoapods add the following to your podfile, then run pod repo update and then pod install platform \ ios, '12 0' use frameworks! source 'https //cdn cocoapods org/' # default global repository source 'https //github com/bitmovin/cocoapod specs git' target 'yourapptarget' do pod 'hotmicmediaplayer', podspec 'https // /hotmicmediaplayer podspec' end post install do |installer| installer pods project targets each do |target| target build configurations each do |config| config build settings\['build library for distribution'] = 'yes' end end end privacy add the following keys to your target’s info tab nscamerausagedescription this is required to join the room with video nsmicrophoneusagedescription this is required to join the room with audio and sync the stream with a tv capabilities add the following capabilities to your app’s target background modes audio, airplay, and picture in picture in app purchases hotmic supports two types of in app purchases in the player experience tip the host and join the stream you can support these in your app as well you will need to configure these in app store connect, then be sure to submit them to apple for review alongside your app update api key and authorization token you will need an api key for your app to use the hotmic service you will also need to create an authorization token hotmic will provide you with the key and information on how to create the token in the appropriate format initialization initialize hmmediaplayer with your api key and authorization token this must be done before any other functions are called in hmmediaplayer, so we recommend performing initialization in your appdelegate’s didfinishlaunchingwithoptions if needed, it can be called again later, for example if the token changes import hotmicmediaplayer hmmediaplayer initialize(apikey "your key", accesstoken token) apikey a uuid provided by hotmic accesstoken use any jwt library to create a token format is as follows const accesstoken = jwt sign({identity { user id \[consistent id for user], display name user display name, profile pic user profile pic, }}, apisecret, { expiresin '1d'}) get streams streams are returned in reverse chronological order you can fetch streams from hotmic with the following filters that are live, scheduled, and/or video on demand replays userid the userid you want to filter the streams by type string default null (all possible streams) limit the max number of streams to return type integer example 40 default 30 skip the number of results to skip you want to not return in the results, typically used for pagination type integer example 10 default 0 hmmediaplayer getstreams(live true, scheduled true, vod true, userid nil, limit nil, skip nil) { result in switch result { case success(let streams) // display the list of streams case failure(let error) // handle error } } player view controller initialize a hmplayerviewcontroller and present it let playerviewcontroller = hmmediaplayer initializeplayerviewcontroller( streamid streamid, delegate self, supportsminimizingtopip true, prefersvideocontrolshidden false ) present(playerviewcontroller, animated true, completion nil) setting the modalpresentationstyle to a value other than fullscreen is not allowed the player view controller is presented in portrait mode, then after presentation it will support rotating to landscape note that the player may force rotate to portrait in the experience, for example, if a tall sheet needs to be presented player view controller delegate implement the hmplayerviewcontrollerdelegateprotocol to support functionality of the player screen each time a player is needed, the following function is called allowing you to provide a custom player that implements the hmplayerprotocol return nilif you'd like to use the default player func playerviewcontroller( viewcontroller hmplayerviewcontroller, playerfororientation orientation hmplayerorientation) > hmplayer? { return nil } when the player screen will display chat content, the following function is called allowing you to provide a custom chat handler and view controller to display provide nilif you'd like to use the default hotmic chat service and ui note the handler and view controller can be the same instance of your view controller if it conforms to hmchathandler func playerviewcontroller( viewcontroller hmplayerviewcontroller, viewcontrollerforcontext context hmplayerviewcontroller context) > uiviewcontroller? { // provide a custom view controller based on the context return nil } func playerviewcontrollerchathandler( viewcontroller hmplayerviewcontroller) > hmchathandler? { // provide a custom chat handler return nil } when the user is finished with the player experience, for example when they tap a button to close the screen, the following function is called allowing you to dismiss it a pip view may be provided which allows you to place it into a custom picture in picture window, such as one provided by the pipkitlibrary if you’d like to support pip, initialize a player view controller with supportsminimizingtopip true, then store this view controller in a strongly held property to prevent it from deinitializing when you wish to restore the player full screen, provide the player view to move back into the player view controller then present it again be sure to set the view controller property to nilwhen the user wishes to close the player or pip func playerviewcontroller( viewcontroller hmplayerviewcontroller, didfinishwith pipview uiview?) { dismiss(animated true, completion nil) if let pipview { let pipviewcontroller = pipviewcontroller() pipviewcontroller addcontentview(pipview) pipkit show(with pipviewcontroller) } else { if pipkit isactive { pipkit dismiss(animated true) { self playerviewcontroller = nil } } else { playerviewcontroller = nil } } } player view controller functions to support returning to the full screen player experience from picture in picture, call hmplayerviewcontroller’s restorepipview( ) function and then present the player view controller full screen func pipviewcontrollerdidtapfullscreen( viewcontroller pipviewcontroller) { guard let playerviewcontroller else { return } playerviewcontroller restorepipview(viewcontroller contentview) pipkit dismiss(animated true) present(playerviewcontroller, animated true, completion nil) } to show a banner ad in the player screen, call hmplayerviewcontroller’s displaybannerad(withview\ duration\ delay ) function provide any uiview to display the view fills the screen width and needs to have a known height such as an intrinsic content size or an auto layout constraint that defines a constant height or aspect ratio if you do not specify a duration of time to display the ad, it will be displayed until you hide it if you do not specify a delay, it will be displayed immediately if you do not specify an animationduration, a default value will be used if you specify animationduration 0, it will not animate func displayad() { let imageview = uiimageview() imageview\ contentmode = scaleaspectfill imageview\ isuserinteractionenabled = true imageview\ addgesturerecognizer(uitapgesturerecognizer(target self, action #selector(banneradtapped( )))) imageview\ image = uiimage(named "ad") imageview\ translatesautoresizingmaskintoconstraints = false imageview\ heightanchor constraint(equalto imageview\ widthanchor, multiplier 3 0/8 0) isactive = true playerviewcontroller displaybannerad(withview imageview) } to remove the banner ad from the player screen, call hmplayerviewcontroller’s hidebannerad() function if you do not specify an animationduration, a default value will be used if you specify animationduration 0, it will not animate func removead() { playerviewcontroller hidebannerad() } user profile delegate to support user profile functionality, you can implement the hmmediaplayeruserprofiledelegate protocol if you do not implement this delegate, profile info will be obtained by hotmic rather than your app, and buttons such as follow/unfollow will not be available hmmediaplayer userprofiledelegate = self when a user's profile info is to be shown, the following function is called allowing you to provide that user's information provide a success result with an hmuserprofile containing information such as their name, profile pic, followers count, following count, if they're following the current user, and if they're followed by the current user provide nil for any values that are unavailable for example, if followingcount is nil the number of people this user follows will not be shown, and if followedbyme is nil the follow/unfollow button will not be shown or provide a success result with nil if you would like the profile info to be provided by hotmic rather than your app provide a failure result with an error if one occurred func getuserprofile(for id string, restriction string?, ishost bool, iscohost bool, completion @escaping (result\<hmuserprofile?, error>) > void) { // fetch the profile info and call completion} the restriction returned should be null or view and polls only, depending on whether the user is restricted when the user taps the follow/unfollow button, the following function is called allowing you to record the new following state provide an error to the completion handler if one occurs func setfollowinguser(id string, following bool, completion @escaping (error?) > void) { // record the new state and call completion} when the user profile is shown, the following function is called allowing you to specify whether the see full profile button should be shown func shouldshowseefullprofilebutton(for id string) > bool { return true} when the user taps the see full profile button, the following function is called allowing you to handle this action, for example by presenting a view controller func seefullprofilebuttontapped(for id string, in viewcontroller uiviewcontroller) { // show the full profile} example of hmuserprofile hmuserprofile( name "test user", displayname "testuser1", profilepic nil, badge nil, bio "hello and welcome to my user profile ", instagramhandle "testuser1", tiktokhandle "testuser1", twitchhandle "testuser1", twitterhandle "testuser1", youtubehandle "testuser1", followerscount 100, followingcount 50, followingme false, followedbyme true ) in app purchase delegate to support tipping hosts and joining their streams for a price, you can implement the hmmediaplayerinapppurchasedelegate protocol and integrate it with your storekit in app purchase code if you do not implement this delegate, users cannot tip hosts, but can still join the host for free hmmediaplayer inapppurchasedelegate = self when the user opens the tip sheet, the following function will be called to get the skproducts available to purchase for a host id your app should fetch the products that are applicable to this host from the app store func gettipproducts(hostid string, completion @escaping (result<\[skproduct], error>) > void) { // fetch the products and call completion } when the user wishes to purchase a tip, the following function will be called your app should initiate the in app purchase process if the purchase is successful, your app should then submit the tip purchase information including the app store receipt via hmmediaplayer’s submittippurchase function hotmic will verify this purchase is legitimate and record the tip if validated be sure to provide an error if one occurs in this process, such as if the device cannot make payments, a purchase is already in progress, the transaction was canceled, the transaction failed, failed to get transaction info, no purchase info was found, failed to verify, or failed to process the completion handler allows you to specify if you want this error’s localizeddescription to be shown to the user and if a button should be provided to retry submitting their purchase information if that request fails we strongly recommend persisting the purchase information on the device and avoid marking the skpaymenttransaction finished until the purchase has been successfully submitted, as this allows you to retry submitting the information when storekit informs you there is a not yet finished purchased transaction func purchasetip( product skproduct, userid string, hostid string, streamid string, message string?, anonymous bool, completion @escaping ((error error?, showerror bool, canretry bool)) > void) { // purchase the product then call hmmediaplayer submittippurchase to record it, // or provide an error } when the user wishes to retry submitting their purchase information, the following function will be called your app should look up the purchase information with the provided product identifier and submit it via hmmediaplayer’s submittippurchase function func retrysubmittingpurchaseinfo( productid string, completion @escaping ((error error?, showerror bool, canretry bool)) > void) { // call hmmediaplayer submittippurchase to try recording it again } to support join stream in app purchases, very similar functions as those for tips are available and should be used in the same way if you reach out to us we would be happy to provide you with more information and example code from our in app purchase manager that will allow you to implement this the same way we did analytics event observing to be notified of analytics events as they occur throughout the experience, you can implement the hmmediaplayeranalyticseventobserving protocol documentation for event names and info keys and values is not yet available hmmediaplayer analyticsobserver = self func eventstarted(name string) { // start timing this event by name } func eventoccurred(name string, info \[string any]) { // stop timing event by name, maybe record the event } chat handler the hmchathandlerprotocol defines functions you implement if you'd like to provide a custom view controller for chat while utilizing the hotmic chat service your implementation needs to store a reference to an hmchathandlerdelegateand call its functions to get information and inform the delegate when various events occur store the delegate in a weak optional variable to call its functions in the future func setdelegate( delegate hmchathandlerdelegate) { self delegate = delegate } store the host id and use it to indicate when a chat was sent by the host func sethostid( id string) { self hostid = id } update the configuration of the people feature make it accessible in the interface if available the number of people is provided, allowing you to display this in the interface, for example as a badge on the button func updatepeopleconfiguration(isavailable bool, count int) { peoplebutton ishidden = !isavailable peoplebutton badge = count } update the configuration of the polls feature make it accessible in the interface if available the state of unanswered polls is provided, allowing you to display an indicator in the interface, for example as an "unread" badge on the button func updatepollsconfiguration(isavailable bool, unansweredpollscount int) { pollsbutton ishidden = !isavailable pollsbutton unreadbadge ishidden = unansweredpollscount == 0 } update the configuration of the tipping feature make it accessible in the interface if available func updatetippingconfiguration(isavailable bool) { tippingbutton ishidden = !isavailable } update the top insets of your interface to avoid underlapping other interface elements func updatetopcontentinset( topinset cgfloat) { tableview\ contentinset top = topinset } make the chat input toolbar visible func displaychatinputtoolbar() becomefirstresponder() } dismiss the chat input toolbar func dismisschatinputtoolbar() { resignfirstresponder() } dismiss the keyboard if it is visible func dismisschatinputtoolbar() { inputaccessoryview? textfield resignfirstresponder() } handle the backlog of chats and tips to display them in your interface func handlebacklog(chats \[hmchat], tips \[hmtip]) { // update data source tableview\ reloaddata() } handle new chats, tips, and reactions to insert them into your interface this function is called periodically to give you the opportunity to handle the pending items return true if handled or false if not if not handled, the same items will be provided in the next invocation func handlepending(chats \[hmchat], tips \[hmtip], reactions \[hmchatreaction]) > bool { if !chats isempty || !tips isempty { // update data source tableview\ insertrows(at newindexpaths, with automatic) } if !reactions isempty { // update data source, update cell contents, update cell height } return true } remove a chat by id func handledeletedchat(id string) { // update data source tableview\ deleterows(at indexpath], with automatic) } remove a tip by id func handledeletedtip(id string) { // update data source tableview\ deleterows(at indexpath], with automatic) } remove a reaction by chat id, user id, and type func handledeletedreaction(chatid string, userid string, type hmchatreactiontype) { // update data source, update cell contents, update cell height } chat handler delegate the hmchathandlerdelegateprotocol defines functions your hmchathandlerimplementation calls to get information and inform hotmic when various events occur asks the delegate if the handler can display the chat input toolbar override var canbecomefirstresponder bool { delegate? chathandlercandisplaychatinputtoolbar(self) ?? false } asks the delegate if the currently authenticated user can moderate another user if delegate? chathandler(self, canmoderateuser userid) == true { // display options to block user from chat/tip and delete chat/tip } asks the delegate if the currently authenticated user can make another user a moderator if delegate? chathandler(self, canmakeusermoderator userid) == true { // display option to make user moderator } inform the delegate the handler invoked send chat delegate? chathandler(self, didtapsendchat text) inform the delegate the handler invoked add reaction delegate? chathandler(self, didtapaddreaction reactiontype, to chat id) inform the delegate the handler invoked remove reaction delegate? chathandler(self, didtapremovereaction reactiontype, from chat id) inform the delegate the handler invoked make user moderator delegate? chathandler(self, didtapmakeusermoderator userid) inform the delegate the handler invoked report chat delegate? chathandler(self, didtapreportchat chat) inform the delegate the handler invoked report tip delegate? chathandler(self, didtapreporttip tip) inform the delegate the handler invoked block user from chat delegate? chathandler(self, didtapblockuserfromchat chat) inform the delegate the handler invoked block user from tip delegate? chathandler(self, didtapblockuserfromtip tip) inform the delegate the handler invoked delete chat delegate? chathandler(self, didtapdeletechat chat) inform the delegate the handler invoked delete tip delegate? chathandler(self, didtapdeletetip tip) inform the delegate the handler invoked view a user's profile delegate? chathandler(self, didtapviewuserprofile userid) inform the delegate the handler invoked the people feature delegate? chathandlerdidtapshowpeople(self) inform the delegate the handler invoked the polls feature delegate? chathandlerdidtapshowpolls(self) inform the delegate the handler invoked the tipping feature delegate? chathandlerdidtapshowtipping(self) get chat reactions you can fetch reaction details for a specific chat use this to create an interface that lists people who reacted and which reaction they chose if you provide a custom view controller for chat hmmediaplayer getreactions(chatid chat id) { result in switch result { case success(let reactions) // display the list of reactions case failure(let error) // handle error } }