Brian Coleman

  • Home
  • ABOUT
  • SERVICES
  • PORTFOLIO
  • BLOG
  • Contact

Tvos 3

Classix for iPhone, iPad & Apple TV

When Apple announced tvOS in September 2015 I did everything I could to learn it fast, I had been waiting to make my own Apple TV apps for years. I’ve always wanted to build a video platform app just like Netflix. If you’ve followed my blog you may have read my tutorials tvOS Tutorial: Make a Video App in Swift and tvOS Tutorial: Top Shelf in Swift. Those two tutorials were a nice start but there was a lot more work needed to make a Netflix app.

Since I’m not a publisher I don’t have rights to any content so I needed to find some. There are a couple of apps on iOS that have a nice library of classic movies and tv shows that are in the public domain. Little did I know how many great movies and TV shows are in the public domain, movies like House on Haunted Hill, Night of the Living Dead and TV shows like Looney Tunes. I downloaded a couple of these apps and saw that it was nice that you could watch those movies but the apps didn’t offer many features, the key features missing being a Recommendation Engine, My List and Continue Watching.

After spending hours and hours entering a large sample set of movies and tv shows into my database I built out the backend APIs that were needed, such as listing movies by categories, recommended movies, trending movies and recently added. I haven’t done much web programming in a long time, but my past knowledge of Perl and MySQL were enough to get the job done.

Next was the UI, sure I could have come up with a simple user interface but what’s simpler than the existing Netflix interface. So that’s what I used for iPhone, iPad and Apple TV. Netflix has a ton of users and more than likely the users of my app have already used Netflix so using the same interface would be very familiar.

“Classix, the Netflix for Classic Movies & TV Shows”

Screen Shot 2016-05-24 at 8.34.00 AM

Watch Now on Your iPhone®, iPad® and Apple TV®

Watch classic TV shows and movies recommended just for you. Classix has something for everyone. There’s even cartoons and movies just for kids with family-friendly entertainment.

Sign up for Classix Premium and receive no commercials and access to premium content. No hidden fees. One time in-app purchase. No recurring subscription needed.

How does Classix work?

• Classix adds TV shows and movies all the time. Browse titles or search for your favorites.
• The more you watch, the better Classix gets at recommending TV shows and movies that you’ll love—just for you.
• Save your favorites to your list and continue watching where you left off.
• Instantly stream on all iOS devices.

Simulator Screen Shot May 20, 2016, 6.12.54 PM

2

3

Simulator Screen Shot May 20, 2016, 6.13.12 PM

Simulator Screen Shot May 20, 2016, 6.15.35 PM

Find out more at http://www.classixapp.com

Twitter: @classixapp

Instagram: @classixapp

Facebook: http://www.facebook.com/ClassixApp

May 20, 2016 About Me, My Apps, tvOSapps, ios, swift, tvos

tvOS Tutorial: Top Shelf in Swift

The top shelf is a content showcase area above the top row of apps on the Apple TV Home screen. The user can decide which apps are in the top row. When one of these apps is brought into focus, the top shelf displays featured content specific to that app.

It’s a unique opportunity for your app to highlight new, featured, or useful content and let the user jump directly to it. For example, when the iTunes TV Shows app is in focus, people see a row of featured shows. Focusing on and clicking a TV show goes right to the related product page in the app. Pressing the Play/Pause button on the remote while a show is in focus begins media playback for the show.

To begin, you’ll already need a tvOS app. Top Shelf is an extension to the tvOS app similar to a Today Widget and Apple Watch app to an iOS app, they are bundled together. You can follow the steps in this article on how to build an tvOS video app: tvOS Tutorial: Make a Video App in Swift. We’ll take this app and add the featured movies as our top shelf.

Static Image

At a minimum, every app must supply a single, static top shelf image that can be displayed when your app is in the top row of the Home screen and in focus. This image needs to be 1920px by 720px.

