In mobile development, developers often get tasked with integrating third-party social platforms like Facebook, Twitter, or YouTube into the applications they build. Clients usually want ways to aggregate their social content into whatever app they build (pulling content in) or to promote other users to share their content within their respective social circles (pushing content out).
Most of these social platforms provide convenient SDKs (Software Development Kits) that allow you to integrate with their services easier. These SDKs are platform-specific tools and components that help expedite the development process by preventing the need to build out particular features from scratch. Additionally, they also have platform-independent APIs that can be queried using the proper application authentication. These APIs can return a multitude of data from within the system, about which you'll discover more in this article.
Throughout the course of this article, you'll focus on one of these platforms in particular: YouTube. You'll learn how to create and register an application and application key, how to pull content from YouTube via their public API, and how to go through the process of integrating their native video player component for playing back YouTube content from within your application.
The YouTube API
YouTube currently has over 1 billion users and serves up millions of hours of content on a daily basis. They reach more 18-to-49 year-olds than any cable network in the U.S. ( www.youtube.com/yt/press/statistics.html ). As a developer, you can take advantage of this content and integrate many of the site's features into your own applications using the provided developer tools and APIs that the service offers.
For the purposes of this demonstration, you'll work with the core platform API to learn how to retrieve a subset of videos from YouTube matching a search query that you define. You'll also learn about various ways to filter the results returned via the query parameters. Lastly, you'll implement the YouTube-provided native iOS video player to play the content that you retrieve. You can access the documentation for the various API endpoints offered, including ones not covered in this article, at https://developers.google.com/youtube/.
YouTube currently has over 1 billion users and serves up millions of hours of content on a daily basis.
API Usage
The YouTube API is free to use for most applications. Larger applications with bigger user bases may need to apply for a higher quota to satisfy its needs, but in most cases, the free tier should suffice. The free tier has the following parameters:
- 50,000,000 units per day
- 3,000 requests per user, per second
- Daily quotas reset at midnight Pacific Time
It's important to note that “units” are a proprietary metric used by Google and are not a one-to-one relationship to the number of API calls that your application makes. Each different API endpoint cost a specific number of units per call. For instance, the YouTube search API demonstrated in this article costs 100 units per call.
It's important to note that “units” are a proprietary metric used by Google and are not a one-to-one relationship to the number of API calls that your application makes.
Before you can begin using the API, you must register your application and enable access to the YouTube API from the Google Developer Console. To do this, first navigate to https://console.developers.google.com. Once there, sign-in with your Google account. After a successful sign-in, you'll arrive at the console dashboard. From the dashboard, complete the following steps:
- To the left of your profile avatar at the top right of the page, find the drop-down menu with the “Create a Project” option. Click that.
- A dialogue box appears with a prompt for the project name. Fill in the name of the project and click “Create”. The dashboard automatically switches to the new project that you just created.
- You should now see a blue dialogue box that says “Use Google APIs” with an option that says, “Enable and manage APIs”. You need to go here to enable YouTube. Click on that option.
- Now you can see a list of several different services and APIs that Google offers. Find the one on the far right entitled “YouTube Data API”. Click that option and, on the next screen, press “Enable API”.
At this point, you've enabled YouTube Data API access for your application, but your application still needs a valid API key so that Google can verify your app's identity when it makes an API call. The level of authentication that your application needs to access the API can vary based on the functionality that it wants to take advantage of. Three levels of access exist that your application can generate credentials for (also shown in Figure 1):
- API Key: Identifies your project using a simple API key to check quota and access. This is the level of access you�ll use later in this article.
- Oauth 2.0 Client ID: Requests user consent so that your app can access the user�s data. It�s needed to access account-specific details and APIs.
- Service Account: Enables server-to-server, app-level authentication using robot accounts. It�s mainly for use with Google Cloud APIs.
On the screen where you enabled YouTube API access, there's a menu on the left with an option titled “Credentials”. Go here to generate the API Key that you need. Once there, drop down the “Add Credentials” option, as shown in Figure 1, and select “API Key”.
Once selected, you'll receive a modal that asks you to identify your application's platform. Select “iOS Key” from the available options to continue. Next, name the key however you see fit and then input the application's Bundle ID
: I used com.codemagazine.YTDemo
for the purposes of this demonstration. Lastly, a dialogue should appear with your API key listed in it. Copy this ID because you'll need it when you begin working through the code samples later in this article.
The Endpoint
For this demonstration, you'll work with the YouTube search API to find videos that relate to a search query that you define. The base API request URL to search YouTube videos looks like the following:
https://www.googleapis.com/youtube/v3/search
In order to call that endpoint and retrieve the relative data, you need to append several parameters to the request. The required parameters for this call include the search query, the API key, and a “part” parameter that indicates the search properties that you'll get back in the response. The “part” parameter for a query to the search endpoint should be set to “snippet.” The following code sample demonstrates what you would append to the above URL in order to search for videos using the keyword “programming”.
?q=programming&part=snippet&key=
{INSERT YOUR API KEY HERE}
Additionally, there's a host of optional parameters that you can append to the URL to filter or organize the results to your liking. The following list describes some of the more useful ones (full documentation on parameters can be found at https://developers.google.com/youtube/v3/docs/search/list).
- channelId: Search only returns matched content created by the specified channel.
- location & locationRadius: Together, these two parameters let you limit search results to a specific geographical area.
- maxResults: The maximum total items returned from the query. Acceptable values range between 0-50 (inclusive).
- type: Set to “channel,” “playlist,” or “video.” Defines the type of content returned by the search.
- regionCode: Limits results to a specific country.
- relevanceLanguage: Returns results most relevant to the indicated language. The parameter is typically an ISO 639-1 two letter language code (en for “English” for instance).
- order: Defines the order of the search results. Set to “date,” “rating,” “relevance,” “title,” “videoCount,” or “viewCount.”
For the purposes of this article, request videos on “programming” in English from the search API. Using the parameters described above, that full request looks like this:
https://www.googleapis.com/youtube/v3/search?
part=snippet®ionCode=US&relevanceLanguage=en
&type=video&order=relevance&maxResults=20
&q=programming&key={YOUR API KEY}
The Data
After sending a search request to the API, the service responds with the resulting matches. The response format is JSON and following snippet demonstrates the structure of the returned data.
{
"kind": "youtube#searchListResponse",
"etag": etag,
"nextPageToken": string,
"prevPageToken": string,
"pageInfo": {
"totalResults": integer,
"resultsPerPage": integer
},
"items": [
search Resource
]
}
The “items” array contains the matched results. Each individual item in that array has a set of data that describes it. Listing 1 demonstrates the format of each item. Additionally, notice the inclusion of next and previous page tokens. The maxResults
parameter described earlier determines the number of items initially returned (if not specified, the default is 5). However, in most cases, the total number of matching items exceeds the maxResults
parameter. In those cases, you can use the next and previous page tokens to paginate through the entirety of the results.
Listing 1: JSON format of video items returned from the YouTube search API
{
"kind": "youtube#searchResult",
"etag": etag,
"id": {
"kind": string,
"videoId": string,
"channelId": string,
"playlistId": string
},
"snippet": {
"publishedAt": datetime,
"channelId": string,
"title": string,
"description": string,
"thumbnails": {
(key): {
"url": string,
"width": unsigned integer,
"height": unsigned integer
}
},
"channelTitle": string,
"liveBroadcastContent": string
}
}
Referring again to Listing 1, notice that the information returned for each video includes channel information, video title, video description, and video thumbnails among other things. In this demonstration, however, you'll only use the “videoId” property, which gets used to play the video.
YouTube iOS Player
Currently, you can't play a YouTube video directly from an iOS application using an AVPlayer
object. The only method currently supported by YouTube involves embedding an iFrame container inside your application to load the video. The user then plays the video from this embedded iFrame and, as is the case with any Web-based-video, the operating system's standard video player takes over once the video begins.
YouTube released an iOS library to simplify this process called YTPlayer (https://github.com/youtube/youtube-ios-player-helper). Rather then going through the hassle of implementing a UIWebView
and then adding the HTML iFrame code to it directly, you can use the YTPLayer library's provided player view to handle this implementation for you. Simply initialize an instance of YTPlayerView
and tell it to load the respective video by passing it a video ID. The sample application you'll build in the next section demonstrates how to add this library to your application and take advantage of it.
Let's Code It
In this code exercise, you'll write a VideoManager
class that can fetch videos from YouTube using the search API. The VideoManager
class uses callbacks to return an NSArray
of videos. You'll also write a Video
object that models the video data returned. This portable library can get dropped into any project that needs to fetch and play videos from YouTube.
Getting Started
The first thing you'll want to do is create a new Xcode project for this exercise. To do this, launch Xcode and click Create a new Xcode project. Select Single View Application and press Next. Finally, fill in a project name and match the other settings to the screenshot from Figure 2.
Once your settings match Figure 2, click Next. You should now have a blank Xcode project. The only other thing you need to set up before you get coding is YTPlayer. Add the YTPlayer library to the project using CocoaPods, one of the simplest methods to add third-party content to any iOS project. For the sake of this walkthrough, I'll assume that you have a working understanding of how to install a library using CocoaPods. (If not, please refer to the sidebar for additional information on how to use CocoaPods or where you can locate additional installation instructions.) Once you've created a Podfile, add the following line to the file and save.
pod "youtube-ios-player-helper", "~> 0.1.4"
Once saved, launch terminal and navigate to the root folder for your project. Make sure that the project is closed in Xcode and run the following command in terminal.
pod install
This automatically downloads the YTPlayer library and creates an .xcworkspace
file. Re-open your project by double clicking that new project file.
Video Model
The next thing you want to add to the project is a video
object model that holds the information you receive from the call to the YouTube search API. Click File > New > File and then choose Cocoa Touch Class. Next, choose NSObject and name the object “Video”, as shown in Figure 3.
Once that has been created, you'll need to add various properties to correspond to the video data you want to collect from the API response. Although you'll only use the video ID
property in this example, the sample code captures additional details to further demonstrate how to interact with the API. Additionally, you can follow the same structure to store any of the remaining data points that don't get picked up in this sample.
Open the Video.h
file that you just generated. Add the properties for the data you'll track, as shown in Listing 2. These properties map to the various data points that get returned by the API and the comments from Listing 2 offer more detail on what each property represents.
Listing 2: Video.h � variable parameters
@interface Video : NSObject
/// title of the video as shown in YouTube
@property (nonatomic, strong) NSString* videoTitle;
/// id of the video used to load the video in the iFrame
@property (nonatomic, strong) NSString* videoID;
/// the name of the channel the video is posted in (author name
@property (nonatomic, strong) NSString* channelTitle;
/// id that can be used to load further content from the channel
@property (nonatomic, strong) NSString* channelID;
/// snippet of the video description from YouTube
@property (nonatomic, strong) NSString* videoDescription;
/// url to the cover thumbnail of the video
@property (nonatomic, strong) NSString* thumbnailURL;
/// date the video was posted on YouTube
@property (nonatomic, strong) NSDate* pubDate;
@end
The next step involves creating the video manager class that gathers the information needed to populate this video
object. That manager class retrieves the video data from the search query as a JSON object. Once you cover that in the next section, you'll loop back and create an initializer method on this video model to take the JSON data and parse it into the appropriate properties you just created.
Video Manager
Just like you previously did with the video model, go to File > New > File and select NSObject to create a new class. Name it VideoManager and click Next and then Save. This VideoManager
class directly integrates with the YouTube search API endpoint described earlier to get the desired video information. NSURLSession
handles the call for data, so you need to write a function that uses a session to make a request to the service. Listing 3 demonstrates this function implementation in VideoManager.m
and how to use NSURLRequest
and NSURLSession
to formulate the query.
Listing 3: VideoManager.m � send API request to specified URL
@implementation VideoManager
/**
* Search programming videos on Youtube
*
* @param channelID ID of channel to only search on
* @param completionBlock Returns array of search results
*/
- (void)getVideos:(NSString*)channelID completionBlock:
(void (^)(NSMutableArray *))completionBlock {
// The API Key you registered for earlier
NSString* apiKey = @"YOUR API KEY";
// only used if we want to limit search by channel
NSString* optionalParams = @"";
// if channel ID provided, create the paramter
if(channelID){
optionalParams = [NSString
stringWithFormat:@"&channelId=%@", channelID];
}
// format the URL request to the YouTube API:
// max results set to 20, language set to english,
// order by most relevant
NSString* URL = [NSString
stringWithFormat:@"https://www.googleapis.com/youtube
/v3/search?part=snippet®ionCode=US
&relevanceLanguage=en&$type=video&order=relevance
&maxResults=20&q=%@&key=%@&alt=json%@",
@"programming", apiKey, optionalParams];
// initialize the request with the encoded URL
NSURLRequest *request = [[NSURLRequest alloc]
initWithURL:[NSURL URLWithString:[URL
stringByAddingPercentEncodingWithAllowedCharacters:
[NSCharacterSet URLQueryAllowedCharacterSet]]]];
// create a session and start the request
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (!error){
Video* vid = [[Video alloc] init];
// create an array of Video objects from
// the JSON received
[vid getVideoList:[NSJSONSerialization
JSONObjectWithData:data
options:0 error:nil] completionBlock:
^(NSMutableArray * videoList) {
// return the final list
completionBlock(videoList);
}];
}
else {
// TODO: better error handling
NSLog(@"error = %@", error);
}
}] resume];
}
@end
Notice that the function definition takes an optional channelID
parameter. If included, the search results only contain videos from the specified ID. Also notice that there is a callback block that passes an NSMutableArray
to the calling function. That array contains the video objects returned by the search query.
You've now enabled the video manager class to communicate with the API. Next, you need to add the getVideoList
function to the Video
model object that you previously created. Notice that this function gets called in the Listing 3 code you just wrote. It creates an array of video objects using the JSON response from YouTube. Before doing that, switch over to VideoManager.h
and add the function definition for what you just wrote in Listing 3.
@interface VideoManager : NSObject
- (void)getVideos:(NSString*)channelID
completionBlock:(void (^)(NSMutableArray *))
completionBlock;
@end
Lastly, you want to import the Video
object or else the call to getVideoList
in VideoManager.m
triggers a build error. Add the import to the top of VideoManager.h
.
#import "Video.h"
Now you can move back to the model and add the functions needed to parse the JSON information into manageable video objects. The next section covers this.
Back to the Video Model
You've almost completed the circle. You started by creating a video model object, wrote a manager class to get the data to populate that object, and now you need the final logic that maps that data from the API results into the model. Move back to your Video.h
file and declare the following function header.
/**
*create array of video objects
*using the JSON data passed in the
*dictionary parameter
*
* @param videoData JSON data from API
* @param completionBlock array of video objects
*/
- (void)getVideoList:(NSDictionary*)videoData
completionBlock:(void (^)(NSMutableArray *))
completionBlock;
This is the function that the VideoManager
class passes the returned JSON data to as described earlier. Listing 4 demonstrates the implementation of this function that you add to Video.m
. That function depends on an initialization function that also gets added to the Video.m
file. Listing 5 shows that initialization function, which takes in a single video item as JSON data in dictionary format. It parses out the needed properties and then returns an instance of self. This gets called for each video in the videoData
that JSON provided to getVideoList
. All of the created instances of video get returned to VideoManager.m
in the completion block as an array.
Listing 4: Video.m – parse search query JSON into video objects
/**
* create array of video objects
* using the JSON data passed in the
* dictionary parameter
*
* @param videoData JSON data from API
* @param completionBlock array of video objects
*/
- (void)getVideoList:(NSDictionary*)videoData
completionBlock:(void (^)(NSMutableArray *))
completionBlock {
// get the array of videos from the dictionary containing
// the JSON data returned from the call to the YouTube API
NSArray *videos = (NSArray*)[videoData objectForKey:@"items"];
NSMutableArray* videoList = [[NSMutableArray alloc] init];
// loop through each video in the array, if it has an ID
// initialize an instance of self with the relative
// properties
for (NSDictionary *videoDetail in videos) {
if (videoDetail[@"id"][@"videoId"]){
[videoList addObject:[[Video alloc]
initWithDictionary:videoDetail]];
}
}
// pass the array of video objects back to VideoManager.m
completionBlock(videoList);
}
Notice that Listing 5 has a call to dateWithJSONString
. This helper function takes a date string from the JSON response and converts it into an NSDate
object, a more manageable format for your application. That function should get created within an NSString
category and its implementation is demonstrated in Listing 6.
Listing 5: Video.m – initialization function
- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
self = [super init];
if (self) {
_videoTitle = dictionary[@"snippet"][@"title"];
_videoID = dictionary[@"id"][@"videoId"];
_channelID = dictionary[@"snippet"][@"channelId"];
_channelTitle = dictionary[@"snippet"][@"channelTitle"];
_videoDescription = dictionary[@"snippet"][@"description"];
_pubDate = [dictionary[@"snippet"][@"publishedAt"]
dateWithJSONString];
_thumbnailURL = dictionary[@"snippet"][@"thumbnails"]
[@"high"][@"url"];
}
return self;
}
Listing 6: NSString utility function that converts a string to an NSDate
/*
* take the current string and converts it into a manipulatable
* NSDate object and then returns that object
*/
- (NSDate*)dateWithJSONString {
[NSDateFormatter setDefaultFormatterBehavior: NSDateFormatterBehavior10_4];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSz"];
[dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[dateFormatter setCalendar:[[NSCalendar alloc]
initWithCalendarIdentifier:NSCalendarIdentifierGregorian]];
NSDate *date = [[NSDate alloc] init];
date = [dateFormatter dateFromString:self];
return date;
}
You have now completed both the VideoManager
and Video
classes. Simply initialize a VideoManager
object from anywhere in your application and call getVideos
to retrieve the desired video information.
The Last Piece to the Puzzle
The final piece of the puzzle involves the YTPlayer
class that you added to the project earlier. Using the code you already wrote in Listings 1-6, you can get YouTube videos by adding the following code to any controller in your application.
NSMutableArray *ytVideos = [[NSMutableArray alloc] init];
VideoManager* vidManager = [[VideoManager alloc] init];
[vidManager getVideos:nil
completionBlock:^(NSMutableArray *videoList) {
ytVideos = [[videoList
sortedArrayUsingDescriptors:[NSArray
arrayWithObject:[[NSSortDescriptor alloc]
initWithKey:@"pubDate" ascending:NO]]]
mutableCopy];
}];
The previous code initializes an instance of the VideoManager
class and makes a request to get videos. Once the videos return, they get sorted by publication date and stored in a local array object where you can do with it what you please. Now you have the video results and you want to display them so your users can play them. To do this, you first want to import YTPlayer
as follows.
#import "YTPlayerView.h"
Next, you create an instance of the player and load the video using the video ID. The next code snippet demonstrates how this is done by taking the first video
object from the previous code's result array, ytVideos
, and creating a YTPLayerView
to display it.
Video *video = [ytVideos objectAtIndex:0];
// initialize a player with the frame that you
// want the video preview to be initially
// displayed in.
// Can also set the view up in Interface Builder
// using auto-layout and use an IBOutlet to
// connect to your instance of YTPlayerView
YTPlayerView *videoPlayerView = [[YTPlayerView alloc] initWithFrame:
CGRectMake(0, 0, 375, 320)];
[videoPlayerView loadWithVideoId:video.videoID];
[self.view addSubview:videoPlayerView];
In the above example, the video preview frame is manually set to 375px wide by 320px high. Figure 4 demonstrates an example of what that looks like once it's added to the view. When the user taps the red play button in the middle of that preview, the standard iOS video player goes to full screen and begins to play the corresponding video.
Practical Application
The code you just wrote is a portable library that can be dropped into any application; just add the VideoManager
and Video
classes to the project and import the YTPlayer
library. To demonstrate the practicality of the library, I've used it to create a demo application that uses the additional video properties that you captured in the Video
model to visually display the search results in a UITableView
. Figure 5 and Figure 6 depict the application UI. The table lists the search results using the video title and thumbnail properties. Thumbnails are loaded asynchronously to prevent any lagging in the scroll. The code included alongside this article not only encompasses the YouTube library covered previously, but it also includes all code for this sample application as well. This should help you understand exactly how to call the library and how to apply the results.
Wrapping Up
Hopefully, over the course of this article, you gained a basic understanding of how to use the search service offered by YouTube's API and how to use that service to pull video content into your iOS application. If you'd like a more detailed breakdown of the search API or additional YouTube APIs, be sure to visit developers.google.com/youtube for full API documentation. The sidebars for this article have links to additional resources that may also be of use.