{"id":5191,"date":"2023-05-01T11:37:44","date_gmt":"2023-05-01T15:37:44","guid":{"rendered":"https:\/\/ionic.io\/blog\/?p=5191"},"modified":"2023-05-01T11:37:45","modified_gmt":"2023-05-01T15:37:45","slug":"creating-your-own-media-plugin-for-capacitor-an-ios-tutorial","status":"publish","type":"post","link":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial","title":{"rendered":"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial"},"content":{"rendered":"\n<p>If you read my recent blog post on <a href=\"https:\/\/ionic.io\/blog\/ionic-capacitor-the-best-path-for-performance\">Progressive Web Apps<\/a>, I covered how performant Ionic and Capacitor can be when creating media-heavy projects like video streaming apps. By utilizing libraries like hls.js in our PWAs, it empowers us to effortlessly create media-rich apps. However, while using libraries like hls.js are great for PWAs, you might have noticed something if you tried to make that demo app cross-platform: hls.js doesn\u2019t work on mobile.<\/p>\n\n\n\n<p>While this may seem like an insurmountable challenge, you are able to get video streaming working in a cross-platform Capacitor application by building a custom <a href=\"https:\/\/ionic.io\/blog\/capacitor-everything-youve-ever-wanted-to-know\">Capacitor<\/a> plugin. In this tutorial, I\u2019ll walk you through how to create your own Capacitor plugin for iOS that interfaces with UIKit, AVKit, and AVFoundation to demonstrate how to get streaming videos playing in your own applications.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Before We Begin<\/h1>\n\n\n\n<p>This tutorial was heavily inspired by Simon Grimm\u2019s <a href=\"https:\/\/youtu.be\/Nf-mOfmD7X4\">video<\/a> on creating Capacitor plugins and the Ionic Capacitor plugin <a href=\"https:\/\/capacitorjs.com\/docs\/plugins\/creating-plugins\">documentation<\/a>. My goal for this tutorial is to be an extension of those by making a few updates to address Capacitor plugin changes and demonstrate how to utilize video libraries that aren\u2019t normally shown. I plan on creating a few blog posts around this topic to show how this Capacitor plugin will work for web and Android as well. Be sure to check back as I will be updating all the related blog posts as I build the plugin. In the meantime, you will be able to find the completed plugin project <a href=\"https:\/\/github.com\/ionic-team\/capacitor-plugin-video-demo\">here<\/a> and the test application <a href=\"https:\/\/github.com\/ionic-team\/capacitor-plugin-video-demo-test-project\">here<\/a> so feel free to check it out or follow along.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">The Media Streaming Problem<\/h1>\n\n\n\n<p>Let\u2019s discuss why hls.js doesn\u2019t work on mobile beyond the fact that it\u2019s a JavaScript library: Every platform handles media differently, and major tech companies are fighting for control in the streaming space. Different steps are required to get the streaming videos working depending on the streaming protocol that you are using.<\/p>\n\n\n\n<p>In this tutorial, the video format I\u2019m using is HLS, a protocol developed by Apple. It&#8217;s widely supported and can be used on web, iOS, and Android, but the implementation requires us to implement it at the device level or use libraries to get it to work. Lucky for us, HLS is natively supported by Apple in iOS, so it&#8217;s simple to implement.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Building The Plugin&nbsp;<\/h1>\n\n\n\n<p>The first step is to initialize the Capacitor Plugin builder in your terminal and go through the prompts. If you plan on publishing your plugin, be sure to make these values <a href=\"https:\/\/capacitorjs.com\/docs\/plugins\/creating-plugins\">unique<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm init @capacitor\/plugin<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"403\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-1024x403.png\" alt=\"\" class=\"wp-image-5194 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-1024x403.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-300x118.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-768x302.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10.png 1516w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/403;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"403\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-1024x403.png\" alt=\"\" class=\"wp-image-5194\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-1024x403.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-300x118.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10-768x302.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-10.png 1516w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/noscript><\/figure>\n\n\n\n<p>Once the CLI has built your Capacitor plugin project, navigate to your project\u2019s directory and run the build:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">cd capacitor-plugin-video-demo\nnpm run build<\/code><\/pre>\n\n\n\n<p>With the project built, you should see the following project structure:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"788\" height=\"732\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8.png\" alt=\"\" class=\"wp-image-5192 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8.png 788w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8-300x279.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8-768x713.png 768w\" data-sizes=\"auto, (max-width: 788px) 100vw, 788px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 788px; --smush-placeholder-aspect-ratio: 788\/732;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"788\" height=\"732\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8.png\" alt=\"\" class=\"wp-image-5192\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8.png 788w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8-300x279.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-8-768x713.png 768w\" sizes=\"auto, (max-width: 788px) 100vw, 788px\" \/><\/noscript><\/figure>\n\n\n\n<p>Navigate to <em>src\/definitions.ts <\/em>and copy\/paste the following line of code as this will allow us to call the playVideo method in our project:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">playVideo(video: string): Promise&lt;void&gt;;<\/code><\/pre>\n\n\n\n<p>The full <em>definitions.ts<\/em> file should look like this:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">export interface CapacitorPluginVideoDemoPlugin {\n\u00a0\u00a0echo(options: { value: string }): Promise&lt;{ value: string }&gt;;\n\u00a0\u00a0playVideo(video: string): Promise&lt;void&gt;;\n}<\/code><\/pre>\n\n\n\n<p>Next go to <em>src\/web.ts<\/em> and add the following (I believe this just pertains to the web implementation, but I will be adding it since we\u2019re here):<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">async playVideo(video: string): Promise&lt;void&gt; {\n\u00a0\u00a0\u00a0\u00a0console.log(&#039;ECHO&#039;, video);\n}<\/code><\/pre>\n\n\n\n<p><em>web.ts <\/em>should look like this<em>:<\/em><\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">import { WebPlugin } from &#039;@capacitor\/core&#039;;\nimport type { CapacitorPluginVideoDemoPlugin } from &#039;.\/definitions&#039;;\n\nexport class CapacitorPluginVideoDemoWeb\n\u00a0\u00a0extends WebPlugin\n\u00a0\u00a0implements CapacitorPluginVideoDemoPlugin\n{\n\n\u00a0\u00a0async playVideo(video: string): Promise&lt;void&gt; {\n\u00a0\u00a0\u00a0\u00a0console.log(&#039;ECHO&#039;, video);\n\u00a0\u00a0}\n\n}<\/code><\/pre>\n\n\n\n<p>Our plugin is done for now, so let\u2019s build the project (*Note: The approach we\u2019re taking is locally creating and editing a plugin, but you could publish this to NPM then install it in your test project):<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm run build<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Creating A Test App<\/h1>\n\n\n\n<p>With our plugin built or published to NPM, we now need a project to test it in, so let\u2019s create a new Ionic Angular project:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">ionic start videoPluginTest blank --type=angular --capacitor<\/code><\/pre>\n\n\n\n<p>Open videoPluginTest in your IDE and install your custom Capacitor plugin:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm install ..\/capacitor-plugin-video-demo<\/code><\/pre>\n\n\n\n<p>After the plugin is installed, you should be able to see it in your <em>package.json <\/em>file:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"198\" data-src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-1024x198.png\" alt=\"\" class=\"wp-image-5193 lazyload\" data-srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-1024x198.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-300x58.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-768x148.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-1536x297.png 1536w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9.png 1600w\" data-sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" src=\"data:image\/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==\" style=\"--smush-placeholder-width: 1024px; --smush-placeholder-aspect-ratio: 1024\/198;\" \/><noscript><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"198\" src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-1024x198.png\" alt=\"\" class=\"wp-image-5193\" srcset=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-1024x198.png 1024w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-300x58.png 300w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-768x148.png 768w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9-1536x297.png 1536w, https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/image-9.png 1600w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/noscript><\/figure>\n\n\n\n<p>With everything installed properly, we now need a way to get the video to play, so we\u2019re just going to implement a simple play button:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">&lt;ion-header [translucent]=&quot;true&quot;&gt;\n\u00a0\u00a0&lt;ion-toolbar&gt;\n\u00a0\u00a0\u00a0\u00a0&lt;ion-title&gt;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Video Test\n\u00a0\u00a0\u00a0\u00a0&lt;\/ion-title&gt;\n\u00a0\u00a0&lt;\/ion-toolbar&gt;\n&lt;\/ion-header&gt;\n\n&lt;ion-content [fullscreen]=&quot;true&quot;&gt;\n\u00a0\u00a0&lt;ion-button expand=&quot;full&quot; (click)=&quot;playVideo(&#039;https:\/\/hlsdemooutput.s3.us-west-2.amazonaws.com\/Ionic+in+30+Series_+Ionic+Angular.m3u8&#039;)&quot;&gt;\n\u00a0\u00a0\u00a0\u00a0Play Video\n\u00a0\u00a0&lt;\/ion-button&gt;\n&lt;\/ion-content&gt;<\/code><\/pre>\n\n\n\n<p>Go to our <em>home.page.ts<\/em>, import the plugin, and add the playVideo method:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">import { Component } from &#039;@angular\/core&#039;;\nimport { IonicModule } from &#039;@ionic\/angular&#039;;\nimport {CapacitorPluginVideoDemo} from &quot;..\/..\/..\/..\/capacitor-plugin-video-demo&quot;;\n\n@Component({\n\u00a0\u00a0selector: &#039;app-home&#039;,\n\u00a0\u00a0templateUrl: &#039;home.page.html&#039;,\n\u00a0\u00a0styleUrls: [&#039;home.page.scss&#039;],\n\u00a0\u00a0standalone: true,\n\u00a0\u00a0imports: [IonicModule],\n})\n\nexport class HomePage {\n\u00a0\u00a0video = &lt;any&gt;[];\n\u00a0\u00a0constructor() {}\n\n\u00a0\u00a0async playVideo(video: string) {\n\u00a0\u00a0\u00a0\u00a0await CapacitorPluginVideoDemo.playVideo(video)\n\u00a0\u00a0}\n\n}<\/code><\/pre>\n\n\n\n<p>Now that we have that, we\u2019ll need to build the test application, install the Capacitor\/iOS package, sync with iOS, and then open our project in XCode:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">npm run build\nnpm install @capacitor\/ios\nnpx cap add ios\nnpx cap sync ios\nnpx cap open ios<\/code><\/pre>\n\n\n\n<h1 class=\"wp-block-heading\">Adding Video To The Plugin<\/h1>\n\n\n\n<p>In XCode we\u2019ll update our Capacitor plugin by navigating to <em>Pods\/Development Pods\/CapacitorPluginVideoDemoPlugin.swift<\/em> and replace it with the code below (You can do this in your web IDE, but I find it easier in XCode with Swift code completion):<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">import Foundation\nimport Capacitor\nimport UIKit\nimport AVKit\n\n\/**\n\u00a0* Please read the Capacitor iOS Plugin Development Guide\n\u00a0* here: https:\/\/capacitorjs.com\/docs\/plugins\/ios\n\u00a0*\/\n\n@objc(CapacitorPluginVideoDemoPlugin)\npublic class CapacitorPluginVideoDemoPlugin: CAPPlugin {\n\u00a0\u00a0\u00a0\u00a0private let implementation = CapacitorPluginVideoDemo()\n\n\u00a0\u00a0\u00a0\u00a0@objc func echo(_ call: CAPPluginCall) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0let value = call.getString(&quot;value&quot;) ?? &quot;&quot;\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0call.resolve([\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&quot;value&quot;: implementation.echo(value)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0])\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0@objc func playVideo(_ call: CAPPluginCall) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0guard let url = URL(string: &quot;https:\/\/hlsdemooutput.s3.us-west-2.amazonaws.com\/Ionic+in+30+Series_+Ionic+Angular.m3u8&quot;) else { return }\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0let player = AVPlayer(url: url)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0DispatchQueue.main.sync {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Create a new AVPlayerViewController and pass it a reference to the player.\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0let controller = AVPlayerViewController()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0controller.player = player\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Modally present the player and call the player&#039;s play() method when complete.\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0self.bridge?.viewController?.present(controller, animated: true) {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0player.play()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0}\n}<\/code><\/pre>\n\n\n\n<p>To help understand what\u2019s happening, this is the playVideo logic and more information about native views in Capacitor can be found <a href=\"https:\/\/capacitorjs.com\/docs\/core-apis\/ios#viewcontroller\">here<\/a>:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">@objc func playVideo(_ call: CAPPluginCall) {\n\n\/\/This is our video URL that we&#039;re converting from a string to a URL\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0guard let url = URL(string: &quot;https:\/\/hlsdemooutput.s3.us-west-2.amazonaws.com\/Ionic+in+30+Series_+Ionic+Angular.m3u8&quot;) else { return }\n\n\/\/This instantiates our player\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0let player = AVPlayer(url: url)\n\n\/\/This is a weird iOS quirk where only the main thread can update the view\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0DispatchQueue.main.sync {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Create a new AVPlayerViewController and pass it a reference to the player.\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0let controller = AVPlayerViewController()\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0controller.player = player\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Modally present the player and call the player&#039;s play() method when complete.\n            \/\/This lets us communicate with Capacitor to let us show a native view\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0self.bridge?.viewController?.present(controller, animated: true) {\n\n                    \/\/If it works, play the video\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0player.play()\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0}<\/code><\/pre>\n\n\n\n<p>Then navigate to the <em>CapacitorPluginVideoDemoPlugin.m<\/em> and update the code to include the playVideo method to bind our Capacitor project with our plugin:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">#import &lt;Foundation\/Foundation.h&gt;\n#import &lt;Capacitor\/Capacitor.h&gt;\n\n\/\/ Define the plugin using the CAP_PLUGIN Macro, and\n\/\/ each method the plugin supports using the CAP_PLUGIN_METHOD macro.\n\nCAP_PLUGIN(CapacitorPluginVideoDemoPlugin, &quot;CapacitorPluginVideoDemo&quot;,\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise);\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0CAP_PLUGIN_METHOD(playVideo, CAPPluginReturnPromise);\n\n)<\/code><\/pre>\n\n\n\n<p>Finally, we need to update the AppDelegate file to configure the <a href=\"https:\/\/developer.apple.com\/documentation\/avfoundation\/media_playback\/creating_a_basic_video_player_ios_and_tvos\">audio<\/a> of our video application with the following code:<\/p>\n\n\n\n<pre class=\"wp-block-prismatic-blocks\"><code class=\"language-\">import UIKit\nimport Capacitor\nimport AVFoundation\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n\u00a0\u00a0\u00a0\u00a0var window: UIWindow?\n\n\u00a0\u00a0\u00a0\u00a0func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -&gt; Bool {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Override point for customization after application launch.\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0let audioSession = AVAudioSession.sharedInstance()\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0do {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0try audioSession.setCategory(.playback, mode: .moviePlayback)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0catch {\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0print(&quot;Setting category to AVAudioSessionCategoryPlayback failed.&quot;)\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return true\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0func applicationWillResignActive(_ application: UIApplication) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0func applicationDidEnterBackground(_ application: UIApplication) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0func applicationWillEnterForeground(_ application: UIApplication) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0func applicationDidBecomeActive(_ application: UIApplication) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0func applicationWillTerminate(_ application: UIApplication) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -&gt; Bool {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Called when the app was launched with a url. Feel free to add additional processing here,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ but if you want the App API to support tracking app url opens, make sure to keep this call\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ApplicationDelegateProxy.shared.application(app, open: url, options: options)\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -&gt; Void) -&gt; Bool {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Called when the app was launched with an activity, including Universal Links.\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ Feel free to add additional processing here, but if you want the App API to support\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/\/ tracking app url opens, make sure to keep this call\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)\n\n\u00a0\u00a0\u00a0\u00a0}\n\n}<\/code><\/pre>\n\n\n\n<p>With that completed, you now should be able to test your Capacitor plugin for iOS! As of this writing, you will not be able to use the built-in XCode simulator due to a bug with AVKit. However, you should be able to plug in your iPhone and launch the application directly on your phone with the following result:<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video height=\"720\" style=\"aspect-ratio: 1560 \/ 720;\" width=\"1560\" controls src=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/RPReplay_Final1682039221.mp4\"><\/video><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Conclusion<\/h1>\n\n\n\n<p>By creating your own custom Capacitor plugins, you can unlock more native experiences and up-level your cross-platform applications. In this example, we were able to interface with AVKit to create a basic video streaming application that will allow you to watch streaming videos in iOS and utilize features like AirPlay in your apps with only a few lines of code. If you are looking to extend the functionality of this player or want to try working with other libraries, definitely check out Apple\u2019s <a href=\"https:\/\/developer.apple.com\/videos\/\">Developer<\/a> page to learn more about what you can interface with, and be sure to follow the Ionic <a href=\"https:\/\/ionic.io\/blog\">Blog<\/a> for more content like this!<\/p>\n\n\n\n<p>If you have any questions about this project, feel free to <a href=\"https:\/\/ionic.io\/blog\/support\">contact<\/a> us in the forum or the official <a href=\"https:\/\/discord.com\/invite\/UPYYRhtyzp\">Discord<\/a> page, and we\u2019ll do our best to help!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you read my recent blog post on Progressive Web Apps, I covered how performant Ionic and Capacitor can be when creating media-heavy projects like video streaming apps. By utilizing libraries like hls.js in our PWAs, it empowers us to effortlessly create media-rich apps. However, while using libraries like hls.js are great for PWAs, you [&hellip;]<\/p>\n","protected":false},"author":103,"featured_media":5197,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"publish_to_discourse":"","publish_post_category":"","wpdc_auto_publish_overridden":"","wpdc_topic_tags":"","wpdc_pin_topic":"","wpdc_pin_until":"","discourse_post_id":"","discourse_permalink":"","wpdc_publishing_response":"","wpdc_publishing_error":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1,123,124],"tags":[151,23,100,25],"class_list":["post-5191","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-all","category-perspectives","category-tutorials","tag-capacitor","tag-framework","tag-ios","tag-tutorials"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v23.0 (Yoast SEO v23.0) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Creating Your Own Media Plugin For Capacitor: An iOS Tutorial - Ionic Blog<\/title>\n<meta name=\"description\" content=\"Learn how to create custom media-rich Capacitor plugins that will work on iOS.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Creating Your Own Media Plugins For Capacitor: An iOS Tutorial\" \/>\n<meta property=\"og:description\" content=\"In this tutorial we&#039;ll cover how you can create a custom video streaming Capacitor plugin for iOS!\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial\" \/>\n<meta property=\"og:site_name\" content=\"Ionic Blog\" \/>\n<meta property=\"article:published_time\" content=\"2023-05-01T15:37:44+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2023-05-01T15:37:45+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2240\" \/>\n\t<meta property=\"og:image:height\" content=\"1120\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Logan Brade\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"Creating Your Own Media Plugins For Capacitor: An iOS Tutorial\" \/>\n<meta name=\"twitter:description\" content=\"In this tutorial we&#039;ll cover how you can create a custom video streaming Capacitor plugin for iOS!\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png\" \/>\n<meta name=\"twitter:creator\" content=\"@loganbrade\" \/>\n<meta name=\"twitter:site\" content=\"@ionicframework\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Logan Brade\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"9 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#article\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial\"},\"author\":{\"name\":\"Logan Brade\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/07a04c81f6b3180076d1a6ac967bc562\"},\"headline\":\"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial\",\"datePublished\":\"2023-05-01T15:37:44+00:00\",\"dateModified\":\"2023-05-01T15:37:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial\"},\"wordCount\":1017,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png\",\"keywords\":[\"Capacitor\",\"Framework\",\"iOS\",\"Tutorials\"],\"articleSection\":[\"All\",\"Perspectives\",\"Tutorials\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial\",\"url\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial\",\"name\":\"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial - Ionic Blog\",\"isPartOf\":{\"@id\":\"https:\/\/ionic.io\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage\"},\"thumbnailUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png\",\"datePublished\":\"2023-05-01T15:37:44+00:00\",\"dateModified\":\"2023-05-01T15:37:45+00:00\",\"description\":\"Learn how to create custom media-rich Capacitor plugins that will work on iOS.\",\"breadcrumb\":{\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png\",\"width\":2240,\"height\":1120},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/ionic.io\/blog\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/ionic.io\/blog\/#website\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"name\":\"ionic.io\/blog\",\"description\":\"Build amazing native and progressive web apps with the web\",\"publisher\":{\"@id\":\"https:\/\/ionic.io\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/ionic.io\/blog\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/ionic.io\/blog\/#organization\",\"name\":\"Ionic\",\"url\":\"https:\/\/ionic.io\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png\",\"width\":1920,\"height\":854,\"caption\":\"Ionic\"},\"image\":{\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/ionicframework\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/07a04c81f6b3180076d1a6ac967bc562\",\"name\":\"Logan Brade\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/03\/IMG_2854-150x150.jpg\",\"contentUrl\":\"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/03\/IMG_2854-150x150.jpg\",\"caption\":\"Logan Brade\"},\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/loganbrade\/\",\"https:\/\/x.com\/loganbrade\"],\"url\":\"https:\/\/ionic.io\/blog\/author\/logan-brade\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial - Ionic Blog","description":"Learn how to create custom media-rich Capacitor plugins that will work on iOS.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial","og_locale":"en_US","og_type":"article","og_title":"Creating Your Own Media Plugins For Capacitor: An iOS Tutorial","og_description":"In this tutorial we'll cover how you can create a custom video streaming Capacitor plugin for iOS!","og_url":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial","og_site_name":"Ionic Blog","article_published_time":"2023-05-01T15:37:44+00:00","article_modified_time":"2023-05-01T15:37:45+00:00","og_image":[{"width":2240,"height":1120,"url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png","type":"image\/png"}],"author":"Logan Brade","twitter_card":"summary_large_image","twitter_title":"Creating Your Own Media Plugins For Capacitor: An iOS Tutorial","twitter_description":"In this tutorial we'll cover how you can create a custom video streaming Capacitor plugin for iOS!","twitter_image":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png","twitter_creator":"@loganbrade","twitter_site":"@ionicframework","twitter_misc":{"Written by":"Logan Brade","Est. reading time":"9 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#article","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial"},"author":{"name":"Logan Brade","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/07a04c81f6b3180076d1a6ac967bc562"},"headline":"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial","datePublished":"2023-05-01T15:37:44+00:00","dateModified":"2023-05-01T15:37:45+00:00","mainEntityOfPage":{"@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial"},"wordCount":1017,"commentCount":0,"publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"image":{"@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png","keywords":["Capacitor","Framework","iOS","Tutorials"],"articleSection":["All","Perspectives","Tutorials"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial","url":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial","name":"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial - Ionic Blog","isPartOf":{"@id":"https:\/\/ionic.io\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage"},"image":{"@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage"},"thumbnailUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png","datePublished":"2023-05-01T15:37:44+00:00","dateModified":"2023-05-01T15:37:45+00:00","description":"Learn how to create custom media-rich Capacitor plugins that will work on iOS.","breadcrumb":{"@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#primaryimage","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png","width":2240,"height":1120},{"@type":"BreadcrumbList","@id":"https:\/\/ionic.io\/blog\/creating-your-own-media-plugin-for-capacitor-an-ios-tutorial#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/ionic.io\/blog"},{"@type":"ListItem","position":2,"name":"Creating Your Own Media Plugin For Capacitor: An iOS Tutorial"}]},{"@type":"WebSite","@id":"https:\/\/ionic.io\/blog\/#website","url":"https:\/\/ionic.io\/blog\/","name":"ionic.io\/blog","description":"Build amazing native and progressive web apps with the web","publisher":{"@id":"https:\/\/ionic.io\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/ionic.io\/blog\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/ionic.io\/blog\/#organization","name":"Ionic","url":"https:\/\/ionic.io\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2020\/10\/white-on-color.png","width":1920,"height":854,"caption":"Ionic"},"image":{"@id":"https:\/\/ionic.io\/blog\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/ionicframework"]},{"@type":"Person","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/07a04c81f6b3180076d1a6ac967bc562","name":"Logan Brade","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ionic.io\/blog\/#\/schema\/person\/image\/","url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/03\/IMG_2854-150x150.jpg","contentUrl":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/03\/IMG_2854-150x150.jpg","caption":"Logan Brade"},"sameAs":["https:\/\/www.linkedin.com\/in\/loganbrade\/","https:\/\/x.com\/loganbrade"],"url":"https:\/\/ionic.io\/blog\/author\/logan-brade"}]}},"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/ionic.io\/blog\/wp-content\/uploads\/2023\/04\/capacitor-plugin-feature-image.png","_links":{"self":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5191","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/users\/103"}],"replies":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/comments?post=5191"}],"version-history":[{"count":3,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5191\/revisions"}],"predecessor-version":[{"id":5204,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/posts\/5191\/revisions\/5204"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media\/5197"}],"wp:attachment":[{"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/media?parent=5191"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/categories?post=5191"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ionic.io\/blog\/wp-json\/wp\/v2\/tags?post=5191"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}