With the Assets folder, drag your image into the Top Shelf Image location, as seen below.

Screen Shot 2015-10-30 at 9.21.10 AM

Sectioned Content Row

This layout style shows a single labeled row of sectioned content. It’s commonly used to highlight recently viewed content, new content, or favorites.

Content in the row is focusable, letting the user scroll through it at the desired speed. A label appears when an individual piece of content comes into focus, and small movements on the remote’s touch surface bring the focused image to life. A sectioned content row can be configured to show multiple labels, if desired.

This layout is templated and cannot be customized any further. It must contain a title, image and the title for the video will appear once the user has put it in focus. When a user taps on it it should deep link directly to the content within your app.

To create a sectioned content row, follow the steps below.

  1. Click File > New > Target
  2. Select “Application Extension” under tvOS and choose the “TV Services Extension”, and click “Next”.
    Screen Shot 2015-10-27 at 9.02.27 AM

  3. Name the extension, “TopShelf” is good.
  4. The Bundle Identifier is important. Since this is an extension it should be the same as your parent app. You can only add one more period “.” to the end to make it unique, i.e. com.brianjcoleman.Video-App.TopShelf and click “Finish”.
    Screen Shot 2015-10-27 at 9.05.12 AM

  5. A popup will appear, “Activate “TopShelf” scheme?”, you can click “Activate” on this. It will make the TopShelf target active so you can test it within Xcode.
    Screen Shot 2015-10-27 at 9.07.40 AM

  6. You’ll notice we now have a new folder in the Project Navigator with the ServiceProvider.swift and Info.plist file.
  7. To ensure we can access web servers for grabbing content, be sure to go into the Info.plist and add the following keys.
    Screen Shot 2015-10-12 at 12.35.22 PM


Enter the following code into ServiceProvider.swift

import Foundation
import TVServices

class ServiceProvider: NSObject, TVTopShelfProvider {

    override init() {
        super.init()
    }

    var topShelfStyle: TVTopShelfContentStyle {
        // Return desired Top Shelf style.
        return .Sectioned
    }

    var topShelfItems: [TVContentItem] {
        // Create an array of TVContentItems.
        let wrapperID = TVContentIdentifier(identifier: "shelf-wrapper", container: nil)!
        let wrapperItem = TVContentItem(contentIdentifier: wrapperID)!
        var ContentItems = [TVContentItem]()
        
        for (var i = 0; i < 8; i++)
        {
            let identifier = TVContentIdentifier(identifier: "VOD", container: wrapperID)!
            let contentItem = TVContentItem(contentIdentifier: identifier )!
            
            if let url = NSURL(string: "http://www.brianjcoleman.com/code/images/feature-\(i).jpg")
            {
                contentItem.imageURL = url
                contentItem.imageShape = .HDTV
                contentItem.title = "Movie Title"
                contentItem.displayURL = NSURL(string: "VideoApp://video/\(i)");
                contentItem.playURL = NSURL(string: "VideoApp://video/\(i)");
            }
            ContentItems.append(contentItem)
        }
        
        // Section Details
        wrapperItem.title = "Featured Movies"
        wrapperItem.topShelfItems = ContentItems
    
        return [wrapperItem]
    }
}

Notice that we are defining the type of content to display in the topShelfStyle method. You can return either .Sectioned to display multiple items in the row on the screen at once (i.e. iTunes Movies) or .Inset which will display one large banner similar to the App Store.

The topShelfItems method should return the array of items, they are of type TVContentItem, which contain the following properties:

  • imageURL - A URL giving the location of the image to be displayed for this content item.
  • imageShape - The intended aspect ratio or shape of the content image.
    Below are the three sizes available for .Sectioned content.
    Screen Shot 2015-10-27 at 9.39.24 AM

  • title - The localized string title of the item.
  • displayURL - A URL that causes the app which created this content item to display a description screen for the item. (type is NSURL)
  • playURL - A URL that causes the app which created this content item to begin playing the item at the user's current position. (type is NSURL)

