WistiaKit
The best way to play Wistia video on iPhone, iPad, and Apple TV. Written in Swift.
Swift 4 compatibility on the swift4 branch
Your Video on iOS in 5 minutes!
Disclaimer 1: You need to have Xcode 8 GM Seed installed (which will take > 5 minutes if you don't already have it updated) Disclaimer 2: You need to have RubyGems installed (which may also take a little while)
Ok, got that out of the way. Now for the fun and fairly easy part!
- Install Cocoapods if you haven't already:
Until Cocoapods 1.1.0 is released, install the prerelease withgem install cocoapods
gem install cocoapods --pre
pod try WistiaKit
will pull this Pod down and open it in Xcode- Choose the "WistiaKit-Example" project next to the play icon and press play!
This simple app lets you enter the Hashed ID of any media and play it. Look at the code in WistiaKit/Example for Wistia Kit/ViewController.swift
and look at the basic interface in WistiaKit/Example for Wistia Kit/Main.storyboard
. That's all there is to it; two interface outlets, one custom instance variable, and three lines of code to play the video.
Your Video on tvOS (Apple TV)
Just add the WistiaKit
pod to any tvOS project. Yup, that's it.
Two caveats:
- There is not yet an example project (like above). So it may take more than 5 minutes.
- The
WistiaPlayerViewController
is not available on tvOS. Instead, create aWistiaPlayer
and anAVPlayerViewController
and marry them withavPlayerVC.player = wistiaPlayer.avPlayer
. We think theAVPlayerViewController
looks great on the TV and would be hard pressed to do better.
You Improve WistiaKit
We're still in the early phases of developing this thing. Please get in touch with us (Create an issue, a pull request, email or tweet at Wistia, etc.) to let us know what you'd like to see in WistiaKit
.
Requirements
You need a Wistia account on the Platform plan. You'll also need some videos in your account. And at this point in the game, you need the Hashed ID of the video you'd like to play.
Installation
CocoaPods
WistiaKit is available through CocoaPods.
CocoaPods 1.1.0+ is required to build WistiaKit 0.12+.
To install, simply add the following line to your Podfile:
pod "WistiaKit"
Carthage
Starting from 0.30.2
WistiaKit should work with Carthage. Don't forget to include Alamofire
and AlamofireImage
as they are used by WistiaKit. Sample Cartfile:
github "Wistia/WistiaKit" ~> 0.30.2
github "Alamofire/Alamofire" ~> 4.4
github "Alamofire/AlamofireImage" ~> 3.1
This creates 2 frameworks WistiaKit
and WistiaKitCore
as explained inthe Usage section below.
Just remember to include the necessary frameworks in your swift code like this:
import WistiaKit
import WistiaKitCore
Usage
WistiaKit
is conceptually divided into two tranches; playback and core. Depending on your application, you may use both components -- which work seamlessly together -- or either of them independently. Let's briefly get to know them before diving into the details.
Playback is akin to a web embed. You can present a WistiaPlayerViewController
and play any of your videos using nothing but its hashedID
. Customizations are applied to the player and statistics are tracked like normal; you need do nothing extra. Run the example project in this pod to see it in action (pod try WistiaKit
then hit
If you don't want all the chrome (ie. player controls, scrubber, time, initial poster, etc.) you can get a little lower level with WistiaPlayer
. You still need just a hashedID
, but all you get is an AVPlayerLayer
which you can present and gussy up however you wish. All your Wistia statistics are belong to us are tracked like normal. Psst: the WistiaPlayerViewController
uses the WistiaPlayer
under the hood.
Core is provided through the Data API. We've Swift'd it up, built a bunch of structs, and added some nice syntactic sugar. And the end result -- we hope -- is that it feels natural whether you're coming from another code-level interface to the Data API or the web view you use to manage your account. Initialize a WistiaAPI
object with an API Token from your account and you're off to the races. View account details, list your projects, dig into your medias; everything you can do from the Data API, you can do from here.
Bring them both together: create a WistiaAPI
to browse your WistiaProject
s, choose a WistiaMedia
, use that WistiaMedia
object to initialize a WistiaPlayerViewController
, and then self.presentViewController(_:animated:completion:)
! It's so easy, I bet you could build a pretty nice Apple TV app in a hackathon...
Why did we pull the API interface and data models into WistiaKitCore
? For the times when you don't need playback. Especially when some of the APIs used in WistiaKit
are unavailable but you still wish to view or manipulate account data. Example: include only WistiaKitCore
when implementing an App Extension (or a Framework that will be used in an App Extension).
Video upload is where it all begins! Technically part of the WistiaAPI
but cool enough to get it's own section. Simply create a URL
pointing to a movie on the device, or a Data
of the movie itself, and upload into your account with just one line.
Core
I guess there's not much to say here. Mostly just refer to the Data API docs. And of course, you should use an instance of WistiaAPI
to intrect with the API. For example:
import WistiaKitCore
let globalAPI = WistiaAPI(apiToken:"C4NTB3L13V3TH1S1SARAND0MT0K3N")
func printMediasByProject(){
globalAPI.listProjects { (projects, error) in
for project in projects {
print("\(project) medias:")
globalAPI.listMedias(limitedToProject: project, completionHandler: { (medias, error) in
for media in medias {
print(" \(media)")
}
})
}
}
}
Caveat: WistiaKitCore is not yet Data API complete. But it will be. If there's some functionality that you want, but is not yet available, let us know by submitting a Pull Request
A Note About Structs
New to iOS programming, via Swift, is the expanded availability of value semantics. We'll discuss exatly what that means in soon.
In the good old days of Objective-C you had objects. These were your classes and what not. When you passed objects into methods, or stored them in variables, you were always talking about the same object. This is because your object was a reference type. You were actually passing around references (aka pointers) to the object.
But even then you had value types. A good example were your integers. If you said
a = 4
andb = a
, you set botha
andb
to the value of 4. They weren't pointing to an object. Sob++
didn't change the value ofa
, it would remain 4.Enter Swift and a whole lot more value types! Now we have struct s (and enum s and tuples) that may remind us of reference types, but are actually value types. In
WistiaKitCore
, your data objects are struct s. So if youlet a = someMedia
andlet b = a
, your variablesa
andb
have independent copies of thatWistiaMedia
. If you change something, likea.name = "whatever"
, this won't affectb.name
.We think this makes
WistiaKitCore
less error prone and makes it easier to reason about your code.If you want to spend some guru meditation time on this, you could do worse than starting with the Swift Blog and a WWDC 2015 talk.
Upload
Whether you take a video with the camera, download it from the web, or pull it from iCloud, it's just one line to upload to Wistia. The hardest part should be locating the file URL
or Data
. And that's pretty easy ;-]
import WistiaKitCore
let api = WistiaAPI(apiToken: "C4NTB3L13V3TH1S1SARAND0MT0K3N")
let fileURL = Bundle.main.url(forResource: "hello", withExtension: "mov")
api.upload(fileURL: fileURL!, into: "ProjectHashedID", name: "Hello, World!",
progressHandler: { progress in
print("reticulating splines... \(progress)")
},
completionHandler: { media, error in
print("You've got media! \(media)")
})
Shouldn't need it, but it's nice to know the Wistia Upload API documentation is also available.
Playback
The first thing to do is decide how you'd like your video player to look. If you're familiar with video playback on iOS already, then all you need to know is: WistiaPlayerViewController ~= AVPlayerViewController
and WistiaPlayer ~= AVPlayer
. If you're new to video on iOS, or just need a refresher, read on.
Those who like the look of our web player -- including (most) customizations -- and/or don't want to do a ton of UI buliding should use the WistiaPlayerViewController
. You may present it fullscreen or within a ContainerView
inside your own ViewController
. It comes with standard playback controls and some nice delegate methods for you to hook into.
For those of you who want total control, you want the WistiaPlayer
. To see the video you will need to present the AVPlayerLayer
vended by your instance of the WistiaPlayer
. And you'll need to programatically control the WistiaPlayer
yourself; an AVPlayerLayer
renders only video and includes no player controls or other UI. As you can see, with great power comes great responsibility. :-P
WistiaPlayerViewController
or WistiaPlayer
Initializing referrer
- We recommend using a universal link to the video. This will allow you to click that link from the Wistia stats page while still recording the in-app playback location. If you are using Domain Restrictions, the referrer should include the http(s) protocol and match a domain in your whitelist or video will not load.requireHLS
- Apple has specific requirements for playing video within apps. It boils down to this: if you want to play video over 10 minutes in length over the celluar network (ie. you don't force wifi), then you must use HLS. And Wistia fully supports and has thoroughly tested our HLS implementation with Apple. We recommend leaving this totrue
which is the default.
WistiaPlayerViewController
Example
Lets say we're building an app that has an introductory section. We can use a single WistiaPlayerViewController
to load any number of intro videos and present them full screen, as in the following example:
import WistiaKit
class IntroductionViewController: UIViewController {
let wistiaPlayerVC = WistiaPlayerViewController(referrer: "https://wistia.tv/intro")
func loadVideoWithHashedID(hashedID: String) {
wistiaPlayerVC.replaceCurrentVideoWithVideoForHashedID(hashedID)
self.presentViewController(wistiaPlayerVC, animated: true, completion: nil)
}
}
This will load the video, but it is up to the user to play and otherwise interact with the content.
WistiaPlayer
Example
If we want our intro video to behave a little differently, we might use a WistiaPlayer
. In the following example, we play an intro video without giving the user any way to control the video. They have to sit there and watch it! <evil>
bwaa ha ha ha!</evil>
When video playback completes, we automatically progress to the next intro screen.
Below, we display the video with a WistiaFlatPlayerView
, which is a plain UIView backed by an AVPlayerLayer
. This layer is configured to display video content when you set the view's wistiaPlayer
property to an instance of WistiaPlayer
. While it behaves like any other UIView
, allowing you to use common and familiar lifecycle and layout mechanisms, you may clamor for more power.
A standalone AVPlayerLayer
is avaiable through an WistiaPlayer
s newPlayerLayer()
method. Upon requesting this layer, any previously configured layers or view's will cease to render the content for that player. A newly initialized AVPlayerLayer
- like any CALayer
- should have its frame set before being added as a sublayer. You are also responsible for maintaining layout at the layer level, either manually, with CAConstraint
s, or an other layer-based mechanism.
import WistiaKitCore
import WistiaKit
class IntroductionViewController: UIViewController, WistiaPlayerDelegate {
let wistiaPlayer = WistiaPlayer(referrer: "https://wistia.tv/intro")
// In Interface Builder we set the view's class to WistiaFlatPlayerView.
// If we had a compelling reason, we could instead use an AVPlayerLayer directly via `newPlayerLayer()`.
// But a UIView is more familiar without sacrificing much flexibility.
@IBOutlet weak var playerContainer: WistiaFlatPlayerView!
override public func viewDidLoad() {
wistiaPlayer.delegate = self
playerContainer.wistiaPlayer = wistiaPlayer
wistiaPlayer.replaceCurrentVideoWithVideoForHashedID(IntroVideoHashedID)
}
//Mark: - WistiaPlayerDelegate
public func wistiaPlayer(player:WistiaPlayer, didChangeStateTo newState:WistiaPlayer.State) {
switch newState {
case .VideoReadyForPlayback:
wistiaPlayer.play()
default:
//ignoring, but probably shouldn't
}
}
public func wistiaPlayerDidPlayToEndTime(player: WistiaPlayer) {
self.showNextIntroScreen()
}
// ... rest of delegate methods ...
}
Closed Captioning
If you are using the WistiaPlayerViewController
, closed captioning if fully supported right out of the box. You don't need to do anything more!
For those interpid slingers of the codez using WistiaPlayer
directly, we've made it fairly simple to display captions. Grab the WistiaPlayer.captionsRenderer
-- an instance of WistiaCaptionsRenderer
configured for that player -- and hook up its captionsView
to a view in your UI that overlays the video view. The renderer handles the under-the-hood work of retrieving the captions, timing data, updating the UI content, and mainting proper UI visibility. Captions are kept up to date during playback and seeking across all videos loaded by the WistiaPlayer
. Control what captions, if any, are displayed with the enabled
and captionsLanguageCode
properties of the WistiaCaptionsRenderer
.
Audio Playback
Enabling audio playback when the user has their device switched to vibrate mode must be done by you, in your application:
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
WistiaKit does not presume to read minds ;-]
Player APIs
Up above are a bunch of words that explain how WistiaKit
is structured, how to approach it, and some examples of how to use it. It's good to know the lay of the land. But as they say, the map is not the terrain. You're ready young padawan, go forth and read the appledoc.
Author
d j spinosa, spinosa@gmail.com
License
WistiaKit and WistiaKitCore are available under the MIT license. See the LICENSE file for more info.