Creating an iPhone movie talk app using Parse and Opentok (tokbox)
Creating an iPhone movie talk app isn’t rocket science, but there are a lot of intricacies to account for. This tutorial covers the entire purpose of developing such an app from begin to finish. It is fairly long, so you might want to bookmark it so you can revisit it later.
The end result: The entire project can be downloaded here. You can browse if you want to figure it out for yourself, for those who are interested in a detailed walkthrough, read on.
Movie talk is one of the most popular forms of communication on mobile, and is for productivity apps as well as social networking.
So how does one go about building such an app? The question is more of design than of technical implementation when many online services provide a lot of the infrastructure needed.
By the end of this tutorial, our app would look like this:
Yes, that is a monkey talking with a mammoth. Which is basically what we would be doing communicating with the services composing our app.
The Basics of a movie Talk app:
While the top movie talk application use their own streaming servers, we will be using a cloud-based solution to treat that part of the equation. Opentok is the service that we’ll be using in this tutorial, and it provides a nice, easy-to-integrate SDK for adding movie streaming capabilities to our app.
While Opentok provides their iOS API and iOS SDK with two useful examples of iOS implementation (here and here), there is still one lump left: User management. Opentok provides nice platform for movie talk, but inbetween whom? Let’s take a deeper dive into how streaming actually works inbetween users of services like Skype, MSN, and Yahoo movie talk.
A streaming server works on fundamentals of an Http web server: In a pretty raw representation, all it recognizes is request and response, without caring who sent it. An Http server does just that. It does not worry about states, userID, password or any such access token mechanism. In other words, session management is absent.
The script switches when there arises a need to identify the user, authenticate him, and keep him authenticated during his entire browsing practice. A server needs to recall a user, and whatever other entities that come linked with him. These requirements become more stringent while developing a mobile software like iPhone movie talk, where identities are fairly crucial – they can make or break the authenticity of your app. In large web portals such as Yahoo.com or Amazon.com, dedicated authentication servers generate unique identity for users, and supply application servers with these goodies so that they can track the users until they log out.
To perform its task, a streaming server relies on only one entity to recognize who sent the request and whom to react – this entity is session ID. It does not recognize or want to care who generated it. As far as it receives a valid sessionID, it keeps replying to streaming requests. And here comes rule of thumb for any talking app:
Any user with a valid sessionID is, in principle, automatically entitled to view other user’s feed who is also using the same sessionID. Session IDs are of the form:
You very likely know where this is going: To keep track of who wants to connect to whom in a more real-worldly way, like we do in Yahoo Messenger or Skype, we need another server that keeps track of users. User management is essential in any social networking app. It depends on you how much you want to do it – you can store big data including address, phone numbers, profile pics and the likes, or you can pick 3-4 fields of your choice to make it lighter on the user. But the crux of the matter is you need to do it. Any software that relies on user interaction cannot do without it – and so is our iPhone movie talk.
To treat user management, we need a central server. To our superb convenience, there are number of cloud solutions available again. For the purpose of this tutorial, I have chosen Parse.com because it boasts of 100k+ apps live, and is backed by Facebook. It claims easiest of the APIs, and those claims, as I have experienced, are right. What’s more, it’s cross platform, so if you plan to make Android or Windows mobile client for your movie talk app, you are in safe forearms.
The objectives of this tutorial are to demonstrate:
- How to enable Parse.com users see each other (Your dearest messenger’s who’s online sort)
- How to make them talk with each other using Opentok movie streaming feature in your next good iphone movie talk app
Tokbox provides a Broadcast tutorial which contains some of what we need for a fully featured chap app. The main difference is that we’re creating a two-way talk instead of a one-to-many broadcast system. I’ll also attempt to simplify the process so that even novice developers should be able to finish it without much trouble.
Setup (Parse.com and Tokbox.com) for your iPhone movie talk app – LiveSessions:
My showcase app – Livesessions – requires some configuration on both Tokbox.com and Parse.com profiles – the server side. Parse.com needs your app, and so does Tokbox.com, albeit it names it a Project.
I assume you know enough to configure an app on Parse.com. In addition to it, you also need two data tables – ActiveUsers and ActiveSessions, tho’ there is no need to pre-populate them at any point. Here is a snap of what their column lists will look like:
- callerID – String
- callerTitle – String
- receiverID – String
- sessionID – String
- publisherToken – String
- subscriberToken – String
- isVideo – Boolean
- isAudio – Boolean
Similarly, on Tokbox.com, once you login, go to dashboard and create fresh project, it will automatically create an API key and API secret. Note that these are your credential as a Tokbox developer, not a talk user. API key is more like your user ID and API secret is a password.
The resulting Tokbox dashboard screen will look like this (the real API key and text are hidden):
iPhone movie talk
There is one, crucial lump left to be done to finish the server side. As we discussed earlier, we need to connect our streaming application server (Tokbox.com) with authentication server (Parse.com). Unless this is done, streaming server has no way to know which user is calling whom because all it knows about is session ID. Our iPhone movie talk user, on the other palm, only knows about his own user credentials supplied to him by Parse.com.
As it is apparent, it is Parse.com’s job to connect the two. That is:
- To intercept logged in user’s request to talk (the caller).
- To convert it into Tokbox session ID, get the session ID and other necessary information (the token) from Tokbox.
- To send the caller and receiver (Parse.com users) the sessionID.
- Since both users now have a session ID and a token which is received from Parse.com, they can seamlessly communicate via Tokbox streaming server.
To accomplish above, Parse.com must obtain sessionID, publisherToken and subscriberToken from tokbox, and that is where Parse cloud code comes into picture. In Parse Cloud code section, you must upload some code so that the resulting screen looks like this:
The process of uploading (oops, deploying) cloud code on Parse.com is described in this source tutorial (Setting Up section) and here, and it is far better than I could explain here. So let’s skip it to maintain the scope. However for plainness’s sake, I have included it step by step in readme.txt that comes within the code.
I would still take a few moments to explain what this code does, and why. The iPhone app user initiates a call to another user. No, he does not make a phone call. Recall, it’s LiveSession app’s job to treat the entire call, just like Skype or Yahoo messenger does. What our iPhone app needs to do under the rubber hood is fairly plain task: it is saving a row to ActiveSessions Parse table we just created. There, it stores caller user ID (callerID) and receiver user ID (receiverID) among other things.
Now what this cloud code does is something fairly magical, yet plain. It intercepts the Save operation in its beforeSave cloud trigger – nicely elaborated here. From within beforesave, Opentok javascript API takes over. Opentok supplied function createSession generates a session ID. Another function, Opentok.generateToken, creates a publisher token or subscriber token, depending on the role argument passed, which determines whether you want to publish your own movie feed (Opentok.ROLE.PUBLISHER) or see other user’s movie feed (Opentok.ROLE.SUBSCRIBER) using that session ID. (It is unclear from my practice if there is any difference inbetween the two. For the scope of this discussion let us generate both as we need two-way movie feeds anyway.)
Since we must recall we are within beforeSave trigger, we already have the treat to the object being saved – ActiveSessions. By simply setting its respective members – we can save three distinct items to our Parse.com database: sessionID, publisherToken and subscriberToken – all three columns in ActiveSession table.
Well – that’s for that. What? You are done with the back end of your very first iPhone movie talk app! Congratulations!
But how? All we required for two user’s to connect via streaming server is a user id (session id) and a password (token) – and we have both. Now all we have to do is – avail them to iOS client via Parse.com. Not that it’s fairly effortless as 1-2-3, but the mammoth has bitten the dust already.
Serve yourself a cup of hot coffee as you wait to see one of your friends on your iPhone movie app screen! Well, not fairly quickly, but before you leap in, you can do them some favor: read this disclaimer if it can help any of your Android friends:
iOS client – the other part of the deal:
Now that the back end is accomplished, let’s concentrate on how iOS client – our own LiveSession iPhone app – keeps its part of the deal. The major tasks we aim to cover are:
1) Initiate iPhone movie talk call – by saving an ActiveSession row (we already described the server part of it above as part of cloud code)
Two) Treat Incoming Call – check Parse.com database for an incoming movie call – described down the line.
Let’s tackle each of them – one by one. But very first and foremost, let’s setup the basic iOS project and go over what’s all needed.
iPhone Movie Talk Initial Project Setup:
Open XCode, setup a single View application with default options. Name it LiveSessions. In storyboards, set up two scenes: One for users list – named LSViewController (derived from UIViewController), and another for hosting movie talk view, LSStreamingViewController (again, subclass of UIViewController).
Next, add a UITableView object to LSViewController scene through storyboard. This table view must maintain a list of Parse.com users who log into LiveSession app any time. LSViewController will treat all the chores related to UITableView, nothing unusual. Do not leave behind to implement UITableViewDelegate and UITableViewDatasource protocols in LSViewController so as to treat table view related stuff.
In addition to the above, we also need a helper class called ParseHelper which wraps our calls to Parse. All of its members and functions would be static.
To use Parse and Opentok framework, we need to link them, as well as some of the required frameworks used by both of them.
Parse framework can be obtained from here.
Opentok SDK can be obtained from here. This link also explains at length what all you need to do in order to link Opentok framework successfully.
Next, you should add some libraries to LiveSessions by selecting it, going to Build Phases->Link Binary section. After adding number of frameworks, this section should look like this:
At the end of linking everything, your Project tree should look like this:
(Notice the Opentok.bundle thing that come as part of Opentok sdk. Also see three other libraries that go below it in order to link everything together).
Before we proceed, let’s have one look at how beautiful (!) our storyboard looks:
Step one – Initiate Movie Call:
Parse.com acts as a mediator inbetween caller, Opentok streaming engine and receiver. The very first and foremost requirement is to generate sessionID, publisherToken and subscriberToken, so that both clients can seamlessly connect via Opentok once session is established. We already know the Parse.com cloud code does that. But how will LiveSessions app invoke the cloud code?
The following code not only stores an ActiveSessions object to Parse, but also invokes cloud code (beforeSave trigger) we discussed above that generates sessionID, publisherToken and subscriberToken – and they are eventually stored into ActiveSessions table itself.
At the end of executing the above code, we should have a sessionID, publisherToken as well as subscriberToken in our Parse.com ActiveSessions table. Alright, but who will execute it? Lot of stuff still remain unanswered – for example, from where does all the argument values (receiverID, callerID) come from? We deliberately missed that part, because establishing the session was most significant. The callerID, receiverID parameters that we used above are actually just the user IDs generated by Parse.com PFUser object. You can have your own way of registering and authenticating a user. In LiveSessions, we just store each user within ActiveUsers table, and only using a user title of his / her own choice. No emails, passwords or verification. And here is code that is responsible for it:
What this does is plain: when the app launches, check for the locally stored Parse user ( [PFUser currentUser] ), and if one does not exist, perform anonymous login, which will create a PFUser object on Parse.com Users table. What is significant to us is loggedInUser static object that we use to store presently logged on user. At the end of successful login, showUserTitlePrompt function prompts the user to come in a title of his / her choice.
Fine, but what happens when user comes in it? Well, significant number things. For a begin, here is how LiveSessions treats it:
Notice the part under tag kUIAlertViewTagUserName . This code tells LiveSessions that user is now fully logged in, along with an identification (title) of his / her choice. This title will be eventually stored into ActiveUsers table as userTitle, but with one more thing: user’s current location. Yes, LiveSessions is a location-aware app. And to obtain user’s location, ParseHelper.m posts a kLoggedInNotification notification to LSViewController. LSViewController has CLLocationManager code inwards it which will track user’s current location. At the end, once we have everything, the entire user (his title, user ID and location) are saved into ActiveUsers table.
Here is what goes inwards LSViewController to obtain user’s current location, and call to Parse wrapper for storing it to ActiveUsers table:
The very first unknown in above code so far is call to fireNearUsersQuery function,which serves front end. We will come to it later. The other unknown is saveUserWithLocationToParse function, which will pack the gaps left so far to finish the back end. It belongs to ParseHelper.m, and here it goes – there is nothing unusual about storing it, and it acts as our own little user repository. The generated user’s object ID is stored for later use inwards activeUserObjectID .
The code so far ensured a user is saved inwards ActiveUsers table. We also eyed how he / she can initiate a movie call to another user, by creating an ActiveSessions object. But whom does the user talk with?
We must also present a list of users to logged on user to talk with – equivalent of Yahoo/Skype friend’s list. Sending friend requests through email or any other means would be fairly an overkill for our tutorial’s scope. To keep things minimal, we don’t even ask our users to come in their email ID for registration.
Instead, we have chosen a unique way to test out movie talk feature: display list of users who are geographically within specified radii – say two hundred miles. Parse.com already has PFGeopoint related query mechanism which makes our task lighter.
The other unknown in code above, fireNearUsersQuery goes as below, and it fills up the datasource for the LSViewController table view – an NSMutableArray made of dictionaries packed with user’s titles:
The result of fireNearUsersQuery call will be somewhat like below, where three nearby users (<200 miles radii) are visible for talk:
Inwards LSViewController, the m_userTableView gets populated from m_userArray . Each row in the table view has a green Call button. When you tap that button, call is initiated for that user as the receiver ID. What call? The code we just covered to store the session: saveSessionToParse . Who calls it? Well, now it’s time the movie talk scene (LSStreamingViewController) takes charge.
Before proceeding, take a look at this activity flow – the big picture. You will come back to it fairly often as you read on:
Upon tapping of the green phone call button, a segue is performed to transition to LSStreamingViewController. Inwards LSStreamingViewController, [ParsHelper saveSessionToParse] is called. Here is that part:
Once inwards LSStreamingViewController:
As a matter of its duty, LSStreamingViewController treats both outgoing and incoming calls. To differentiate the two, it uses receiver ID ( self.callreceiverID ): For outgoing calls, it has a value supplied from LSViewController (see the segue transition code). For incoming calls, there is no need for it so it is null or empty.
As soon as saveSessionToParse saves ActiveSessions object to Parse.com database, it notifies LSStreamingViewController so that sessionID, publisherToken and subscriberToken values from Opentok (that became available to app’s delegate) can be usable to LSStreamingViewController. This notification ( kSessionSavedNotification ) is treated by sessionSaved like this:
In forthcoming section we will see how the above call makes movie talk fully seamless inbetween two users, without Parse intervention.
Huh..the mammoth has been laid to rest, but there is still life in it. We already covered session generation part. But how does the other user know about it? And when exactly Opentok takes the charge to commence the titillating movie?
Step two – Treat Incoming Call:
Treating of an incoming call is tricky bit. Let’s list out the nude minimum necessities:
- You need to poll the database for a session destined to you (logged on user) – that is – search for an ActiveSessions record where current user is listed as receiver.
- You need to ensure that database is up-to-date once the call has been established – that is, eliminate the session row once sessionID and tokens have been read up into iPhone app
- You also need to signal interruptions while a session is ON – that is, inform the caller gracefully that the receiver is busy on another call. For simpleness’s sake, we aren’t treating multi-user calls (conference) right now, albeit it can be treated fairly lightly.
Recall that in alertView:(UIAlertView *)alertView clickedButtonAtIndex , we witnessed a call to [appDelegate fireListeningTimer] ,and now it is time to expand it, because it accomplishes our very first task of the three listed above: It fires a timer that continually polls Parse.com ActiveSessions table for calls destined to current user.
As it is named, [ParseHelper pollParseForActiveSessions] will poll ActiveSessions table for sessions calling out to this user – that is, rows which have receiverID = presently logged on user’s object ID.
The method is fairly self-explanatory – whenever it finds an ActiveSessions object, it just copies all the fields it needs – sessionID, publisherToken, and subscriberToken into app delegate’s properties. Once done, it deletes it from Parse.com backend using [self deleteActiveSession] call. [self setPollingTimer:NO] is to keep things in sync: it ensures that timer doesn’t fire up another polling query through pollParseForActiveSessions after an object has been found and deletion is in progress using [self deleteActiveSession] .
Once the ActiveSession values are copied to App’s delegate, more significant stuff is waiting: user needs to be notified of an incoming call. incomingCallAlert performs this task, and here is the result:
What’s more significant is incomingCallAlert’s delegate, which we already visited in Step one – let’s go over it again:
If user had not accepted the call, the polling timer flag is set and app starts to look for fresh incoming call session. If user rather determines to accept the call, kIncomingCallNotification is posted, and it is responsible for notifying LSViewController that a call has arrived. In fact, any view controller within your talk app should be able to receive this notification, so that you can treat the call irrespective of where you are in the app. Such notifications can be treated by having a UIViewController subclass from which all your view controllers can inherit. For plainness, Livesessions only has one view controller that treats incoming call, and here is how:
didCallArrive fires in response to kIncomingCallNotification , and all it does it empty the m_receiverID to indicate that call is destined to self – an incoming call. This, as we already spotted in prepareForSegue – is enough to signal LSStreamingViewController that call is supposed to be treated as incoming call – so no fresh ActiveSessions object need to be stored. All that is left is to utilize the sessionID and token values to connect to Tokbox streaming server.
So far, we discussed both cases – in both we obtained sessionID, publisherToken and subscriberToken into our app’s delegate. We eventually passed the control over to LSStreamingViewController. In case of an outgoing call, [LSStreamingViewController sessionSaved] function calls [LSStreamingViewController connectWithSubscriberToken] . In case of incoming call, as we already witnessed in [LSStreamingViewController viewDidAppear] , a call is made to [LSStreamingViewController connectWithPublisherToken] .
The only difference inbetween two of them is the token they use – and Opentok isn’t fairly clear about what switches if you use one token instead of the other (publisher token or subscriber token – as you reminisce it is generated from cloud code in beforeSave trigger). Irrespective of which one you use to connect to a session, it permits you to publish your stream (your camera feed) as well as subscribe to other user’s stream.
Once [_session connectWithApiKey] call is made, Opentok takes over. All you need to recall is that your streaming view controller (LSStreamingViewController) must implement these protocols: OTSessionDelegate, OTSubscriberDelegate, OTPublisherDelegate. See the activity flow diagram up again – there, iOS app initiated deeds are listed in yellow, and delegates are marked in green. These delegate functions are part of these three protocols that LSStreamingViewController must implement. As they are called by Opentok along the flow, you need to take various deeds to make that enticing movie available to your user.
Now it no longer matters whether you are a caller or a receiver as far as you implement necessary delegate methods from Opentok. The Broadcast tutorial from Opentok has all implementation details, and I have followed it bit by bit, apart from my own UI modifications. For example, if you choose to view your own stream as soon as session gets connected, following code accomplishes it:
In the code above, [_session publish] call prompts the user to permit his / her own camera feed, and as soon as he / she permits it, LiveSessions commence publishing the camera feed to Opentok streaming server. A crucial chunk to reminisce here is the call to following:
SDK is designed such that without this, you never get to see your own feed. And no, any indirect method (e.g. addSubView to a container view) to set the framework doesn’t work. At the same time you can decorate your publisher view. For example, we have switched features like border color and corner radius.
Witnessing the feed of the other user in the same session is somewhat that doesn’t fall into any order. All you need to do is implement necessary delegates so that as soon as you begin receiving that feed, you get an chance to configure it fully – like this:
subscriberDidConnectToStream delegate permits you to configure your own subscriber view. Again, setFrame statement is crucial and if you don’t include it or do it with wrong values, you may never get to see other user’s feed – something that can break your (and your friends’) heart! Again, you can do your own UI modifications such as reporting the current status (using _statusLabel ) and decorating the subscriber view inwards the same delegate.
There are slew of other delegates that Opentok sdk provides that you can use to include various features to smarten your app.
For example, see how I chose to treat subscriber didFailWithError delegate:
There is much more inwards LSStreamingViewController that needs little or no explanation for someone who knows UIKit well. So we pridefully announce that the mammoth may have stopped breathing -you can see it yourself:
By the way, who is that insane soul screaming in the publisher view? LiveSessions isn’t that clever – what it shows there is what your iPhone’s camera sees!
And yeah, if you didn’t notice, the sleeping animal is an elephant, not a mammoth. Mammoths existed only in ice age. So is this real?
Who knows, it’s just virtual. But so are nerds trolling in talk rooms.
All we need now is to discard the remnants to smoother the flow of our iPhone movie talk – let’s do them in one go.
The Cleanup:
As a VOIP app, LiveSessions must either keep running in the background or do its part of cleanup as soon as it comes in background. To preserve plainness, I chose later. There are some rules laid by Apple to perform any task in background – be it trivial or not. Within LSAppDelegate.m, we cleanup our back end following those rules.
And we also wake up following those rules:
And here goes what we call in the above code:
Both delete functions do what is expected – they delete ActiveUsers and ActiveSessions object from Parse.com database. There is nothing unusual that they do. objectsUnderDeletion array is our way of keeping things in sync: when Parse.com is busy deleting stuff in background, it prevents our app from repeatedly firing delete guidelines.
Sign off and Providing Ins:
This tutorial and the code that comes with can serve as naked backbone to your next cutting edge messenger App. You can do your own customizations for Parse user management or UI layout and create your next killer iphone movie talk app.
Was this tutorial useful? Check out VideoStream SDK for iOS:
A powerful movie streaming Objective-C framework for iOS devices with support for numerous protocols & formats (mms, http, rtsp, rtmp, mjpeg). Built with FFmpeg, OpenGL ES Two.0 & Apple’s AudioUnit.
Creating an iPhone movie talk app using Parse and Opentok (tokbox) – Tutorial
Creating an iPhone movie talk app using Parse and Opentok (tokbox)
Creating an iPhone movie talk app isn’t rocket science, but there are a lot of intricacies to account for. This tutorial covers the entire purpose of developing such an app from begin to finish. It is fairly long, so you might want to bookmark it so you can revisit it later.
The end result: The entire project can be downloaded here. You can browse if you want to figure it out for yourself, for those who are interested in a detailed walkthrough, read on.
Movie talk is one of the most popular forms of communication on mobile, and is for productivity apps as well as social networking.
So how does one go about building such an app? The question is more of design than of technical implementation when many online services provide a lot of the infrastructure needed.
By the end of this tutorial, our app would look like this:
Yes, that is a monkey talking with a mammoth. Which is basically what we would be doing communicating with the services composing our app.
The Basics of a movie Talk app:
While the top movie talk application use their own streaming servers, we will be using a cloud-based solution to treat that part of the equation. Opentok is the service that we’ll be using in this tutorial, and it provides a nice, easy-to-integrate SDK for adding movie streaming capabilities to our app.
While Opentok provides their iOS API and iOS SDK with two useful examples of iOS implementation (here and here), there is still one lump left: User management. Opentok provides nice platform for movie talk, but inbetween whom? Let’s take a deeper dive into how streaming actually works inbetween users of services like Skype, MSN, and Yahoo movie talk.
A streaming server works on fundamentals of an Http web server: In a pretty raw representation, all it recognizes is request and response, without caring who sent it. An Http server does just that. It does not worry about states, userID, password or any such access token mechanism. In other words, session management is absent.
The script switches when there arises a need to identify the user, authenticate him, and keep him authenticated during his entire browsing practice. A server needs to recall a user, and whatever other entities that come linked with him. These requirements become more stringent while developing a mobile software like iPhone movie talk, where identities are fairly crucial – they can make or break the authenticity of your app. In large web portals such as Yahoo.com or Amazon.com, dedicated authentication servers generate unique identity for users, and supply application servers with these goodies so that they can track the users until they log out.
To perform its task, a streaming server relies on only one entity to recognize who sent the request and whom to react – this entity is session ID. It does not recognize or want to care who generated it. As far as it receives a valid sessionID, it keeps replying to streaming requests. And here comes rule of thumb for any talking app:
Any user with a valid sessionID is, in principle, automatically entitled to view other user’s feed who is also using the same sessionID. Session IDs are of the form:
You most likely know where this is going: To keep track of who wants to connect to whom in a more real-worldly way, like we do in Yahoo Messenger or Skype, we need another server that keeps track of users. User management is essential in any social networking app. It depends on you how much you want to do it – you can store big data including address, phone numbers, profile pics and the likes, or you can pick 3-4 fields of your choice to make it lighter on the user. But the crux of the matter is you need to do it. Any software that relies on user interaction cannot do without it – and so is our iPhone movie talk.
To treat user management, we need a central server. To our excellent convenience, there are number of cloud solutions available again. For the purpose of this tutorial, I have chosen Parse.com because it boasts of 100k+ apps live, and is backed by Facebook. It claims easiest of the APIs, and those claims, as I have experienced, are right. What’s more, it’s cross platform, so if you plan to make Android or Windows mobile client for your movie talk app, you are in safe palms.
The objectives of this tutorial are to showcase:
- How to enable Parse.com users see each other (Your beloved messenger’s who’s online sort)
- How to make them talk with each other using Opentok movie streaming feature in your next fine iphone movie talk app
Tokbox provides a Broadcast tutorial which contains some of what we need for a fully featured chap app. The main difference is that we’re creating a two-way talk instead of a one-to-many broadcast system. I’ll also attempt to simplify the process so that even novice developers should be able to finish it without much trouble.
Setup (Parse.com and Tokbox.com) for your iPhone movie talk app – LiveSessions:
My showcase app – Livesessions – requires some configuration on both Tokbox.com and Parse.com profiles – the server side. Parse.com needs your app, and so does Tokbox.com, albeit it names it a Project.
I assume you know enough to configure an app on Parse.com. In addition to it, you also need two data tables – ActiveUsers and ActiveSessions, however there is no need to pre-populate them at any point. Here is a snap of what their column lists will look like:
- callerID – String
- callerTitle – String
- receiverID – String
- sessionID – String
- publisherToken – String
- subscriberToken – String
- isVideo – Boolean
- isAudio – Boolean
Similarly, on Tokbox.com, once you login, go to dashboard and create fresh project, it will automatically create an API key and API secret. Note that these are your credential as a Tokbox developer, not a talk user. API key is more like your user ID and API secret is a password.
The resulting Tokbox dashboard screen will look like this (the real API key and text are hidden):
iPhone movie talk
There is one, crucial lump left to be done to finish the server side. As we discussed earlier, we need to connect our streaming application server (Tokbox.com) with authentication server (Parse.com). Unless this is done, streaming server has no way to know which user is calling whom because all it knows about is session ID. Our iPhone movie talk user, on the other palm, only knows about his own user credentials supplied to him by Parse.com.
As it is apparent, it is Parse.com’s job to connect the two. That is:
- To intercept logged in user’s request to talk (the caller).
- To convert it into Tokbox session ID, get the session ID and other necessary information (the token) from Tokbox.
- To send the caller and receiver (Parse.com users) the sessionID.
- Since both users now have a session ID and a token which is received from Parse.com, they can seamlessly communicate via Tokbox streaming server.
To accomplish above, Parse.com must obtain sessionID, publisherToken and subscriberToken from tokbox, and that is where Parse cloud code comes into picture. In Parse Cloud code section, you must upload some code so that the resulting screen looks like this:
The process of uploading (oops, deploying) cloud code on Parse.com is described in this source tutorial (Setting Up section) and here, and it is far better than I could explain here. So let’s skip it to maintain the scope. However for simpleness’s sake, I have included it step by step in readme.txt that comes within the code.
I would still take a few moments to explain what this code does, and why. The iPhone app user initiates a call to another user. No, he does not make a phone call. Reminisce, it’s LiveSession app’s job to treat the entire call, just like Skype or Yahoo messenger does. What our iPhone app needs to do under the rubber hood is fairly plain task: it is saving a row to ActiveSessions Parse table we just created. There, it stores caller user ID (callerID) and receiver user ID (receiverID) among other things.
Now what this cloud code does is something fairly magical, yet ordinary. It intercepts the Save operation in its beforeSave cloud trigger – nicely elaborated here. From within beforesave, Opentok javascript API takes over. Opentok supplied function createSession generates a session ID. Another function, Opentok.generateToken, creates a publisher token or subscriber token, depending on the role argument passed, which determines whether you want to publish your own movie feed (Opentok.ROLE.PUBLISHER) or see other user’s movie feed (Opentok.ROLE.SUBSCRIBER) using that session ID. (It is unclear from my practice if there is any difference inbetween the two. For the scope of this discussion let us generate both as we need two-way movie feeds anyway.)
Since we must recall we are within beforeSave trigger, we already have the treat to the object being saved – ActiveSessions. By simply setting its respective members – we can save three distinct items to our Parse.com database: sessionID, publisherToken and subscriberToken – all three columns in ActiveSession table.
Well – that’s for that. What? You are done with the back end of your very first iPhone movie talk app! Congratulations!
But how? All we required for two user’s to connect via streaming server is a user id (session id) and a password (token) – and we have both. Now all we have to do is – avail them to iOS client via Parse.com. Not that it’s fairly effortless as 1-2-3, but the mammoth has bitten the dust already.
Serve yourself a cup of hot coffee as you wait to see one of your friends on your iPhone movie app screen! Well, not fairly quickly, but before you hop in, you can do them some favor: read this disclaimer if it can help any of your Android friends:
iOS client – the other part of the deal:
Now that the back end is accomplished, let’s concentrate on how iOS client – our own LiveSession iPhone app – keeps its part of the deal. The major tasks we aim to cover are:
1) Initiate iPhone movie talk call – by saving an ActiveSession row (we already described the server part of it above as part of cloud code)
Two) Treat Incoming Call – check Parse.com database for an incoming movie call – described down the line.
Let’s tackle each of them – one by one. But very first and foremost, let’s setup the basic iOS project and go over what’s all needed.
iPhone Movie Talk Initial Project Setup:
Open XCode, setup a single View application with default options. Name it LiveSessions. In storyboards, set up two scenes: One for users list – named LSViewController (derived from UIViewController), and another for hosting movie talk view, LSStreamingViewController (again, subclass of UIViewController).
Next, add a UITableView object to LSViewController scene through storyboard. This table view must maintain a list of Parse.com users who log into LiveSession app any time. LSViewController will treat all the chores related to UITableView, nothing unusual. Do not leave behind to implement UITableViewDelegate and UITableViewDatasource protocols in LSViewController so as to treat table view related stuff.
In addition to the above, we also need a helper class called ParseHelper which wraps our calls to Parse. All of its members and functions would be static.
To use Parse and Opentok framework, we need to link them, as well as some of the required frameworks used by both of them.
Parse framework can be obtained from here.
Opentok SDK can be obtained from here. This link also explains at length what all you need to do in order to link Opentok framework successfully.
Next, you should add some libraries to LiveSessions by selecting it, going to Build Phases->Link Binary section. After adding number of frameworks, this section should look like this:
At the end of linking everything, your Project tree should look like this:
(Notice the Opentok.bundle thing that come as part of Opentok sdk. Also see three other libraries that go below it in order to link everything together).
Before we proceed, let’s have one look at how beautiful (!) our storyboard looks:
Step one – Initiate Movie Call:
Parse.com acts as a mediator inbetween caller, Opentok streaming engine and receiver. The very first and foremost requirement is to generate sessionID, publisherToken and subscriberToken, so that both clients can seamlessly connect via Opentok once session is established. We already know the Parse.com cloud code does that. But how will LiveSessions app invoke the cloud code?
The following code not only stores an ActiveSessions object to Parse, but also invokes cloud code (beforeSave trigger) we discussed above that generates sessionID, publisherToken and subscriberToken – and they are eventually stored into ActiveSessions table itself.
At the end of executing the above code, we should have a sessionID, publisherToken as well as subscriberToken in our Parse.com ActiveSessions table. Alright, but who will execute it? Lot of stuff still remain unanswered – for example, from where does all the argument values (receiverID, callerID) come from? We deliberately missed that part, because establishing the session was most significant. The callerID, receiverID parameters that we used above are actually just the user IDs generated by Parse.com PFUser object. You can have your own way of registering and authenticating a user. In LiveSessions, we just store each user within ActiveUsers table, and only using a user title of his / her own choice. No emails, passwords or verification. And here is code that is responsible for it:
What this does is plain: when the app launches, check for the locally stored Parse user ( [PFUser currentUser] ), and if one does not exist, perform anonymous login, which will create a PFUser object on Parse.com Users table. What is significant to us is loggedInUser static object that we use to store presently logged on user. At the end of successful login, showUserTitlePrompt function prompts the user to inject a title of his / her choice.
Fine, but what happens when user comes in it? Well, significant number things. For a begin, here is how LiveSessions treats it:
Notice the part under tag kUIAlertViewTagUserName . This code tells LiveSessions that user is now fully logged in, along with an identification (title) of his / her choice. This title will be eventually stored into ActiveUsers table as userTitle, but with one more thing: user’s current location. Yes, LiveSessions is a location-aware app. And to obtain user’s location, ParseHelper.m posts a kLoggedInNotification notification to LSViewController. LSViewController has CLLocationManager code inwards it which will track user’s current location. At the end, once we have everything, the entire user (his title, user ID and location) are saved into ActiveUsers table.
Here is what goes inwards LSViewController to obtain user’s current location, and call to Parse wrapper for storing it to ActiveUsers table:
The very first unknown in above code so far is call to fireNearUsersQuery function,which serves front end. We will come to it later. The other unknown is saveUserWithLocationToParse function, which will pack the gaps left so far to finish the back end. It belongs to ParseHelper.m, and here it goes – there is nothing unusual about storing it, and it acts as our own little user repository. The generated user’s object ID is stored for later use inwards activeUserObjectID .
The code so far ensured a user is saved inwards ActiveUsers table. We also eyed how he / she can initiate a movie call to another user, by creating an ActiveSessions object. But whom does the user talk with?
We must also present a list of users to logged on user to talk with – equivalent of Yahoo/Skype friend’s list. Sending friend requests through email or any other means would be fairly an overkill for our tutorial’s scope. To keep things minimal, we don’t even ask our users to inject their email ID for registration.
Instead, we have chosen a unique way to test out movie talk feature: showcase list of users who are geographically within specified radii – say two hundred miles. Parse.com already has PFGeopoint related query mechanism which makes our task lighter.
The other unknown in code above, fireNearUsersQuery goes as below, and it fills up the datasource for the LSViewController table view – an NSMutableArray made of dictionaries packed with user’s titles:
The result of fireNearUsersQuery call will be somewhat like below, where three nearby users (<200 miles radii) are visible for talk:
Inwards LSViewController, the m_userTableView gets populated from m_userArray . Each row in the table view has a green Call button. When you tap that button, call is initiated for that user as the receiver ID. What call? The code we just covered to store the session: saveSessionToParse . Who calls it? Well, now it’s time the movie talk scene (LSStreamingViewController) takes charge.
Before proceeding, take a look at this activity flow – the big picture. You will come back to it fairly often as you read on:
Upon tapping of the green phone call button, a segue is performed to transition to LSStreamingViewController. Inwards LSStreamingViewController, [ParsHelper saveSessionToParse] is called. Here is that part:
Once inwards LSStreamingViewController:
As a matter of its duty, LSStreamingViewController treats both outgoing and incoming calls. To differentiate the two, it uses receiver ID ( self.callreceiverID ): For outgoing calls, it has a value supplied from LSViewController (see the segue transition code). For incoming calls, there is no need for it so it is null or empty.
As soon as saveSessionToParse saves ActiveSessions object to Parse.com database, it notifies LSStreamingViewController so that sessionID, publisherToken and subscriberToken values from Opentok (that became available to app’s delegate) can be usable to LSStreamingViewController. This notification ( kSessionSavedNotification ) is treated by sessionSaved like this:
In forthcoming section we will see how the above call makes movie talk fully seamless inbetween two users, without Parse intervention.
Huh..the mammoth has been laid to rest, but there is still life in it. We already covered session generation part. But how does the other user know about it? And when exactly Opentok takes the charge to embark the titillating movie?
Step two – Treat Incoming Call:
Treating of an incoming call is tricky bit. Let’s list out the naked minimum necessities:
- You need to poll the database for a session destined to you (logged on user) – that is – search for an ActiveSessions record where current user is listed as receiver.
- You need to ensure that database is up-to-date once the call has been established – that is, eliminate the session row once sessionID and tokens have been read up into iPhone app
- You also need to signal interruptions while a session is ON – that is, inform the caller gracefully that the receiver is busy on another call. For plainness’s sake, we aren’t treating multi-user calls (conference) right now, albeit it can be treated fairly lightly.
Recall that in alertView:(UIAlertView *)alertView clickedButtonAtIndex , we eyed a call to [appDelegate fireListeningTimer] ,and now it is time to expand it, because it accomplishes our very first task of the three listed above: It fires a timer that continually polls Parse.com ActiveSessions table for calls destined to current user.
As it is named, [ParseHelper pollParseForActiveSessions] will poll ActiveSessions table for sessions calling out to this user – that is, rows which have receiverID = presently logged on user’s object ID.
The method is fairly self-explanatory – whenever it finds an ActiveSessions object, it just copies all the fields it needs – sessionID, publisherToken, and subscriberToken into app delegate’s properties. Once done, it deletes it from Parse.com backend using [self deleteActiveSession] call. [self setPollingTimer:NO] is to keep things in sync: it ensures that timer doesn’t fire up another polling query through pollParseForActiveSessions after an object has been found and deletion is in progress using [self deleteActiveSession] .
Once the ActiveSession values are copied to App’s delegate, more significant stuff is waiting: user needs to be notified of an incoming call. incomingCallAlert performs this task, and here is the result:
What’s more significant is incomingCallAlert’s delegate, which we already visited in Step one – let’s go over it again:
If user had not accepted the call, the polling timer flag is set and app starts to look for fresh incoming call session. If user rather determines to accept the call, kIncomingCallNotification is posted, and it is responsible for notifying LSViewController that a call has arrived. In fact, any view controller within your talk app should be able to receive this notification, so that you can treat the call irrespective of where you are in the app. Such notifications can be treated by having a UIViewController subclass from which all your view controllers can inherit. For simpleness, Livesessions only has one view controller that treats incoming call, and here is how:
didCallArrive fires in response to kIncomingCallNotification , and all it does it empty the m_receiverID to indicate that call is destined to self – an incoming call. This, as we already eyed in prepareForSegue – is enough to signal LSStreamingViewController that call is supposed to be treated as incoming call – so no fresh ActiveSessions object need to be stored. All that is left is to utilize the sessionID and token values to connect to Tokbox streaming server.
So far, we discussed both cases – in both we obtained sessionID, publisherToken and subscriberToken into our app’s delegate. We eventually passed the control over to LSStreamingViewController. In case of an outgoing call, [LSStreamingViewController sessionSaved] function calls [LSStreamingViewController connectWithSubscriberToken] . In case of incoming call, as we already witnessed in [LSStreamingViewController viewDidAppear] , a call is made to [LSStreamingViewController connectWithPublisherToken] .
The only difference inbetween two of them is the token they use – and Opentok isn’t fairly clear about what switches if you use one token instead of the other (publisher token or subscriber token – as you recall it is generated from cloud code in beforeSave trigger). Irrespective of which one you use to connect to a session, it permits you to publish your stream (your camera feed) as well as subscribe to other user’s stream.
Once [_session connectWithApiKey] call is made, Opentok takes over. All you need to recall is that your streaming view controller (LSStreamingViewController) must implement these protocols: OTSessionDelegate, OTSubscriberDelegate, OTPublisherDelegate. See the activity flow diagram up again – there, iOS app initiated deeds are listed in yellow, and delegates are marked in green. These delegate functions are part of these three protocols that LSStreamingViewController must implement. As they are called by Opentok along the flow, you need to take various deeds to make that enticing movie available to your user.
Now it no longer matters whether you are a caller or a receiver as far as you implement necessary delegate methods from Opentok. The Broadcast tutorial from Opentok has all implementation details, and I have followed it bit by bit, apart from my own UI modifications. For example, if you choose to view your own stream as soon as session gets connected, following code accomplishes it:
In the code above, [_session publish] call prompts the user to permit his / her own camera feed, and as soon as he / she permits it, LiveSessions commence publishing the camera feed to Opentok streaming server. A crucial chunk to recall here is the call to following:
SDK is designed such that without this, you never get to see your own feed. And no, any indirect method (e.g. addSubView to a container view) to set the framework doesn’t work. At the same time you can decorate your publisher view. For example, we have switched features like border color and corner radius.
Eyeing the feed of the other user in the same session is somewhat that doesn’t fall into any order. All you need to do is implement necessary delegates so that as soon as you embark receiving that feed, you get an chance to configure it fully – like this:
subscriberDidConnectToStream delegate permits you to configure your own subscriber view. Again, setFrame statement is crucial and if you don’t include it or do it with wrong values, you may never get to see other user’s feed – something that can break your (and your friends’) heart! Again, you can do your own UI modifications such as reporting the current status (using _statusLabel ) and decorating the subscriber view inwards the same delegate.
There are slew of other delegates that Opentok sdk provides that you can use to include various features to smarten your app.
For example, see how I chose to treat subscriber didFailWithError delegate:
There is much more inwards LSStreamingViewController that needs little or no explanation for someone who knows UIKit well. So we pridefully announce that the mammoth may have stopped breathing -you can see it yourself:
By the way, who is that insane soul screaming in the publisher view? LiveSessions isn’t that brainy – what it shows there is what your iPhone’s camera sees!
And yeah, if you didn’t notice, the sleeping animal is an elephant, not a mammoth. Mammoths existed only in ice age. So is this real?
Who knows, it’s just virtual. But so are nerds trolling in talk rooms.
All we need now is to discard the remnants to smoother the flow of our iPhone movie talk – let’s do them in one go.
The Cleanup:
As a VOIP app, LiveSessions must either keep running in the background or do its part of cleanup as soon as it comes in background. To preserve simpleness, I chose later. There are some rules laid by Apple to perform any task in background – be it trivial or not. Within LSAppDelegate.m, we cleanup our back end following those rules.
And we also wake up following those rules:
And here goes what we call in the above code:
Both delete functions do what is expected – they delete ActiveUsers and ActiveSessions object from Parse.com database. There is nothing unusual that they do. objectsUnderDeletion array is our way of keeping things in sync: when Parse.com is busy deleting stuff in background, it prevents our app from repeatedly firing delete guidelines.
Sign off and Providing Ins:
This tutorial and the code that comes with can serve as nude backbone to your next cutting edge messenger App. You can do your own customizations for Parse user management or UI layout and create your next killer iphone movie talk app.
Was this tutorial useful? Check out VideoStream SDK for iOS:
A powerful movie streaming Objective-C framework for iOS devices with support for numerous protocols & formats (mms, http, rtsp, rtmp, mjpeg). Built with FFmpeg, OpenGL ES Two.0 & Apple’s AudioUnit.
Creating an iPhone movie talk app using Parse and Opentok (tokbox) – Tutorial
Creating an iPhone movie talk app using Parse and Opentok (tokbox)
Creating an iPhone movie talk app isn’t rocket science, but there are a lot of intricacies to account for. This tutorial covers the entire purpose of developing such an app from commence to finish. It is fairly long, so you might want to bookmark it so you can revisit it later.
The end result: The entire project can be downloaded here. You can browse if you want to figure it out for yourself, for those who are interested in a detailed walkthrough, read on.
Movie talk is one of the most popular forms of communication on mobile, and is for productivity apps as well as social networking.
So how does one go about building such an app? The question is more of design than of technical implementation when many online services provide a lot of the infrastructure needed.
By the end of this tutorial, our app would look like this:
Yes, that is a monkey talking with a mammoth. Which is basically what we would be doing communicating with the services composing our app.
The Basics of a movie Talk app:
While the top movie talk application use their own streaming servers, we will be using a cloud-based solution to treat that part of the equation. Opentok is the service that we’ll be using in this tutorial, and it provides a nice, easy-to-integrate SDK for adding movie streaming capabilities to our app.
While Opentok provides their iOS API and iOS SDK with two useful examples of iOS implementation (here and here), there is still one lump left: User management. Opentok provides nice platform for movie talk, but inbetween whom? Let’s take a deeper dive into how streaming actually works inbetween users of services like Skype, MSN, and Yahoo movie talk.
A streaming server works on fundamentals of an Http web server: In a pretty raw representation, all it recognizes is request and response, without caring who sent it. An Http server does just that. It does not worry about states, userID, password or any such access token mechanism. In other words, session management is absent.
The screenplay switches when there arises a need to identify the user, authenticate him, and keep him authenticated during his entire browsing practice. A server needs to recall a user, and whatever other entities that come linked with him. These requirements become more stringent while developing a mobile software like iPhone movie talk, where identities are fairly crucial – they can make or break the authenticity of your app. In large web portals such as Yahoo.com or Amazon.com, dedicated authentication servers generate unique identity for users, and supply application servers with these goodies so that they can track the users until they log out.
To perform its task, a streaming server relies on only one entity to recognize who sent the request and whom to react – this entity is session ID. It does not recognize or want to care who generated it. As far as it receives a valid sessionID, it keeps replying to streaming requests. And here comes rule of thumb for any talking app:
Any user with a valid sessionID is, in principle, automatically entitled to view other user’s feed who is also using the same sessionID. Session IDs are of the form:
You most likely know where this is going: To keep track of who wants to connect to whom in a more real-worldly way, like we do in Yahoo Messenger or Skype, we need another server that keeps track of users. User management is essential in any social networking app. It depends on you how much you want to do it – you can store big data including address, phone numbers, profile pics and the likes, or you can pick 3-4 fields of your choice to make it lighter on the user. But the crux of the matter is you need to do it. Any software that relies on user interaction cannot do without it – and so is our iPhone movie talk.
To treat user management, we need a central server. To our excellent convenience, there are number of cloud solutions available again. For the purpose of this tutorial, I have chosen Parse.com because it boasts of 100k+ apps live, and is backed by Facebook. It claims easiest of the APIs, and those claims, as I have experienced, are right. What’s more, it’s cross platform, so if you plan to make Android or Windows mobile client for your movie talk app, you are in safe mitts.
The objectives of this tutorial are to display:
- How to enable Parse.com users see each other (Your beloved messenger’s who’s online sort)
- How to make them talk with each other using Opentok movie streaming feature in your next fine iphone movie talk app
Tokbox provides a Broadcast tutorial which contains some of what we need for a fully featured chap app. The main difference is that we’re creating a two-way talk instead of a one-to-many broadcast system. I’ll also attempt to simplify the process so that even novice developers should be able to finish it without much trouble.
Setup (Parse.com and Tokbox.com) for your iPhone movie talk app – LiveSessions:
My showcase app – Livesessions – requires some configuration on both Tokbox.com and Parse.com profiles – the server side. Parse.com needs your app, and so does Tokbox.com, albeit it names it a Project.
I assume you know enough to configure an app on Parse.com. In addition to it, you also need two data tables – ActiveUsers and ActiveSessions, however there is no need to pre-populate them at any point. Here is a snap of what their column lists will look like:
- callerID – String
- callerTitle – String
- receiverID – String
- sessionID – String
- publisherToken – String
- subscriberToken – String
- isVideo – Boolean
- isAudio – Boolean
Similarly, on Tokbox.com, once you login, go to dashboard and create fresh project, it will automatically create an API key and API secret. Note that these are your credential as a Tokbox developer, not a talk user. API key is more like your user ID and API secret is a password.
The resulting Tokbox dashboard screen will look like this (the real API key and text are hidden):
iPhone movie talk
There is one, crucial lump left to be done to finish the server side. As we discussed earlier, we need to connect our streaming application server (Tokbox.com) with authentication server (Parse.com). Unless this is done, streaming server has no way to know which user is calling whom because all it knows about is session ID. Our iPhone movie talk user, on the other arm, only knows about his own user credentials supplied to him by Parse.com.
As it is apparent, it is Parse.com’s job to connect the two. That is:
- To intercept logged in user’s request to talk (the caller).
- To convert it into Tokbox session ID, get the session ID and other necessary information (the token) from Tokbox.
- To send the caller and receiver (Parse.com users) the sessionID.
- Since both users now have a session ID and a token which is received from Parse.com, they can seamlessly communicate via Tokbox streaming server.
To accomplish above, Parse.com must obtain sessionID, publisherToken and subscriberToken from tokbox, and that is where Parse cloud code comes into picture. In Parse Cloud code section, you must upload some code so that the resulting screen looks like this:
The process of uploading (oops, deploying) cloud code on Parse.com is described in this source tutorial (Setting Up section) and here, and it is far better than I could explain here. So let’s skip it to maintain the scope. However for simpleness’s sake, I have included it step by step in readme.txt that comes within the code.
I would still take a few moments to explain what this code does, and why. The iPhone app user initiates a call to another user. No, he does not make a phone call. Recall, it’s LiveSession app’s job to treat the entire call, just like Skype or Yahoo messenger does. What our iPhone app needs to do under the rubber hood is fairly elementary task: it is saving a row to ActiveSessions Parse table we just created. There, it stores caller user ID (callerID) and receiver user ID (receiverID) among other things.
Now what this cloud code does is something fairly magical, yet plain. It intercepts the Save operation in its beforeSave cloud trigger – nicely elaborated here. From within beforesave, Opentok javascript API takes over. Opentok supplied function createSession generates a session ID. Another function, Opentok.generateToken, creates a publisher token or subscriber token, depending on the role argument passed, which determines whether you want to publish your own movie feed (Opentok.ROLE.PUBLISHER) or see other user’s movie feed (Opentok.ROLE.SUBSCRIBER) using that session ID. (It is unclear from my practice if there is any difference inbetween the two. For the scope of this discussion let us generate both as we need two-way movie feeds anyway.)
Since we must recall we are within beforeSave trigger, we already have the treat to the object being saved – ActiveSessions. By simply setting its respective members – we can save three distinct items to our Parse.com database: sessionID, publisherToken and subscriberToken – all three columns in ActiveSession table.
Well – that’s for that. What? You are done with the back end of your very first iPhone movie talk app! Congratulations!
But how? All we required for two user’s to connect via streaming server is a user id (session id) and a password (token) – and we have both. Now all we have to do is – avail them to iOS client via Parse.com. Not that it’s fairly effortless as 1-2-3, but the mammoth has bitten the dust already.
Serve yourself a cup of hot coffee as you wait to see one of your friends on your iPhone movie app screen! Well, not fairly quickly, but before you hop in, you can do them some favor: read this disclaimer if it can help any of your Android friends:
iOS client – the other part of the deal:
Now that the back end is accomplished, let’s concentrate on how iOS client – our own LiveSession iPhone app – keeps its part of the deal. The major tasks we aim to cover are:
1) Initiate iPhone movie talk call – by saving an ActiveSession row (we already described the server part of it above as part of cloud code)
Two) Treat Incoming Call – check Parse.com database for an incoming movie call – described down the line.
Let’s tackle each of them – one by one. But very first and foremost, let’s setup the basic iOS project and go over what’s all needed.
iPhone Movie Talk Initial Project Setup:
Open XCode, setup a single View application with default options. Name it LiveSessions. In storyboards, set up two scenes: One for users list – named LSViewController (derived from UIViewController), and another for hosting movie talk view, LSStreamingViewController (again, subclass of UIViewController).
Next, add a UITableView object to LSViewController scene through storyboard. This table view must maintain a list of Parse.com users who log into LiveSession app any time. LSViewController will treat all the chores related to UITableView, nothing unusual. Do not leave behind to implement UITableViewDelegate and UITableViewDatasource protocols in LSViewController so as to treat table view related stuff.
In addition to the above, we also need a helper class called ParseHelper which wraps our calls to Parse. All of its members and functions would be static.
To use Parse and Opentok framework, we need to link them, as well as some of the required frameworks used by both of them.
Parse framework can be obtained from here.
Opentok SDK can be obtained from here. This link also explains at length what all you need to do in order to link Opentok framework successfully.
Next, you should add some libraries to LiveSessions by selecting it, going to Build Phases->Link Binary section. After adding number of frameworks, this section should look like this:
At the end of linking everything, your Project tree should look like this:
(Notice the Opentok.bundle thing that come as part of Opentok sdk. Also see three other libraries that go below it in order to link everything together).
Before we proceed, let’s have one look at how beautiful (!) our storyboard looks:
Step one – Initiate Movie Call:
Parse.com acts as a mediator inbetween caller, Opentok streaming engine and receiver. The very first and foremost requirement is to generate sessionID, publisherToken and subscriberToken, so that both clients can seamlessly connect via Opentok once session is established. We already know the Parse.com cloud code does that. But how will LiveSessions app invoke the cloud code?
The following code not only stores an ActiveSessions object to Parse, but also invokes cloud code (beforeSave trigger) we discussed above that generates sessionID, publisherToken and subscriberToken – and they are eventually stored into ActiveSessions table itself.
At the end of executing the above code, we should have a sessionID, publisherToken as well as subscriberToken in our Parse.com ActiveSessions table. Alright, but who will execute it? Lot of stuff still remain unanswered – for example, from where does all the argument values (receiverID, callerID) come from? We deliberately missed that part, because establishing the session was most significant. The callerID, receiverID parameters that we used above are actually just the user IDs generated by Parse.com PFUser object. You can have your own way of registering and authenticating a user. In LiveSessions, we just store each user within ActiveUsers table, and only using a user title of his / her own choice. No emails, passwords or verification. And here is code that is responsible for it:
What this does is elementary: when the app launches, check for the locally stored Parse user ( [PFUser currentUser] ), and if one does not exist, perform anonymous login, which will create a PFUser object on Parse.com Users table. What is significant to us is loggedInUser static object that we use to store presently logged on user. At the end of successful login, showUserTitlePrompt function prompts the user to come in a title of his / her choice.
Fine, but what happens when user comes in it? Well, significant number things. For a embark, here is how LiveSessions treats it:
Notice the part under tag kUIAlertViewTagUserName . This code tells LiveSessions that user is now fully logged in, along with an identification (title) of his / her choice. This title will be eventually stored into ActiveUsers table as userTitle, but with one more thing: user’s current location. Yes, LiveSessions is a location-aware app. And to obtain user’s location, ParseHelper.m posts a kLoggedInNotification notification to LSViewController. LSViewController has CLLocationManager code inwards it which will track user’s current location. At the end, once we have everything, the entire user (his title, user ID and location) are saved into ActiveUsers table.
Here is what goes inwards LSViewController to obtain user’s current location, and call to Parse wrapper for storing it to ActiveUsers table:
The very first unknown in above code so far is call to fireNearUsersQuery function,which serves front end. We will come to it later. The other unknown is saveUserWithLocationToParse function, which will pack the gaps left so far to finish the back end. It belongs to ParseHelper.m, and here it goes – there is nothing unusual about storing it, and it acts as our own little user repository. The generated user’s object ID is stored for later use inwards activeUserObjectID .
The code so far ensured a user is saved inwards ActiveUsers table. We also eyed how he / she can initiate a movie call to another user, by creating an ActiveSessions object. But whom does the user talk with?
We must also present a list of users to logged on user to talk with – equivalent of Yahoo/Skype friend’s list. Sending friend requests through email or any other means would be fairly an overkill for our tutorial’s scope. To keep things minimal, we don’t even ask our users to inject their email ID for registration.
Instead, we have chosen a unique way to test out movie talk feature: demonstrate list of users who are geographically within specified radii – say two hundred miles. Parse.com already has PFGeopoint related query mechanism which makes our task lighter.
The other unknown in code above, fireNearUsersQuery goes as below, and it fills up the datasource for the LSViewController table view – an NSMutableArray made of dictionaries packed with user’s titles:
The result of fireNearUsersQuery call will be somewhat like below, where three nearby users (<200 miles radii) are visible for talk:
Inwards LSViewController, the m_userTableView gets populated from m_userArray . Each row in the table view has a green Call button. When you tap that button, call is initiated for that user as the receiver ID. What call? The code we just covered to store the session: saveSessionToParse . Who calls it? Well, now it’s time the movie talk scene (LSStreamingViewController) takes charge.
Before proceeding, take a look at this activity flow – the big picture. You will come back to it fairly often as you read on:
Upon tapping of the green phone call button, a segue is performed to transition to LSStreamingViewController. Inwards LSStreamingViewController, [ParsHelper saveSessionToParse] is called. Here is that part:
Once inwards LSStreamingViewController:
As a matter of its duty, LSStreamingViewController treats both outgoing and incoming calls. To differentiate the two, it uses receiver ID ( self.callreceiverID ): For outgoing calls, it has a value supplied from LSViewController (see the segue transition code). For incoming calls, there is no need for it so it is null or empty.
As soon as saveSessionToParse saves ActiveSessions object to Parse.com database, it notifies LSStreamingViewController so that sessionID, publisherToken and subscriberToken values from Opentok (that became available to app’s delegate) can be usable to LSStreamingViewController. This notification ( kSessionSavedNotification ) is treated by sessionSaved like this:
In forthcoming section we will see how the above call makes movie talk fully seamless inbetween two users, without Parse intervention.
Huh..the mammoth has been laid to rest, but there is still life in it. We already covered session generation part. But how does the other user know about it? And when exactly Opentok takes the charge to begin the arousing movie?
Step two – Treat Incoming Call:
Treating of an incoming call is tricky bit. Let’s list out the nude minimum necessities:
- You need to poll the database for a session destined to you (logged on user) – that is – search for an ActiveSessions record where current user is listed as receiver.
- You need to ensure that database is up-to-date once the call has been established – that is, liquidate the session row once sessionID and tokens have been read up into iPhone app
- You also need to signal interruptions while a session is ON – that is, inform the caller gracefully that the receiver is busy on another call. For simpleness’s sake, we aren’t treating multi-user calls (conference) right now, albeit it can be treated fairly lightly.
Recall that in alertView:(UIAlertView *)alertView clickedButtonAtIndex , we witnessed a call to [appDelegate fireListeningTimer] ,and now it is time to expand it, because it accomplishes our very first task of the three listed above: It fires a timer that continually polls Parse.com ActiveSessions table for calls destined to current user.
As it is named, [ParseHelper pollParseForActiveSessions] will poll ActiveSessions table for sessions calling out to this user – that is, rows which have receiverID = presently logged on user’s object ID.
The method is fairly self-explanatory – whenever it finds an ActiveSessions object, it just copies all the fields it needs – sessionID, publisherToken, and subscriberToken into app delegate’s properties. Once done, it deletes it from Parse.com backend using [self deleteActiveSession] call. [self setPollingTimer:NO] is to keep things in sync: it ensures that timer doesn’t fire up another polling query through pollParseForActiveSessions after an object has been found and deletion is in progress using [self deleteActiveSession] .
Once the ActiveSession values are copied to App’s delegate, more significant stuff is waiting: user needs to be notified of an incoming call. incomingCallAlert performs this task, and here is the result:
What’s more significant is incomingCallAlert’s delegate, which we already visited in Step one – let’s go over it again:
If user had not accepted the call, the polling timer flag is set and app starts to look for fresh incoming call session. If user rather determines to accept the call, kIncomingCallNotification is posted, and it is responsible for notifying LSViewController that a call has arrived. In fact, any view controller within your talk app should be able to receive this notification, so that you can treat the call irrespective of where you are in the app. Such notifications can be treated by having a UIViewController subclass from which all your view controllers can inherit. For plainness, Livesessions only has one view controller that treats incoming call, and here is how:
didCallArrive fires in response to kIncomingCallNotification , and all it does it empty the m_receiverID to indicate that call is destined to self – an incoming call. This, as we already eyed in prepareForSegue – is enough to signal LSStreamingViewController that call is supposed to be treated as incoming call – so no fresh ActiveSessions object need to be stored. All that is left is to utilize the sessionID and token values to connect to Tokbox streaming server.
So far, we discussed both cases – in both we obtained sessionID, publisherToken and subscriberToken into our app’s delegate. We eventually passed the control over to LSStreamingViewController. In case of an outgoing call, [LSStreamingViewController sessionSaved] function calls [LSStreamingViewController connectWithSubscriberToken] . In case of incoming call, as we already witnessed in [LSStreamingViewController viewDidAppear] , a call is made to [LSStreamingViewController connectWithPublisherToken] .
The only difference inbetween two of them is the token they use – and Opentok isn’t fairly clear about what switches if you use one token instead of the other (publisher token or subscriber token – as you recall it is generated from cloud code in beforeSave trigger). Irrespective of which one you use to connect to a session, it permits you to publish your stream (your camera feed) as well as subscribe to other user’s stream.
Once [_session connectWithApiKey] call is made, Opentok takes over. All you need to reminisce is that your streaming view controller (LSStreamingViewController) must implement these protocols: OTSessionDelegate, OTSubscriberDelegate, OTPublisherDelegate. See the activity flow diagram up again – there, iOS app initiated deeds are listed in yellow, and delegates are marked in green. These delegate functions are part of these three protocols that LSStreamingViewController must implement. As they are called by Opentok along the flow, you need to take various deeds to make that enticing movie available to your user.
Now it no longer matters whether you are a caller or a receiver as far as you implement necessary delegate methods from Opentok. The Broadcast tutorial from Opentok has all implementation details, and I have followed it bit by bit, apart from my own UI modifications. For example, if you choose to view your own stream as soon as session gets connected, following code accomplishes it:
In the code above, [_session publish] call prompts the user to permit his / her own camera feed, and as soon as he / she permits it, LiveSessions commence publishing the camera feed to Opentok streaming server. A crucial lump to recall here is the call to following:
SDK is designed such that without this, you never get to see your own feed. And no, any indirect method (e.g. addSubView to a container view) to set the framework doesn’t work. At the same time you can decorate your publisher view. For example, we have switched features like border color and corner radius.
Eyeing the feed of the other user in the same session is somewhat that doesn’t fall into any order. All you need to do is implement necessary delegates so that as soon as you commence receiving that feed, you get an chance to configure it fully – like this:
subscriberDidConnectToStream delegate permits you to configure your own subscriber view. Again, setFrame statement is crucial and if you don’t include it or do it with wrong values, you may never get to see other user’s feed – something that can break your (and your friends’) heart! Again, you can do your own UI modifications such as reporting the current status (using _statusLabel ) and decorating the subscriber view inwards the same delegate.
There are slew of other delegates that Opentok sdk provides that you can use to include various features to smarten your app.
For example, see how I chose to treat subscriber didFailWithError delegate:
There is much more inwards LSStreamingViewController that needs little or no explanation for someone who knows UIKit well. So we pridefully proclaim that the mammoth may have stopped breathing -you can see it yourself:
By the way, who is that insane soul screaming in the publisher view? LiveSessions isn’t that wise – what it shows there is what your iPhone’s camera sees!
And yeah, if you didn’t notice, the sleeping brute is an elephant, not a mammoth. Mammoths existed only in ice age. So is this real?
Who knows, it’s just virtual. But so are nerds trolling in talk rooms.
All we need now is to discard the remnants to smoother the flow of our iPhone movie talk – let’s do them in one go.
The Cleanup:
As a VOIP app, LiveSessions must either keep running in the background or do its part of cleanup as soon as it comes in background. To preserve plainness, I chose later. There are some rules laid by Apple to perform any task in background – be it trivial or not. Within LSAppDelegate.m, we cleanup our back end following those rules.
And we also wake up following those rules:
And here goes what we call in the above code:
Both delete functions do what is expected – they delete ActiveUsers and ActiveSessions object from Parse.com database. There is nothing unusual that they do. objectsUnderDeletion array is our way of keeping things in sync: when Parse.com is busy deleting stuff in background, it prevents our app from repeatedly firing delete directions.
Sign off and Providing Ins:
This tutorial and the code that comes with can serve as naked backbone to your next cutting edge messenger App. You can do your own customizations for Parse user management or UI layout and create your next killer iphone movie talk app.
Was this tutorial useful? Check out VideoStream SDK for iOS:
A powerful movie streaming Objective-C framework for iOS devices with support for numerous protocols & formats (mms, http, rtsp, rtmp, mjpeg). Built with FFmpeg, OpenGL ES Two.0 & Apple’s AudioUnit.