Lastly set the returned wrapper TVContentItem title to what you want the section header to be named.

Deep Linking

Top shelf layouts are a path to content that people care about most. Help them get to it quickly. Let users click a focused image to open your app and go right to the related content, or let them use the Play button on the remote to immediately start media playback or enter gameplay.

First setup your URL Scheme within the info.plist of your tvOS parent app.

Screen Shot 2015-10-30 at 9.02.42 AM

The deep link URL will now be the URL scheme, plus any parameters you wish to pass through: VideoApp://video/1

You must provide data for both playURL and displayURL. Failing to provide both means that selecting a top shelf item does nothing. As soon as you provide a value for displayURL, selecting a video will immediately start opening your app. Just like iOS and Watch OS apps the first method that will get called in your App Delegate is openURL.

func application(app: UIApplication, openURL url: NSURL, options: [String : AnyObject]) -> Bool {
    
    if url.host == nil
    {	return true
    }
    
    let urlString = url.absoluteString
    let queryArray = urlString.componentsSeparatedByString("/")
    let query = queryArray[2]
    if query.rangeOfString("video") != nil
    {
        let videoIndexPathRow = queryArray[3]
        let deepLinkVideoIndexPathRow = videoIndexPathRow
        
        if deepLinkVideoIndexPathRow != ""
        {
            if let tabBarController = self.window!.rootViewController as? UITabBarController
            {
                let svc = tabBarController.viewControllers![0] as! MoviesViewController
                tabBarController.selectedIndex = 0
                svc.setDeepLink(deepLinkVideoIndexPathRow)
            }
        }
    }
    
    return true
}

The openURL method above reads in the URL passed from the top shelf. It then checks the query string for "video" as the key identifier to let us know that we want to play a video. This is helpful if you have multiple deep links for different features your app. Next we parse the rest of the URL to look for the row number selected so we know what video to play (this can be any unique identifier to let you know what video was selected). Then we direct the app to go to the MoviesViewController and call a new method named setDeepLink where we pass in the unique identifier and play the video.

func setDeepLink(videoIndexPathRow: String) {
    let playerVC = PlayerViewController()
    playerVC.playVideo()
    [self.presentViewController(playerVC, animated: true, completion: nil)]
}

You'll notice that I am passing in the videoIndexPathRow but am not actually using it since this sample app doesn't have any actual data feeds, but you should pass something so you will need to key on a unique ID so you know what video to play. As soon as this method is called it starts the video player and plays the selected video.

You can grab the full source code for this tutorial.
Note: Created using Xcode 7.1 GM (tvOS & Swift 2).

October 30, 2015 Swift, Tutorials, tvOSios9, swift, tvos

tvOS Tutorial: Make a Video App in Swift

Finally Apple is allowing developers the ability to make our own Apple TV apps using the new tvOS SDK. tvOS is just about the same as developing for iOS but with a couple restrictions. No web views are allowed, tvOS does not contain all of the UI objects that are in iOS but it does have most of them, like buttons, alert views, table views and collection views which are probably the most common objects found in Apple TV apps currently. The other restriction is you can only use third-party frameworks that are built specifically for tvOS, you cannot reuse the same frameworks or libraries you use in your iOS or Watch OS apps.

In this tutorial we are going to build the most common type of tvOS app that is currently on the Apple TV platform, a video app. We’ll try to recreate a Netflix style layout and look at the classes and objects needed to make browsing and viewing TV shows and movies.

Project Setup

  1. Create a new project, File > New > Project
  2. Choose Application under tvOS and select “Single View Application”
    Screen Shot 2015-10-10 at 12.31.05 PM

  3. Name the Product “Video App”, and we’re going to develop it in Swift
    Screen Shot 2015-10-10 at 12.34.17 PM

Setting up the Menu

