<img src="https://img.shields.io/badge/%20-ENTERPRISE-B10031" style="float:left"/><br /> <img src="https://img.shields.io/badge/iOS-V4-009727?logo=apple" style="float:left"/><br />

The following sections provide notes on how to migrate from iOS SDK v3 to iOS SDK 4.x.

<br /> <hr />

## Configuration API

In iOS SDK 3.x, the main player configuration object is called `JWConfig`.



<br />

In iOS SDK 4.x, `JWConfig` is renamed to `JWPlayerConfiguration`. This configuration object is created using a separate object called `JWPlayerConfigurationBuilder`. We have made similar modifications to other player configuration objects such as `JWPlaylistItem` (now `JWPlayerItem`) and `JWAdConfig` (now `JWAdvertisingConfig`).



<br />

By separating the construction of configuration objects from their representation, we are able to throw an error if an object is created incorrectly. For example, a configuration object could be missing required properties or have multiple properties set that should not be set simultaneously.



<br />

The following subsections outline how to migrate the various configuration objects in version 3.x to iOS SDK 4.x.

### JWAdConfig

In iOS SDK 4.x, `JWAdConfig` is called `JWAdvertisingConfig`. Create a `JWAdvertisingConfig` object using either the `JWAdsAdvertisingConfigBuilder`, `JWImaAdvertisingConfigBuilder`, or `JWImaDaiAdvertisingConfigBuilder` class.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> </tr> </thead> <tbody> <tr> <td width="25%"> <code>adMessage</code> </td> <td width="75%"> <code>JWAdsAdvertisingConfig.adMessage(_ adMessage: String)</code></td> </tr> <tr> <td> <span style="color:red; font-size:13px"><strong><code>adVmap</code></span> </td> <td> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /></td> </tr> <tr> <td> <code>client</code></td> <td>Automatically set by the builder that is used to create the <code>JWAdvertisingConfig</code></td> </tr> <tr> <td> <span style="color:red; font-size:13px"><strong><code>freewheel</code></span> </td> <td> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /></td> </tr> <tr> <td> <code>googimaDaiSettings</code></td> <td>Automatically set by the <code>JWImaDaiAdvertisingConfigBuilder</code></td> </tr> <tr> <td> <code>googimaSettings</code></td> <td>Automatically set by the <code>JWImaAdvertisingConfigBuilder</code></td> </tr> <tr> <td> <code>rules</code> </td> <td> <code>JWAdsAdvertisingConfig.adRules(_ adRules: JWAdRules</code><br /><br />-- OR --<br /><br /><code>JWImaAdvertisingConfig.adRules(_ adRules: JWAdRules)</code></td> </tr> <tr> <td> <code>schedule</code> </td> <td> <code>JWAdsAdvertisingConfig.schedule(_ schedule: [JWAdBreak])</code><br /><br />-- OR --<br /><br /><code>JWImaAdvertisingConfig.schedule(_ schedule: [JWAdBreak])</code></td> </tr> <tr> <td> <code>skipMessage</code> </td> <td> <code>JWAdsAdvertisingConfig.skipMessage(_ skipMessage: String)</code> </td> </tr> <tr> <td> <code>skipOffset</code> </td> <td> <code>JWAdsAdvertisingConfig.skipOffset(_ skipOffset: UInt) </td> </tr> <tr> <td> <code>skipText</code> </td> <td> <code>JWAdsAdvertisingConfig.skipText(_ skipText: String) </td> </tr> <tr> <td> <code>tag</code> </td> <td> <code>JWAdsAdvertisingConfig.tag(_ tag: URL)</code><br /><br />-- OR --<br /><br /><code>JWImaAdvertisingConfig.tag(_ tag: URL)</code></td> </tr> <tr> <td> <code>vpaidControls</code> </td> <td> <code>JWAdsAdvertisingConfig.vpaidControls(_ vpaidControls: Bool)</code></td> </tr> </tbody> </table>

<br /> <br />

### JWAdRules

In iOS SDK 4.x, `JWAdRules` is still called `JWAdRules`. Create a `JWAdRules` object using the `JWAdRulesBuilder` class.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> </tr> </thead> <tbody> <tr> <td width="25%"> <code>frequency</code> </td> <td width="75%"> <code>JWAdRulesBuilder.jwRules(_startOn: UInt, frequency: UInt, timeBetweenAds: UInt, startOnSeek: JWAdShownOnSeek)</code><br /><br />-- OR --<br /><br /><code>JWAdRulesBuilder.imaRules(startOn: UInt, frequency: UInt) </code></td> </tr> <tr> <td> <code>startOn</code> </td> <td> <code>JWAdRulesBuilder.jwRules(_startOn: UInt, frequency: UInt, timeBetweenAds: UInt, startOnSeek: JWAdShownOnSeek)</code><br /><br />-- OR --<br /><br /><code>JWAdRulesBuilder.imaRules(startOn: UInt, frequency: UInt)</code></td> </tr> <tr> <td> <code>startOnSeek</code></td> <td> <code>JWAdRulesBuilder.jwRules(_startOn: UInt, frequency: UInt, timeBetweenAds: UInt, startOnSeek: JWAdShownOnSeek)</code></td> </tr> <tr> <td> <code>timeBetweenAds</code></td> <td> <code>JWAdRulesBuilder.jwRules(_startOn: UInt, frequency: UInt, timeBetweenAds: UInt, startOnSeek: JWAdShownOnSeek)</code></td> </tr> </tbody> </table>

<br /> <br />

### JWConfig

In iOS SDK 4.x, `JWConfig` is called `JWPlayerConfiguration`. Create a `JWPlayerConfiguration` object using the `JWPlayerConfigurationBuilder` class.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> </tr> </thead> <tbody> <tr> <td width="25%"> <code>advertising</code> </td> <td width="75%"> <code>JWPlayerConfigurationBuilder.advertising(_ advertising: JWAdvertisingConfig)</code></td> </tr> <tr> <td> <span style="color:red; font-size:13px"><strong><code>assetOptions</code></span> </td> <td> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /> </td> </tr> <tr> <td> <span style="color:red; font-size:13px"><strong><code>audioSwitchingEnabled</code></span> </td> <td> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /> </td> </tr> <tr> <td> <code>autostart</code> </td> <td> <code>JWPlayerConfigurationBuilder.autostart(_ autostart: Bool)</code></td> </tr> <tr> <td> <code>bitRateUpperBound</code> </td> <td> <code>JWPlayerConfigurationBuilder.bitRateUpperBound(_ bitRateUpperBound: Float)</code></td> </tr> <tr> <td> <code>captions</code> </td> <td> <code>JWPlayerView.captionStyle</code></td> </tr> <tr> <td> <code>controls</code> </td> <td> <code>JWPlayerViewController.interfaceBehavior</code></td> </tr> <tr> <td> <code>desc</code> </td> <td> <code>JWPlayerItemBuilder.description(_ description: String)</code></td> </tr> <tr> <td> <code>displayDescription</code> </td> <td> <code>JWPlayerSkin.descriptionIsVisible</code></td> </tr> <tr> <td> <code>displayTitle</code> </td> <td> <code>JWPlayerSkin.titleIsVisible</code></td> </tr> <tr> <td> <code>externalMetadata</code> </td> <td> <code>JWPlayerConfigurationBuilder.externalMetadata(_ externalMetadata: [JWExternalMetadata])</code></td> </tr> <tr> <td> <code>file</code> </td> <td> <code>JWPlayerItemBuilder.file(_ file: URL)</code></td> </tr> <tr> <td> <code>image</code> </td> <td> <code>JWPlayerItemBuilder.posterImage(_ posterImage: URL)</code></td> </tr> <tr> <td> <code>logo</code> </td> <td> <code>JWPlayerViewController.logo</code></td> </tr> <tr> <td> <code>mediaId</code> </td> <td> <code>JWPlayerItemBuilder.mediaId(_ mediaId: String)</code></td> </tr> <tr> <td> <code>nextupDisplay</code> </td> <td> <code>JWPlayerViewController.nextUpStyle</code></td> </tr> <tr> <td> <code>nextupOffset</code> </td> <td> <code>JWPlayerViewController.nextUpStyle</code></td> </tr> <tr> <td> <code>nextupPercentage</code> </td> <td> <code>JWPlayerViewController.nextUpStyle</code></td> </tr> <tr> <td> <code>offlineMessage</code> </td> <td> <code>JWPlayerViewController.offlineMessage</code></td> </tr> <tr> <td> <code>offlinePoster</code> </td> <td> <code>JWPlayerViewController.offlinePosterImage</code></td> </tr> <tr> <td> <code>playbackRates</code> </td> <td> <code>JWPlayerViewController.playbackRates</code></td> </tr> <tr> <td> <code>playbackRateControls</code> </td> <td> <code>JWPlayerViewController.playbackRateControlsEnabled</code></td> </tr> <tr> <td> <code>playlist</code> </td> <td> <code>JWPlayerConfigurationBuilder.playlist(_ playlist: [JWPlayerItem])</code></td> </tr> <tr> <td> <code>preload</code> </td> <td> <code>JWPlayerConfigurationBuilder.preload(_ preload: JWPreload)</code></td> </tr> <tr> <td> <code>related</code> </td> <td> <code>JWPlayerConfigurationBuilder.related(_ related: JWRelatedContentConfiguration)</code></td> </tr> <tr> <td> <code>repeat</code> </td> <td> <code>JWPlayerConfigurationBuilder.repeatContent(_ repeatContent: Bool)</code></td> </tr> <tr> <td> <code>size</code> </td> <td> Set the size of the player by setting the size of <code>JWPlayerView</code></td> </tr> <tr> <td> <code>skin</code> </td> <td> <code>JWPlayerViewController.styling</code></td> </tr> <tr> <td> <code>sources</code> </td> <td> <code>JWPlayerItemBuilder.sources(_ videoSources: [JWVideoSource])</code></td> </tr> <tr> <td> <code>stretching</code> </td> <td> The functionality of <code>JWConfig.stretching</code> can now be achieved dynamically by setting <code>videoGravity</code> on <code>JWPlayerView</code>.</td> </tr> <tr> <td> <code>title</code> </td> <td> <code>JWPlayerItemBuilder.title(_ title: String)</code></td> </tr> <tr> <td> <code>tracks</code> </td> <td> <code>JWPlayerItemBuilder.mediaTracks(_ mediaTracks: [JWMediaTrack])</code></td> </tr> </tbody> </table>

<br /> <br />

### JWPlaylistItem

In iOS SDK 4.x, `JWPlaylistemItem` is called `JWPlayerItem`. Create a `JWPlayerItem` object using the `JWPlayerItemBuilder` class.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> </tr> </thead> <tbody> <tr> <td width="25%"> <code>adSchedule</code> </td> <td width="75%"> <code>JWPlayerItemBuilder.adSchedule(_ adSchedule: [JWAdBreak])</code></td> </tr> <tr> <td> <code>assetOptions</code> </td> <td> <code>JWPlayerItemBuilder.assetOption</code></td> </tr> <tr> <td> <code>desc</code> </td> <td> <code>JWPlayerItemBuilder.description(_ description: String)</code></td> </tr> <tr> <td> <code>externalMetadata</code> </td> <td> <code>JWPlayerItemBuilder.externalMetadata</code></td> </tr> <tr> <td> <code>file</code> </td> <td> <code>JWPlayerItemBuilder.file(_ file: URL)</code></td> </tr> <tr> <td> <span style="color:red; font-size:13px"><strong><code>freewheel</code> </td> <td> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /> </td> </tr> <tr> <td> <code>googleDaiSettings</code> </td> <td> <code>JWPlayerItemBuilder.googleDAIStream</code></td> </tr> <tr> <td> <code>image</code> </td> <td> <code>JWPlayerItemBuilder.posterImage(_ posterImage: URL)</code></td> </tr> <tr> <td> <code>mediaId</code> </td> <td> <code>JWPlayerItemBuilder.mediaId(_ mediaId: String)</code></td> </tr> <tr> <td> <code>recommendations</code> </td> <td> <code>JWPlayerItemBuilder.recommendations(_ recommendations: URL)</code></td> </tr> <tr> <td> <code>sources</code> </td> <td> <code>JWPlayerItemBuilder.videoSources(_ videoSources: [JWVideoSource])</code></td> </tr> <tr> <td> <code>startTime</code> </td> <td> <code>JWPlayerItemBuilder.startTime(_ startTime: TimeInterval)</code></td> </tr> <tr> <td> <code>title</code> </td> <td> <code>JWPlayerItemBuilder.title(_ title: String)</code></td> </tr> <tr> <td> <code>tracks</code> </td> <td> <code>JWPlayerItemBuilder.mediaTracks(_ mediaTracks: [JWMediaTrack])</code></td> </tr> </tbody> </table>

<br /> <br />

### JWSource

In iOS SDK 4.x, `JWSource` is called `JWVideoSource`. Create a `JWVideoSource` object using the `JWVideoSourceBuilder` class.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> </tr> </thead> <tbody> <tr> <td width="25%"> <span style="color:red; font-size:13px"><strong><code>assetOptions</code></span></td> <td width="75%"> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /> </td> </tr> <tr> <td> <code>defaultQuality</code> </td> <td> <code>JWVideoSourceBuilder.defaultVideo(_ defaultVideo: Bool)</code></td> </tr> <tr> <td> <code>file</code> </td> <td> <code>JWVideoSourceBuilder.file(_ file: URL)</code></td> </tr> <tr> <td> <code>label</code> </td> <td> <code>JWVideoSourceBuilder.label(_ label: String)</code></td> </tr> </tbody> </table>

<br /> <br />

### JWTrack

In iOS SDK 4.x, `JWTrack` is called `JWMediaTrack`. Create a `JWMediaTrack` object using either the `JWCaptionTrackBuilder` or `JWThumbnailTrackBuilder` class.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> </tr> </thead> <tbody> <tr> <td width="25%"> <code>defaultTrack</code></td> <td width="75%"> <code>JWCaptionTrackBuilder.defaultTrack(_ defaultTrack: Bool) </code> </td> </tr> <tr> <td> <code>file</code> </td> <td> <code>JWCaptionTrackBuilder.file(_ file: URL)</code><br /><br />-- OR --<br /><br /><code>JWThumbnailTrackBuilder.file(_ file: URL)</code></td> </tr> <tr> <td> <code>kind</code> </td> <td> Automatically set by the builder that’s used to create the <code>JWMediaTrack</code></td> </tr> <tr> <td> <code>label</code> </td> <td> <code>JWCaptionTrackBuilder.file(_ file: String)</code></td> </tr> </tbody> </table>

<br /> <br />

### JWRelatedConfig

In iOS SDK 4.x, `JWRelatedConfig` is called `JWRelatedContentConfiguration`. Create a `JWRelatedContentConfiguration` object using the `JWRelatedContentConfigurationBuilder` class.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> </tr> </thead> <tbody> <tr> <td width="25%"> <code>autoplayMessage</code></td> <td width="75%"> <code>JWRelatedContentConfigurationBuilder.autoplayMessage(_ message: String)</code> </td> </tr> <tr> <td> <code>autoplayTimer</code> </td> <td> <code>JWRelatedContentConfigurationBuilder.autoplayTimer(_ timer: Int)</code></td> </tr> <tr> <td> <code>displayMode</code> </td> <td> <code>JWRelatedContentConfigurationBuilder.displayMode(_ mode: JWRelatedDisplayMode)</code></td> </tr> <tr> <td> <code>file</code> </td> <td> <code>JWRelatedContentConfigurationBuilder.url(_ url: URL)</code></td> </tr> <tr> <td> <code>heading</code> </td> <td> <code>JWRelatedContentConfigurationBuilder.heading(_ heading: String)</code></td> </tr> <tr> <td> <code>onClick</code> </td> <td> <code>JWRelatedContentConfigurationBuilder.onClick(_ relatedOnClick: JWRelatedOnClick)</code></td> </tr> <tr> <td> <code>onComplete</code> </td> <td> <code>JWRelatedContentConfigurationBuilder.onComplete(_ relatedOnComplete: JWRelatedOnComplete)</code></td> </tr> </tbody> </table>

<br /> <hr />

## Event API

In iOS SDK 3.x, there was one delegate (`JWPlayerDelegate`) to subscribe to for all events. Conforming to such a protocol in Swift would be cumbersome, as protocols in Swift require a class to conform to all interfaces.

In iOS SDK 4.x, the callbacks from `JWPlayerDelegate` have now been delegated to more logical interfaces. All playback and advertising-related callbacks have been divided across several new delegates. This allows you to optionally define delegates for categories of events through the `JWPlayer` object.

User interface-related delegates -- such as tracking what buttons a user is tapping or when the control bar appears or disappears -- can be subscribed to through the `JWPlayerViewControllerDelegate`.

<br />

### Event Migration

The table below outlines how to migrate from the callbacks in `JWPlayerDelegate` to the iOS SDK 4.x delegates.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> <th> Callback </th> </tr> </thead> <tbody> <tr> <td width="10%"> <code>onAdBreakEnd</code> </td> <td width="10%"> <code>JWAdDelegate</code> </td> <td width="80%"> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdBreakStart</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdClick</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdCompanions</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdComplete</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdError</code> </td> <td> <code>JWPlayerDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdImpression</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdMeta</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdPause</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdPlay</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdRequest</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdSchedule</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdSkipped</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdStarted</code> </td> <td> <code>JWAdDelegate</code> </td> <td> <code>jwplayer(_ player: AnyObject, adEvent event: JWAdEvent) </code> </td> </tr> <tr> <td> <code>onAdTime</code> </td> <td> <code>JWPlayer</code> </td> <td> See: [Time Events](#time-events) </td> </tr> <tr> <td> <code>onAdWarning</code> </td> <td> <code>JWPlayerDelegate</code> </td> <td> <code>jwplayer(_:encounteredWarning:message:)</code> </td> </tr> <tr> <td> <span style="color:red; font-size:13px"><strong><code>onAll</code></span> </td> <td> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /> </td> <td> --- </td> </tr> <tr> <td> <code>onAudioTrack</code> </td> <td> <code>JWAVDelegate</code> </td> <td> <code>jwplayer(_:audioTracksUpdated:)</code> </td> </tr> <tr> <td> <code>onAudioTrackChanged</code> </td> <td> <code>JWAVDelegate</code> </td> <td> <code>jwplayer(_:audioTrackChanged:)</code> </td> </tr> <tr> <td> <code>onBeforeComplete</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayerContentWillComplete(_:)</code> </td> </tr> <tr> <td> <code>onBeforePlay</code></td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:willPlayWithReason:)</code> </td> </tr> <tr> <td> <code>onBuffer</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayerContentIsBuffering(_:)</code> </td> </tr> <tr> <td> <code>onBufferChange</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:updatedBuffer:position:)</code> </td> </tr> <tr> <td> <code>onCaptionsChanged</code> </td> <td> <code>JWAVDelegate</code> </td> <td> <code>jwplayer(_:captionTrackChanged:)</code> </td> </tr> <tr> <td> <code>onCaptionsList</code> </td> <td> <code>JWAVDelegate</code> </td> <td> <code>jwplayer(_:updatedCaptionList:)</code> </td> </tr> <tr> <td> <code>onComplete</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayerContentDidComplete(_:)</code> </td> </tr> <tr> <td> <code>onControlbarVisible</code> </td> <td> <code>JWPlayerViewControllerDelegate</code> </td> <td> <code>playerViewController(_:controlBarVisibilityChanged)</code> </td> </tr> <tr> <td> <span style="color:red; font-size:13px"><strong><code>onControls</code></span> </td> <td> <img src="https://img.shields.io/badge/DEPRECATED-%20-yellow" /> </td> <td> --- </td> </tr> <tr> <td> <code>onDisplayClick</code> </td> <td> <code>JWPlayerViewControllerDelegate</code> </td> <td> <code>playerViewController(_:screenTappedAt:)</code> </td> </tr> <tr> <td> <code>onError</code> </td> <td> <code>JWPlayerDelegate</code> </td> <td> <code>jwplayer(_:failedWithError:message:)</code> </td> </tr> <tr> <td> <code>onFirstFrame</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:didFinishLoadingWithTime:)</code> </td> </tr> <tr> <td> <code>onFullscreen</code> </td> <td> <code>JWViewControllerDelegate</code> </td> <td> <code>playerViewControllerDidGoFullScreen(_:)</code> </td> </tr> <tr> <td> <code>onFullscreenRequested</code> </td> <td> <code>JWViewControllerDelegate</code> </td> <td> <code>playerViewControllerWillGoFullScreen(_:fullScreenViewController:)</code> </td> </tr> <tr> <td> <code>onIdle</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:didBecomeIdleWithReason:)</code> </td> </tr> <tr> <td> <code>onLevels</code> </td> <td> <code>JWAVDelegate</code> </td> <td> <code>jwplayer(_:qualityLevelsUpdated:)</code> </td> </tr> <tr> <td> <code>onLevelsChanged</code> </td> <td> <code>JWAVDelegate</code> </td> <td> <code>jwplayer(_:qualityLevelChanged:)</code> </td> </tr> <tr> <td> <code>onMeta</code> </td> <td> <code>JWPlaybackMetadataDelegate</code> </td> <td> <code>jwplayer(_:didReceiveMetadata:)</code> </td> </tr> <tr> <td> <code>onPause</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:didPauseWithReason:)</code> </td> </tr> <tr> <td> <code>onPlay</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:isPlayingWithReason:)</code> </td> </tr> <tr> <td> <code>onPlayAttempt</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:isAttemptingToPlay:reason:)</code> </td> </tr> <tr> <td> <code>onPlaybackRateChanged</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:playbackRateChangedTo:at:)</code> </td> </tr> <tr> <td> <code>onPlaylist</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:didLoadPlaylist:)</code> </td> </tr> <tr> <td> <code>onPlaylistComplete</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayerPlaylistHasCompleted(_:)</code> </td> </tr> <tr> <td> <code>onPlaylistItem</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:didLoadPlaylistItem:at:)</code> </td> </tr> <tr> <td> <code>onReady</code> </td> <td> <code>JWPlayerDelegate</code> </td> <td> <code>jwplayerIsReady(_:)</code> </td> </tr> <tr> <td> <code>onRelatedClose</code> </td> <td> <code>JWPlayerViewControllerDelegate</code> </td> <td> <code>playerViewController(_:relatedMenuClosedWithMethod:)</code> </td> </tr> <tr> <td> <code>onRelatedOpen</code> </td> <td> <code>JWPlayerViewControllerDelegate</code> </td> <td> <code>playerViewController(_:relatedMenuOpenedWithItem:withMethod:)</code> </td> </tr> <tr> <td> <code>onRelatedPlay</code> </td> <td> <code>JWPlayerViewControllerDelegate</code> </td> <td> <code>playerViewController(_:relatedItemBeganPlaying:atIndex:withMethod:)</code> </td> </tr> <tr> <td> <code>onResize</code> </td> <td> <code>JWViewControllerDelegate</code><br /><code>JWPlayerViewDelegate</code> </td> <td> <code>playerViewController(_:sizeChangedFrom:to:)</code><br /><code>playerView(_:sizeChangedFrom:to:)</code> </td> </tr> <tr> <td> <code>onSeek</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayerHasSeeked(_:)</code> </td> </tr> <tr> <td> <code>onSeeked</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:seekedFrom:to:)</code> </td> </tr> <tr> <td> <code>onSetupError</code> </td> <td> <code>JWPlayerDelegate</code> </td> <td> <code>jwplayer(_:failedWithSetupError:message:)</code> </td> </tr> <tr> <td> <code>onTime</code> </td> <td> <code>JWPlayer</code> </td> <td> See: [Time Events](#time-events) </td> </tr> <tr> <td> <code>onViewable</code> </td> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:isVisible:)</code> </td> </tr> <tr> <td> <code>onWarning</code> </td> <td> <code>JWPlayerDelegate</code> </td> <td> <code>jwplayer(_:encounteredWarning:message:)</code> </td> </tr> </tbody> </table>

<br /> <br />

### Additional Callbacks

New callbacks have been added to iOS SDK 4.x.

<table> <thead> <tr> <th> 3.x </th> <th> 4.x </th> <th> Callback </th> </tr> </thead> <tbody> <tr> <td width="10%"> <code>JWAVDelegate</code></td> <td width="10%"> <code>jwplayer(_:captionPresented:at:)</code> </td> <td width="80%"> Called when a caption is being presented on the screen </td> </tr> <tr> <td> <code>JWPlayerStateDelegate</code> </td> <td> <code>jwplayer(_:usesMediaType:)</code> </td> <td> Reports when the type of media has been determined </td> </tr> </tbody> </table>

<br /> <br />

### Time Events

The iOS SDK allows you to listen to two different categories of time events: ad time and media time. To listen to these events, you must supply a closure which receives a `JWTimeData` object as a parameter.

The `JWTimeData` object contains a `position` (the number of seconds since the beginning of the content whether it is media or an advertisement) and a `duration` (the length of the currently playing content expressed as a number of seconds). When active, each of these time events fire every 100 milliseconds.

  • **Ad time events** only report while an ad is playing. This type of event reports the number of seconds the current ad has been playing as well as the duration of the current ad.

  • **Media time events** only report while the current `JWPlayerItem`, your main content, is playing. These events do not report while an ad is playing. This type of event reports the number of seconds the content has been playing, as well as the total duration of the content. Neither of these values include the playback duration of an ad if the ad has already been played.



Creating a time observer is best used if you are going to respond to the time events.

For situations in which you need to query the position and duration of the currently playing media at non-specific intervals, it is best to retrieve the value of the time property in `JWPlayer`.

<br />

If you have implemented the player using a `JWPlayerViewController`, these events are observed by overriding methods using your subclass.



<br />

If you have elected to create the player by only instantiating a `JWPlayerView`, you must supply a closure to the `JWPlayer` object.



<br /> <hr />

## Advertising API

In iOS SDK 4.x, the Advertising API maintains support for VAST, VMAP, Google IMA, and Google DAI advertising.

In iOS SDK 3.x, there was an advertising configuration property used to set up any type of ads. In iOS SDK 4.x, you can set up the advertising configuration through new dedicated builders for each type of ad.

AdvertisingBuilder
**VAST** & **VMAP**`JWAdsAdvertisingConfigBuilder`
**Google IMA**`JWImaAdvertisingConfigBuilder`
**Google DAI**`JWImaDaiAdvertisingConfigBuilder`

<br />

To begin running advertising in iOS SDK 4.x, you must create a `JWPlayerConfigurationBuilder` to include the advertising configuration. Refer to [Configuration API](🔗) and [Set up a basic player](🔗) for more info about setting up the player.



  • FreeWheel and VPAID advertising are no longer supported by iOS SDK 4.x.

  • You must include the <a href="https://imasdk.googleapis.com/native/downloads/ima-ios-v3.11.4.zip" target="_blank">GoogleIMA SDK v3.11.4</a> in your project if you want to monetize advertising through IMA or DAI.

  • For examples on creating advertisements, please refer to the [advertising documentation](🔗).

<br />

### Listening to advertising events

In iOS SDK 4.x, there is a dedicated delegate for the advertising events: `JWAdDelegate`. Refer to [Event Migration](🔗) for more info about the implementation of these delegates.

In iOS SDK 3.x, the SDK reported the ad time event by constantly calling the delegate method informing the current time of an ad. In iOS SDK 4.x, the ad time event is reported in a different way that increases the performance of listening for this event. Refer to [Time Events](🔗) for more information.

<br />

<!-- Removes the automatic page-to-page navigation at the bottom of the page -->

<style> .rm-Pagination { display: none; } </style>