The main menu for most Apple TV apps is a tab bar controller, except for tvOS apps it’s at the top of the screen instead of the bottom like iOS apps. This menu will let the user select between the sections of the app, i.e. Movies or TV Shows.

  1. Click “Main.storyboard” from the project navigator.
  2. From the UI objects library in the bottom right of Xcode, find “Tab Bar Controller” and drag it onto the Storyboard. Use the pinch gesture on your trackpad to zoom out so you can see everything in the Storyboard.
    Screen Shot 2015-10-10 at 12.39.30 PM

  3. Select the Item 2 View Controller and name it “TV” and change the “Item 2” bar item to “TV”
    Screen Shot 2015-10-10 at 12.43.34 PM

  4. Do the same for the Item 1 Scene but change that to “Movies”
  5. Run the program and you’ll see that we have a standard tab bar with both Movies and TV. You can use your arrow keys to switch back and forth between view controllers.
    Screen Shot 2015-10-10 at 12.44.06 PM

Designing the Featured Movies Layout

Our layout is going to include one top row with the main featured movies or shows then a few rows after it which are individual movies and shows that the user can browse through, just like Netflix.

The secret to creating a tvOS app similar to the ones on the Apple TV now is to put all of your objects into a scroll view. As the user moves the focus down the page the scroll view will automatically move up or down so the user can see objects that are outside of the current visible area.

  1. Starting with the Movies Scene selected, uncheck “Under Top Bars” and “Under Bottom Bars” from the Attributes Inspector on the right. This will allow the Menu to hide smoothly when the user is at the top of the screen.
    Screen Shot 2015-10-10 at 12.48.44 PM

  2. Click on the view so we can customize the layout for our scrolling movie lists.
  3. From the UI objects library drag out a scroll view and make it the entire size of the view controller 1920 x 940.
    Screen Shot 2015-10-10 at 12.51.33 PM

  4. Next drag a UICollectionView from the UI objects library and place it at the top of the screen. This will be our main featured area. It’s size should be 1920×400.
    Screen Shot 2015-10-10 at 12.55.53 PM

  5. Set the Cell Size to 640 x 480 so we can fit 3 on the screen.
  6. Add your featured assets to the project. Right click on the Project Navigator and select “Add Files to …”, select the images you want to store locally.
  7. Back to the Storyboard, from the UI objects library drag a UIImageView into the Collection View Cell, it should be the entire width and height of the cell.
    Screen Shot 2015-10-12 at 11.38.14 AM

Custom Featured Collection View Cell

We need to create a custom collection view cell so we can tailor the layout of the cell to exactly how the design looks. We’ll call it FeaturedCollectionViewCell. Start with the large featured collection view cells.

  1. Right click in the Project Navigator, select “New File”, choose “tvOS > Source > Swift File”.
    Screen Shot 2015-10-10 at 1.44.01 PM

  2. Enter in the name of the custom class FeaturedCollectionViewCell and click “Create”.
    Screen Shot 2015-10-10 at 1.44.13 PM

Below is the code for the featured collection view class, it only contains 1 object, an UIImageView.

import UIKit

class FeaturedCollectionViewCell: UICollectionViewCell {
    
    @IBOutlet weak var featuredImage: UIImageView!
    
    override init(frame: CGRect)
    {
        super.init(frame: frame)
        self.commonInit()
    }
    
    required init?(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
    }
    
    private func commonInit()
    {
        // Initialization code
        
        self.layoutIfNeeded()
        self.layoutSubviews()
        self.setNeedsDisplay()
    }
    
    override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
        if (self.focused)
        {
            self.featuredImage.adjustsImageWhenAncestorFocused = true
        }
        else
        {
            self.featuredImage.adjustsImageWhenAncestorFocused = false
        }
    }
    
    override func layoutSubviews()
    {
        super.layoutSubviews()
    }
    
    override func awakeFromNib() {
        super.awakeFromNib()
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
    }
    
}

You’ll notice a new method you’ve never seen before:

override func didUpdateFocusInContext(context: UIFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
        if (self.focused)
        {
            self.featuredImage.adjustsImageWhenAncestorFocused = true
        }
        else
        {
            self.featuredImage.adjustsImageWhenAncestorFocused = false
        }
    }

Since tvOS doesn’t have touch capability like iOS or Watch OS this method tells the cell what to do when it is in focus. The adjustsImageWhenAncestorFocused property for the UIImageView will make the image expand and popout when it’s in focus for the user. This is the most common UI indicator for the user when browsing through the content.

Hook up the Featured Cell

Now that we have the custom class, we need to implement the UI objects for it within the Storyboard.

  1. Click “Main.storyboard” from the project navigator.
  2. Click the Cell under Collection View 1 and inside the Attributes Inspector change the Identifier to “FeaturedCell”. This will be our re-useable identifier when creating the cells.
    Screen Shot 2015-10-10 at 1.49.10 PM

  3. Enter the custom class “FeaturedCollectionViewCell” with the object so you can hookup the IBOutlet for the UIImageView we defined earlier.
    Screen Shot 2015-10-10 at 1.49.26 PM

  4. From the UI objects library in the bottom right of Xcode, find “Image View” and drag it onto the collection view cell. Then hookup the IBOutlet by dragging the featuredImage outlet to the FeaturedCell.
    Screen Shot 2015-10-10 at 1.49.39 PM

Movies View Controller

  1. Make a new Swift file for our Movies View Controller. Right Click on the Project Navigator and select “New File”, choose “tvOS > Source > Swift File”. Name it “MoviesViewController” and click the “Create” button.
    Screen Shot 2015-10-10 at 1.44.01 PM

  2. Enter the following code, this is everything required to get the scroll view and collection view to work with the images we have within the project.
    import Foundation
    import UIKit
    
    class MoviesViewController: UIViewController, UICollectionViewDelegateFlowLayout, UICollectionViewDataSource, UIScrollViewDelegate {
        
        @IBOutlet var scrollView : UIScrollView!
        @IBOutlet var collectionView1 : UICollectionView!
        let reuseIdentifierFeatured = "FeaturedCell"
        
        override func viewDidLoad() {
            super.viewDidLoad()
        }
    
        override func viewDidLayoutSubviews() {
            self.scrollView!.contentSize = CGSizeMake(1920, 2200)
        }
    
        // Collection View Methods
        
        func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAtIndex section: Int) -> CGFloat {
            return 50
        }
        
        func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
            return 50
        }
        
        func collectionView(collectionView: UICollectionView,
            layout collectionViewLayout: UICollectionViewLayout,
            insetForSectionAtIndex section: Int) -> UIEdgeInsets {
                return UIEdgeInsets(top: 0.0, left: 50.0, bottom: 0.0, right: 50.0)
        }
        
        func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
            return 1
        }
        
        func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
        {
            if (collectionView == self.collectionView1)
            {
                return 8
            }
            
            return 0
        }
        
        func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
            
            if (collectionView == self.collectionView1)
            {
                let cell : FeaturedCollectionViewCell = collectionView.dequeueReusableCellWithReuseIdentifier(self.reuseIdentifierFeatured, forIndexPath: indexPath) as! FeaturedCollectionViewCell
                
                let  imageFilename = "feature-\(indexPath.row).jpg"
                cell.featuredImage.image = UIImage(named: imageFilename)
                
                return cell
            }
            
            return UICollectionViewCell()
        }
        
    }
    

    Note: As of Beta3 the scrollView contentSize should be entered within the viewDidLayoutSubviews() method since the view size is not yet set when viewDidLoad() is called

Hook up the Outlets

All of the code has been written in the Movie View Controller class, so we just need to hookup the outlet and delegates for the scroll view and collection view.

  1. Click “Main.storyboard” from the project navigator.
  2. Define the custom class for the Movies view controller.
    Screen Shot 2015-10-12 at 12.15.29 PM

  3. Click the scroll view and drag the outlet to the Movies
    Screen Shot 2015-10-12 at 12.16.39 PM

  4. Click the Collection View and drag the delegates and outlet to the Movies view controller so we can control it within the MoviesViewController.swift file.
    Screen Shot 2015-10-12 at 12.18.16 PM

Run It

If everything is linked up correctly and the code above all entered you should see the first collection view on the screen with the images displayed.

Screen Shot 2015-10-10 at 2.04.22 PM

Scrolling Movies

If you would like to have categories that the user can browse through, you can make them by adding more collection views below the featured one that we just created. Each collection view will be unique with it’s own set of data.

Download the entire project at the end of this tutorial to see how I did it.

Screen Shot 2015-10-12 at 12.27.45 PM

Playing Video

The last feature we need to make is allowing the user to watch video once one of the movies or TV shows has been selected.

  1. Make a new Swift file for our Player View Controller. Right Click on the Project Navigator and select “New File”, choose “tvOS > Source > Swift File”. Name it “PlayerViewController” and click the “Create” button.
  2. Add the following code to it:
    import Foundation
    import UIKit
    import AVKit
    
    class PlayerViewController: AVPlayerViewController, AVPlayerViewControllerDelegate {
        
        override func viewDidLoad() {
            super.viewDidLoad()
        }
        
        func playVideo() {
            
            player = AVPlayer(URL: NSURL(string: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4")!)
            player?.play()
        }
        
    }
    
  3. Add the following collection view method to the the MoviesViewController.swift so when a cell is selected it’ll open up the PlayerViewController:
    func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
        let playerVC = PlayerViewController()
        playerVC.playVideo()
        [self.presentViewController(playerVC, animated: true, completion: nil)]
    }
    
  4. Click “Main.storyboard” from the project navigator.
  5. From the UI objects library in the bottom right of Xcode, find “AVKit Player View Controller” and drag it onto the Storyboard.
  6. Change the custom class name to “PlayerViewController”.
    Screen Shot 2015-10-12 at 12.33.10 PM

  7. Before we can run this, as of iOS 9, we need to allow our app to access external URLs, like the one in our PlayerViewController that links to the video we’re playing.
  8. Click the Info.plist from the Project Navigator and add the keys below:
    Screen Shot 2015-10-12 at 12.35.22 PM

Watch Video

Lastly lets test to see if our video player works. Run the app, scroll to a video cell and click the Select button on the new Apple TV remote (or Enter key in the simulator) to play the video. You should see the screen below.

Screen Shot 2015-10-12 at 12.38.07 PM

There you have it, your own video app where users can browse through titles by category and select one to watch a video. Be sure to download the sample app below so you can see the full working app.

You can grab the full source code for this tutorial.
Note: Created using Xcode 7.1 GM (tvOS & Swift 2).

October 13, 2015 Swift, Tutorials, tvOSswift, tutorial, tvos
Recent Posts
  • Classix for iPhone, iPad & Apple TV
  • Tutorial: How to test your app for IPv6 compatibility
  • Tutorial: Testing SSL using Charles Proxy on an iOS Device
  • Tutorial: 3D Touch – Quick Actions in Swift
  • tvOS Tutorial: Top Shelf in Swift
Featured Apps
Classix
Sportsnet
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
Search
TAGS
tutorialswiftios8iosobjective-cvideogamesstrategynewsframeworkappsmonitizefacebookwatchappleios7toolstvosios9apiprovisionsocialtutorialsbooksdesignbookiapIPv6iTunes Connect
ABOUT
Brian is a Lead iOS/tvOS Developer from Toronto with over 18 years of multifaceted experience including development, design, business analysis and project management.

FOLLOW ME
    
Email Subscription
Sign up for my newsletter to receive the latest news and tutorials posted.

Enter your email address:

2023 © Brian Coleman