<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>iOS App Development – IMG.LY Blog</title><description>Posts tagged iOS App Development on the IMG.LY blog.</description><link>https://img.ly/blog/tag/ios-app-development/</link><language>en-us</language><image><url>https://img.ly/apple-touch-icon.png</url><title>iOS App Development – IMG.LY Blog</title><link>https://img.ly/blog/tag/ios-app-development/</link></image><atom:link href="https://img.ly/blog/tag/ios-app-development/rss.xml" rel="self" type="application/rss+xml"/><generator>Astro</generator><lastBuildDate>Tue, 09 Jun 2026 09:48:34 GMT</lastBuildDate><ttl>60</ttl><item><title>CE.SDK v1.73 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-73-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-73-0-release-notes/</guid><description>v1.73 brings Starter Kits to iOS &amp; Android, giving you full ownership of your mobile editor config. Plus SVG export across platforms.</description><pubDate>Tue, 21 Apr 2026 13:29:35 GMT</pubDate><content:encoded>&lt;p&gt;v1.73 is a mobile-focused release. The main act is our new architectural foundation for iOS and Android: Starter Kits give you full ownership of your editor configuration. On the engine side, CE.SDK now supports SVG export across all platforms.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;own-your-mobile-editor--not-just-the-config&quot;&gt;Own Your Mobile Editor — Not Just the Config&lt;/h2&gt;
&lt;p&gt;The old model was simple: drop in a solution component (PhotoEditor, VideoEditor, DesignEditor), pass a license key, ship. For most teams, that was exactly the right starting point. Fast to integrate, zero configuration overhead, a full editor running in your app within hours.&lt;/p&gt;
&lt;p&gt;As products matured, though, teams needed more. Deeper customization. Granular control over behaviors. An editor that felt like theirs.&lt;/p&gt;
&lt;p&gt;That’s what &lt;strong&gt;Starter Kits for Mobile&lt;/strong&gt; addresses.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Set up fast, customize, and own your code - now for iOS &amp;amp;#x26; Android&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 2000px) 2000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;2000&quot; height=&quot;857&quot; src=&quot;https://img.ly/_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_Zuekpc.webp&quot; srcset=&quot;/_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_hid7x.webp 640w, /_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_1fMlOs.webp 750w, /_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_14DV9O.webp 828w, /_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_Z2opymo.webp 1080w, /_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_ZEa6Mt.webp 1280w, /_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_Z2tuMOW.webp 1668w, /_astro/starter-kits-design-editor-video-apparel-mobile-ios-android_Zuekpc.webp 2000w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The prebuilt solution components are replaced by a single &lt;code&gt;Editor&lt;/code&gt; entry point and a set of Starter Kits: complete, working editor projects for each use case that live in your codebase, not ours. Set it up in minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For your product, this means:&lt;/strong&gt; you can adapt the editor to fit your product, not the other way around.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everything Is In Your Code Now&lt;/strong&gt;&lt;br&gt;
Each Starter Kit is a real editor project — not a thin config wrapper. Every toolbar item, every callback, every loading phase is written out explicitly in files you own.&lt;/p&gt;
&lt;p&gt;That means your team can read the integration, review changes to it, and understand exactly what the editor does and why. No more hunting through SDK internals to figure out why something behaves the way it does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SDK Updates Stop Touching Your UI&lt;/strong&gt;&lt;br&gt;
Previously, updating CE.SDK could introduce new buttons, changed defaults, or different menus without you asking for them. Your QA had to catch it.&lt;/p&gt;
&lt;p&gt;That dynamic is reversed now. Engine improvements, bug fixes, and performance gains come through updates as before. But your editor UI stays exactly where you left it. New features arrive as optional building blocks, documented and ready when you want them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No More Customization Ceiling&lt;/strong&gt;&lt;br&gt;
The old configuration APIs had a line between what you could change and what you couldn’t. Starter Kits remove that line entirely. You can restructure initialization, inject custom logic between loading phases, and build workflows specific to your product.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you can see it in the code, you can change it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If none of the five kits match your needs, the &lt;code&gt;Editor&lt;/code&gt; view accepts raw configuration directly.&lt;/p&gt;
&lt;h3 id=&quot;migration&quot;&gt;Migration&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;This is a breaking change on both iOS and Android. Migration guides cover every change with before-and-after examples.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;→ &lt;a href=&quot;https://img.ly/docs/cesdk/android/to-v1-73-ab14fb/&quot;&gt;Android Migration Guide&lt;/a&gt;&lt;br&gt;
→ &lt;a href=&quot;https://img.ly/docs/cesdk/ios/to-v1-73-ab14fb/&quot;&gt;iOS Migration Guide&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;starter-kit-repositories&quot;&gt;Starter Kit Repositories&lt;/h3&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Editor&lt;/th&gt;&lt;th&gt;iOS (Swift)&lt;/th&gt;&lt;th&gt;Android (Kotlin)&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Photo&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-photo-editor-ios&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-photo-editor-android&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Design&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-design-editor-ios&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-design-editor-android&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Video&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-video-editor-ios&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-video-editor-android&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Postcard&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-postcard-editor-ios&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-postcard-editor-android&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Apparel&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-apparel-editor-ios&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a href=&quot;https://github.com/imgly/starterkit-apparel-editor-android&quot;&gt;GitHub&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;export-vector-designs-as-svg&quot;&gt;Export Vector Designs as SVG&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/svg-export_sXlp.webp&quot; srcset=&quot;/_astro/svg-export_Z1Gawc7.webp 640w, /_astro/svg-export_2kYdnC.webp 750w, /_astro/svg-export_ZcTYTA.webp 828w, /_astro/svg-export_1pb2OK.webp 1080w, /_astro/svg-export_Hk0A5.webp 1280w, /_astro/svg-export_sXlp.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Until now, users working with vector content in CE.SDK had no path to get that content out as a vector file. CE.SDK v1.73 adds SVG export via the &lt;code&gt;image/svg+xml&lt;/code&gt; MIME type.&lt;/p&gt;
&lt;p&gt;Text is exported as vector paths for consistent rendering across environments regardless of font availability. Drop shadows, blur, effects, and embedded raster images are rasterized and included as embedded images within the SVG output.&lt;/p&gt;
&lt;p&gt;→ &lt;a href=&quot;https://img.ly/docs/cesdk/engine/guides/export-blocks/&quot;&gt;Exporting Blocks Engine Guide&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;changelog&quot;&gt;Changelog&lt;/h2&gt;
&lt;p&gt;See the &lt;a href=&quot;https://img.ly/docs/cesdk/changelog/v1-73-0/&quot;&gt;full changelog&lt;/a&gt; for the complete list.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Thanks for building with IMG.LY.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Join 3,000+ creative professionals who get early access to new features and updates—&lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;subscribe&lt;/a&gt;.&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2026/04/creative-sdk-imgly-173-release-notes-android-ios-design-video-photo-editor-integrate.jpg" medium="image"/><category>Release Notes</category><category>Android App Development</category><category>iOS App Development</category></item><item><title>CE.SDK v1.46 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-46-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-46-0-release-notes/</guid><description>This release gives you full control over the iOS dock and inspector bar, letting you customize actions and buttons for a faster, more intuitive editing experience.</description><pubDate>Tue, 11 Mar 2025 15:58:30 GMT</pubDate><content:encoded>&lt;p&gt;Welcome to a new release! CE.SDK v1.46 introduces a more flexible and intuitive user interface for iOS, allowing you to optimize your workflow with a customizable dock and inspector. Read on for upcoming features.&lt;/p&gt;
&lt;h2 id=&quot;shape-your-ios-interface-around-your-workflow&quot;&gt;Shape Your iOS Interface Around Your Workflow&lt;/h2&gt;
&lt;p&gt;We unlock full &lt;strong&gt;dock and inspector bar customization&lt;/strong&gt;, giving you the flexibility to optimize your workflow. With an open API, you can now tailor these UI elements to better suit your editing needs—whether by reordering actions, adding custom functionality, or simplifying the interface.&lt;/p&gt;
&lt;p&gt;Take control of your interface by modifying both the &lt;strong&gt;dock&lt;/strong&gt; and &lt;strong&gt;inspector bar&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rearrange &amp;#x26; Customize Actions&lt;/strong&gt; – Prioritize your most-used tools by reordering or removing actions to create a cleaner, more efficient workspace.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add Custom Buttons &amp;#x26; Views&lt;/strong&gt; – Extend functionality by integrating &lt;strong&gt;custom buttons or views&lt;/strong&gt; that trigger specific actions, such as opening an asset library or displaying additional information.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These enhancements provide a seamless, intuitive editing experience that adapts to your unique workflow needs.&lt;/p&gt;
&lt;p&gt;Start customizing your &lt;a href=&quot;https://img.ly/docs/cesdk/ios/user-interface/customization/dock-cb916c/&quot;&gt;dock&lt;/a&gt; and &lt;a href=&quot;https://img.ly/docs/cesdk/ios/user-interface/customization/inspector-bar-8ca1cd/&quot;&gt;inspector bar&lt;/a&gt; today with our documentation.&lt;/p&gt;
&lt;h2 id=&quot;upcoming&quot;&gt;Upcoming&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Boost Engagement and Accessibility with Video Captions&lt;/strong&gt;&lt;br&gt;
Captions are essential in today’s social media landscape, where videos often autoplay on mute and clear messaging is key. Our upcoming &lt;strong&gt;Video&lt;/strong&gt; &lt;strong&gt;Captions&lt;/strong&gt; for Web will allow you to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Manually add subtitles to videos&lt;/strong&gt; or upload an &lt;strong&gt;SRT file&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adjust timing and formatting&lt;/strong&gt; in an intuitive interface to match your brand’s style.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embed captions directly&lt;/strong&gt; into videos, ensuring they remain visible across all platforms.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stay tuned—Captions are coming soon!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;View all bug fixes and changes in the &lt;a href=&quot;https://img.ly/docs/cesdk/changelog/#v1461--march-5th-2025&quot;&gt;Changelog&lt;/a&gt;. Thanks for reading!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3,000+ creative professionals gain early access to new features and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i?ref=img.ly&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2025/03/creative-editor-sdk-1-46-imgly.jpg" medium="image"/><category>Release Notes</category><category>CE.SDK</category><category>iOS App Development</category></item><item><title>CE.SDK v1.33 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-33-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-33-0-release-notes/</guid><description>Explore new mobile design and photo editor configurations, plugins for UI customization, voiceover capabilities, and more.</description><pubDate>Thu, 29 Aug 2024 12:08:57 GMT</pubDate><content:encoded>&lt;p&gt;Welcome to the latest update of CE.SDK! We are bringing you new features that enhance your app’s creative capabilities and user experience. This release includes Configurations for a Design Editor or Photo Editor on iOS and Android, Plugins, Voiceover Capabilities, and more.&lt;/p&gt;
&lt;p&gt;With this release, you can:&lt;/p&gt;
&lt;h2 id=&quot;integrate-a-design-editor-on-ios-and-android&quot;&gt;Integrate a Design Editor on iOS and Android&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-33/page-management-cesdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Get started with our new Design Editor configuration for iOS and Android. The out-of-the-box package is designed for easy creation and management of multipage designs.&lt;/p&gt;
&lt;p&gt;Users can switch between editing and the page overview mode to review and adjust their work.&lt;/p&gt;
&lt;p&gt;Keep the design process straightforward and efficient: the editor features a dock at the bottom, offering convenient access to essential tools like adding text, images, stickers, and uploading new assets. Additionally, you can insert a photo directly from your camera.&lt;/p&gt;
&lt;h3 id=&quot;key-features&quot;&gt;&lt;strong&gt;Key Features&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Page Management&lt;/strong&gt;&lt;br&gt;
Create and manage your design pages within a template. It’s easy to create fun carousel posts, as seen on Instagram and LinkedIn, or create a series. Head to the ‘Pages’ icon on the top right bar. In the page overview, you can create, duplicate, and edit a page. Lastly, you can change the order of pages by moving them up and down.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Image Settings&lt;/strong&gt;&lt;br&gt;
Selecting an image lets you fine-tune adjustment settings, and add filters, effects, or blur. Easily crop, straighten, rotate, or flip images.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/img-ly-design-editor/id1672991141&quot;&gt;Try the Design Editor Configuration on iOS&lt;/a&gt; and &lt;a href=&quot;https://play.google.com/store/apps/details?id=ly.img.cesdk.catalog&amp;#x26;pli=1&quot;&gt;Android&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;use-plugins-to-customize-ui-and-add-quick-actions&quot;&gt;Use Plugins to Customize UI and Add Quick Actions&lt;/h2&gt;
&lt;p&gt;We’re excited to introduce the first part of our plugin release for CE.SDK on the web: with this release, you can tailor the editor’s UI elements, reorder components, and add quick actions like &lt;strong&gt;background removal.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;grafik.png&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1886px) 1886px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1886&quot; height=&quot;1289&quot; src=&quot;https://img.ly/_astro/plugins-customize-UI_Z1V1dcA.webp&quot; srcset=&quot;/_astro/plugins-customize-UI_Z1VSlKa.webp 640w, /_astro/plugins-customize-UI_1qIha2.webp 750w, /_astro/plugins-customize-UI_XGkYN.webp 828w, /_astro/plugins-customize-UI_ZBDlJC.webp 1080w, /_astro/plugins-customize-UI_Z1fvfbb.webp 1280w, /_astro/plugins-customize-UI_Z1uBYIU.webp 1668w, /_astro/plugins-customize-UI_Z1V1dcA.webp 1886w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We’ve enabled extension points within key areas of the UI: the Canvas, Navigation bar, Inspector Bar, Dock, and Canvas Menu. Modify the appearance of existing features, register new components, and integrate quick actions. For example, you can add a “Background Removal” button directly in the Canvas Menu or create complex interactions like custom cut-out lines.&lt;/p&gt;
&lt;p&gt;For more details on this first batch release and the upcoming plugins, please read our dedicated &lt;a href=&quot;https://img.ly/blog/plugin-release-part-1-customizing-the-ui-quick-actions/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;article&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;add-voiceovers-to-videos-on-ios&quot;&gt;Add Voiceovers to Videos on iOS&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-33/voice-over.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Add voiceover tracks directly within your mobile app, making your projects more dynamic and engaging. Whether creating a tutorial, narrating a slideshow, or adding commentary to a video, this feature allows you to seamlessly record, edit, and integrate voiceovers.&lt;/p&gt;
&lt;p&gt;The popular feature, as enjoyed on TikTok and Instagram, is a staple for content creation and adds a personal touch to video creations.&lt;/p&gt;
&lt;p&gt;To get started, navigate to “Add Audio”, select “Voiceover” section and start recording. You can preview the recording, re-record it, and add more audio, such as music. Adjust the audio levels for a balanced background track.&lt;/p&gt;
&lt;p&gt;Try recording a voiceover with our Demo App for &lt;a href=&quot;https://apps.apple.com/us/app/img-ly-design-editor/id1672991141&quot;&gt;iOS&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;integrate-a-photo-editor-for-ios-and-android&quot;&gt;Integrate a Photo Editor for iOS and Android&lt;/h2&gt;
&lt;p&gt;Our new &lt;strong&gt;Photo Editor Configuration&lt;/strong&gt; is designed to offer an intuitive, out-of-the-box solution that bundles a range of key photo editing capabilities. Enhance individual photos, add text and shapes, or apply filters—this configuration is a kick-start to provide high-quality photo editing for iOS and Android with CE.SDK.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-33/photo-editor-cesdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&quot;key-features-1&quot;&gt;Key Features&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Crop, Resize&lt;/strong&gt;&lt;br&gt;
Crop, straighten, flip, resize, and rotate your photos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Adjustments&lt;/strong&gt;&lt;br&gt;
Fine-tune your photos by adjusting brightness, contrast, and other key settings, ensuring your images look their best.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Text, Shapes, and Stickers&lt;/strong&gt;&lt;br&gt;
Enhance your photos by adding and customizing text, shapes, and stickers. Whether you’re creating eye-catching titles, intricate shapes, or playful stickers—our configuration gives you the flexibility to adjust fonts, font colors, sizes, and positions to perfectly match your vision.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Effects, Filters, Blur, Blending Modes&lt;/strong&gt;&lt;br&gt;
Transform your images with fun effects, blur, and filters. Apply overlays to add depth and texture to your photos by adjusting blend modes and opacity to achieve the perfect effect.&lt;/p&gt;
&lt;p&gt;Additionally, use our range of photo filters to set the mood, from warm and nostalgic tones to vibrant, modern effects, giving your images a distinctive and professional look.&lt;/p&gt;
&lt;p&gt;Check our &lt;a href=&quot;https://img.ly/docs/cesdk/ios/prebuilt-solutions/photo-editor-42ccb2/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;documentation&lt;/a&gt; to get started with our photo editor configuration, or &lt;a href=&quot;https://apps.apple.com/us/app/img-ly-design-editor/id1672991141&quot;&gt;try the photo editor on iOS&lt;/a&gt; and &lt;a href=&quot;https://play.google.com/store/apps/details?id=ly.img.cesdk.catalog&amp;#x26;pli=1&quot;&gt;Android&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;improvements&quot;&gt;Improvements&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Preview Audio on iOS and Android&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-33/prev-audio.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;We’ve made it easier to select the perfect audio clip for your projects. Listen to audio tracks directly within the interface before adding them to your video. Tap the play button of an audio clip in the asset library to preview it, and adjust your audio clip within the timeline.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resize Pages&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-33/resizable-pages-short.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Resize pages in your web editor by simply grabbing corners and side handles, moving beyond the limitations of input fields or preset format sizes. This intuitive resizing integrates seamlessly with other adjustments, such as image cropping, ensuring a smooth workflow. Enable or disable this behavior as needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Create Rounded Corners&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-33/rounded-corners-short.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;You can now easily create rounded corners for images on the web, iOS and Android. Within your designs, you can add a sleek and modern touch to your projects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Drag and Select Multiple Elements&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-33/drag-select.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;You can now click and drag with your mouse to draw a selection rectangle, instantly selecting all blocks within the covered area. This feature saves you time and effort, working with complex templates.&lt;/p&gt;
&lt;p&gt;Thanks for reading! Don’t hesitate to &lt;a href=&quot;https://img.ly/forms/contact-sales?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;reach out&lt;/a&gt; if you have any questions or need assistance.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Over 3,000 creative professionals gain early access to our new features, demos, and updates—don’t miss out, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Please note that these release notes also include improvements from previous versions.&lt;/em&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/08/cesdk-1-32-133-imgly.png" medium="image"/><category>Release Notes</category><category>App Development</category><category>iOS App Development</category><category>Android App Development</category><category>Design Editor</category></item><item><title>CE.SDK v1.23 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-23-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-23-0-release-notes/</guid><description>Record videos on iOS with a dual camera, and edit on a timeline. Add editors to mobile apps quickly with UI Packages, and improved Video Editor for Web.</description><pubDate>Thu, 28 Mar 2024 09:47:34 GMT</pubDate><content:encoded>&lt;p&gt;Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v-1-21-0-release-notes/&quot;&gt;last release&lt;/a&gt;, we’ve been crafting new features to empower your users’ creative journey. In the past month, we have received valuable insight from you in our exclusive Beta testing. Now we are excited to announce CE.SDK v1.23!&lt;/p&gt;
&lt;h2 id=&quot;record-and-edit-short-form-video-content-on-ios&quot;&gt;Record and Edit Short-Form Video Content on iOS&lt;/h2&gt;
&lt;p&gt;Video Content Creation is here! Your users can record videos, blend them with audio, text, and graphics on a timeline. This includes features like countdown timer, and the popular split screen modes for reactions and duets. You may recognize these from Instagram &lt;strong&gt;Reels&lt;/strong&gt; or &lt;strong&gt;TikTok&lt;/strong&gt;. Making adjustments is straightforward using filters, effects, and adjustment settings.&lt;/p&gt;
&lt;h3 id=&quot;record-with-a-new-camera&quot;&gt;Record with a New Camera&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/Camera-UI-camera-sdk-ios.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Record a video and utilize the &lt;strong&gt;Dual Camera&lt;/strong&gt; feature. This allows you to record simultaneously with your front and back camera, producing a split-screen video. Determine when the recording begins using a countdown &lt;strong&gt;timer&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;edit-on-a-timeline&quot;&gt;Edit on a Timeline&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/timeline-video-editor-sdk-ios.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Users can reorder, arrange and split clips on a timeline, and use filters, visual effects, along with other adjustments to modify the &lt;strong&gt;appearance&lt;/strong&gt; of clips and images. Add &lt;strong&gt;audio&lt;/strong&gt; tracks, image or video &lt;strong&gt;overlays&lt;/strong&gt;, format text, integrate stickers, and use tools for cropping, filtering, blurring, and adjusting images and videos. Try recording and editing Video Content with our &lt;a href=&quot;https://apps.apple.com/de/app/img-ly-design-editor/id1672991141&quot;&gt;Demo App&lt;/a&gt; for iOS.&lt;/p&gt;
&lt;p&gt;Get started with our &lt;a href=&quot;https://img.ly/docs/cesdk/ios/prebuilt-solutions/video-editor-9e533a/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;quickly-add-editors-to-mobile-apps-with-ui-packages&quot;&gt;Quickly Add Editors to Mobile Apps with UI Packages&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/1-12_packages_Z1xR7ci.webp&quot; srcset=&quot;/_astro/1-12_packages_2d48mc.webp 640w, /_astro/1-12_packages_ZyzF3C.webp 750w, /_astro/1-12_packages_Z1jY4Ix.webp 828w, /_astro/1-12_packages_Z1esFNR.webp 1080w, /_astro/1-12_packages_Z1oaov5.webp 1280w, /_astro/1-12_packages_Z1xR7ci.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the past, integrating our editors into your app required developers to manually collect code from our showcases and piece together the UI. This not only demanded effort but also necessitated frequent updates to ensure compatibility with each of our new releases.&lt;/p&gt;
&lt;p&gt;To free your time and resources, we are now introducing ready-to-use &lt;strong&gt;UI packages&lt;/strong&gt; for &lt;strong&gt;iOS&lt;/strong&gt; and &lt;strong&gt;Android&lt;/strong&gt;, that bring advanced editors directly to your app. This eliminates the previous labor-intensive process. These packages include a wide range of options, from a fully equipped Video Editor (iOS) and Camera UI (iOS) to specialized examples like an Apparel Editor or Postcard Editor.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Get started instantly, by installing a UI package. From left to right: Camera UI, Video Editor UI - Timeline View, Video Editor UI, Example UIs for Apparel and Postcard Editors.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/1-23-UI-Packages-SDK_Ba7YI.webp&quot; srcset=&quot;/_astro/1-23-UI-Packages-SDK_14KBdi.webp 640w, /_astro/1-23-UI-Packages-SDK_Z1CUB1w.webp 750w, /_astro/1-23-UI-Packages-SDK_2jitUd.webp 828w, /_astro/1-23-UI-Packages-SDK_nrVDy.webp 1080w, /_astro/1-23-UI-Packages-SDK_Z22M25k.webp 1280w, /_astro/1-23-UI-Packages-SDK_Ba7YI.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;We maintain UI packages for you, ensuring compatibility across devices and development environments. Now, you can immediately start with fully featured editors, staying up-to-date with just a click, and without manual intervention. Get started with our &lt;a href=&quot;https://img.ly/docs/cesdk/ios/get-started/overview-e18f40/&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;integrate-a-user-friendly-editor&quot;&gt;Integrate a User-Friendly Editor&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/design-editor-creative-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;We’ve made enhancements for a more user-friendly editor setup of CE.SDK.&lt;/p&gt;
&lt;p&gt;To enhance &lt;strong&gt;legibility&lt;/strong&gt; and perception of our UI elements, we have reworked our &lt;strong&gt;UI colors&lt;/strong&gt;, making sure they meet the minimal contrast requirements for an improved experience. Additionally, the active state of settings is now more apparent than it was previously.&lt;/p&gt;
&lt;p&gt;Next, libraries and all inspectors open on the same side of our default editor now, instead of on both sides. This reduces mouse travel, saves space, and &lt;strong&gt;streamlines workflows&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, you can now &lt;strong&gt;resize&lt;/strong&gt; images more easily by dragging from any side, not just using the handles. This same feature is also applicable when cropping your image.&lt;/p&gt;
&lt;p&gt;Lastly, elements &lt;strong&gt;stay selected&lt;/strong&gt; after grouping or ungrouping. In the past, changing group settings would deselect your elements.&lt;/p&gt;
&lt;h2 id=&quot;integrate-a-smooth-video-editor-into-your-app&quot;&gt;Integrate A Smooth Video Editor into your App&lt;/h2&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-23/video-editor-web-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;We recently launched our Video Editor for the Web. Since the release, we significantly &lt;strong&gt;improved the playback performance&lt;/strong&gt;, leading to smoother editing and playback.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;timeline&lt;/strong&gt; lets you arrange and edit videos, photos, text, audio, shapes, and stickers. Enhance projects with filters or adjustments, and add a personal touch with customizable audio. Precisely split, trim, and replace elements. Add audio files to make your project stand out. Try editing yourself with our &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web?template=sales/?utm_source=blog&amp;#x26;utm_medium=organic&amp;#x26;utm_campaign=video-for-web-sales&quot;&gt;Showcase for Sales Outreach Videos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Please note that these release notes also include improvements from v1.22.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading. Join over 3000 specialists with powerful apps, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter. We keep you in the loop with brand-new features, early access, and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/03/1-23-video-editing-ios-tiktok-app-sdk.jpg" medium="image"/><category>Release Notes</category><category>CE.SDK</category><category>Video Editing</category><category>iOS App Development</category></item><item><title>CE.SDK v1.21 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v-1-21-0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v-1-21-0-release-notes/</guid><description>Create stunning videos using a brand-new timeline for web.</description><pubDate>Thu, 22 Feb 2024 16:22:28 GMT</pubDate><content:encoded>&lt;p&gt;Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v_1_20_0-release-notes/&quot;&gt;last release&lt;/a&gt;, we’ve been crafting new features to empower your users’ creative journey. Today, we are happy to introduce CE.SDK v1.21!&lt;/p&gt;
&lt;h3 id=&quot;create-videos-with-a-new-timeline-for-web&quot;&gt;Create Videos With A New Timeline for Web&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-21/video-for-web-121-s.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Create eye-catching videos within your web app for any platform, including Instagram, TikTok, or YouTube! Here’s what’s new: the integrated &lt;strong&gt;timeline&lt;/strong&gt; supports the arrangement of elements like videos, photos, text, audio, shapes and stickers. Users can fine-tune their projects with adjustments or filters, and personalize their creations with audio from a customizable library.&lt;/p&gt;
&lt;p&gt;To simplify your content creation, use our formats that automatically &lt;strong&gt;set your canvas size&lt;/strong&gt; &lt;strong&gt;for social media&lt;/strong&gt; and other popular video resolutions.&lt;/p&gt;
&lt;p&gt;Effectively &lt;strong&gt;arrange&lt;/strong&gt; and edit multiple elements within your videos, offering precise control over the editing process. Easily split, trim, and replace. Lastly, make it pop by adding audio files to the composition.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Refine designs in our Showcase by customizing templates: adjust filters, swap visuals or audio, including integration with Soundstripe for premium stock audio.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1964px) 1964px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1964&quot; height=&quot;1468&quot; src=&quot;https://img.ly/_astro/grafik-1_1f8RgA.webp&quot; srcset=&quot;/_astro/grafik-1_Z270Y1B.webp 640w, /_astro/grafik-1_mTAMV.webp 750w, /_astro/grafik-1_1wMhS0.webp 828w, /_astro/grafik-1_Z1e88GP.webp 1080w, /_astro/grafik-1_ZhzA1h.webp 1280w, /_astro/grafik-1_27z0Wj.webp 1668w, /_astro/grafik-1_1f8RgA.webp 1964w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Creating &lt;strong&gt;templates&lt;/strong&gt; with placeholders for your users is just as easy. Simply select an element, click ‘Placeholder’, and decide which elements can be edited.&lt;/p&gt;
&lt;p&gt;To integrate, browse our &lt;a href=&quot;https://img.ly/docs/cesdk/js/prebuilt-solutions/video-editor-9e533a/&quot;&gt;Guide&lt;/a&gt;. Try the &lt;a href=&quot;https://img.ly/showcases/cesdk/video-ui/web&quot;&gt;video editor live demo&lt;/a&gt; yourself. This showcase contains templates, audio and video examples for you to try.&lt;/p&gt;
&lt;h3 id=&quot;keep-designs-in-focus-on-ios&quot;&gt;Keep Designs in Focus on iOS&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-21/iOS-zoom-apparel-designer-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Our new Camera Zoom Limits make editing a design easier. Your page stays in focus to &lt;strong&gt;avoid accidental scrolling&lt;/strong&gt; outside the design. The zoom level is set within a minimum and maximum range, ensuring a practical, usable view at all times. When you select an element in your design, it stays in focus when opening adjustments or other inspectors. It allows you to immediately see changes made to the selected element. This improvement is available for iOS and Android, and can be tested in our Design Editor Demo on the &lt;a href=&quot;https://apps.apple.com/de/app/img-ly-design-editor/id1672991141&quot;&gt;App Store&lt;/a&gt; and &lt;a href=&quot;https://play.google.com/store/apps/details?id=ly.img.cesdk.catalog&quot;&gt;Google Play&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ensure-color-purity-in-print&quot;&gt;Ensure Color Purity in Print&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/1-12_underlayer_Z1gSAz3.webp&quot; srcset=&quot;/_astro/1-12_underlayer_1vaO7T.webp 640w, /_astro/1-12_underlayer_ZiCV0p.webp 750w, /_astro/1-12_underlayer_1QlNIp.webp 828w, /_astro/1-12_underlayer_1J6A8t.webp 1080w, /_astro/1-12_underlayer_Z2iYz7K.webp 1280w, /_astro/1-12_underlayer_Z1gSAz3.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Our newly introduced &lt;strong&gt;Underlayer API&lt;/strong&gt; guarantees the preservation of color purity whenever you export designs using our CE.SDK for print. This solution specifically addresses the challenge of maintaining the richness and vibrancy of colors, irrespective of the color of the canvas, which could range from apparel to paper.&lt;/p&gt;
&lt;p&gt;When you’re ready to export your design, this feature adds a special ‘&lt;strong&gt;underbase&lt;/strong&gt;’ layer, underneath your design. This layer, typically a white or specific primer coat, makes sure the final printed colors match your original vision perfectly.&lt;/p&gt;
&lt;h3 id=&quot;very-soon-integrate-short-form-video-creation&quot;&gt;Very soon: Integrate Short-Form Video Creation&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1920px) 1920px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1920&quot; height=&quot;1080&quot; src=&quot;https://img.ly/_astro/VCC_head-loading_s_ZTQpN2.webp&quot; srcset=&quot;/_astro/VCC_head-loading_s_Z1FDdaR.webp 640w, /_astro/VCC_head-loading_s_17HiQ3.webp 750w, /_astro/VCC_head-loading_s_5DATp.webp 828w, /_astro/VCC_head-loading_s_69D9S.webp 1080w, /_astro/VCC_head-loading_s_Z1ghRHI.webp 1280w, /_astro/VCC_head-loading_s_ZaUdJP.webp 1668w, /_astro/VCC_head-loading_s_ZTQpN2.webp 1920w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Video remains key for attracting, engaging and retaining audiences, especially on mobile. Creating a video editor can be tough, thus we’re thrilled to soon release our turnkey solution for &lt;strong&gt;mobile video content creation&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Combined with our new &lt;a href=&quot;https://img.ly/products/camera-sdk&quot;&gt;Camera SDK&lt;/a&gt;, users can seamlessly blend video, audio, text, and graphics on a sleek timeline. It includes features like voiceover, zoom, tap to record, and the popular split screen modes for reactions and duets. You may recognize these from Instagram or TikTok!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Last chance:&lt;/strong&gt; &lt;strong&gt;Secure your waitlist spot&lt;/strong&gt; to our Video Content Creation Release. You will also receive access to our TestFlight app.&lt;br&gt;
Registration is only open until March 6, 2024.&lt;/p&gt;
&lt;p&gt;? &lt;a href=&quot;https://share.hsforms.com/1mrIXiBbURn6sMqYgZG9c6A1hk3i&quot;&gt;Gain Access&lt;/a&gt; to our Video Content Creation Release&lt;br&gt;
? &lt;a href=&quot;https://2498814.fs1.hubspotusercontent-na1.net/hubfs/2498814/Newsletter-Digest/nov-2023/IMGLY_VIDEO_SDK.pdf?utm_campaign=Video%20Content%20Creation&amp;#x26;utm_medium=releasenotes&amp;#x26;_hsmi=2&amp;#x26;utm_content=2&amp;#x26;utm_source=blog&quot;&gt;Download Feature Exposé&lt;/a&gt; instantly (PDF)&lt;/p&gt;
&lt;p&gt;We can’t wait to hear your feedback!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading. Join over 3000 specialists with powerful apps, and&lt;/strong&gt; &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;&lt;strong&gt;subscribe&lt;/strong&gt;&lt;/a&gt; &lt;strong&gt;to our newsletter. We keep you in the loop with brand-new features and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/02/video-web-editor-sdk-imgly-s.jpg" medium="image"/><category>Release Notes</category><category>Video Editing</category><category>Video Editor</category><category>Web-to-print</category><category>Print</category><category>iOS App Development</category></item><item><title>CE.SDK v1.20 Release Notes</title><link>https://img.ly/blog/creative-editor-sdk-v_1_20_0-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/creative-editor-sdk-v_1_20_0-release-notes/</guid><description>Merge photos, videos or text into fantastic shapes. Or bring image adjustments to your iOS app with CE.SDK v1.20!</description><pubDate>Mon, 29 Jan 2024 10:37:42 GMT</pubDate><content:encoded>&lt;p&gt;Since our &lt;a href=&quot;https://img.ly/blog/creative-editor-sdk-v1-19-release-notes/&quot;&gt;last release&lt;/a&gt;, we’ve been crafting new features to empower your users’ creative journey. Today, we are happy to introduce CE.SDK v1.20!&lt;/p&gt;
&lt;p&gt;With this release, you can:&lt;/p&gt;
&lt;h3 id=&quot;create-unique-element-combinations&quot;&gt;Create Unique Element Combinations&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Platforms:&lt;/strong&gt; iOS, Android, Web, API&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/boolean-1-20.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Forge &lt;strong&gt;limitless combinations&lt;/strong&gt; of photos, videos, texts, and shapes with our new boolean operations feature. This dynamic suite includes operations such as Combine, Subtract, Intersect, and Exclude.&lt;/p&gt;
&lt;p&gt;Subtract text from a shape, or seamlessly merge a distinctive shape with a lively &lt;strong&gt;video clip.&lt;/strong&gt; With these powerful tools, your creativity knows no bounds, offering the freedom to craft truly unique design elements.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/boolean-1-20_2.mp4&quot; controls autoplay muted playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&quot;refine-images-with-ios-appearance-settings&quot;&gt;Refine Images with iOS Appearance Settings&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/720/m_ios-design-editor-sdk.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Breathe life into your content and set the ambiance! We’ve recently updated the user interface on iOS to include Appearance Settings. Now, users can enhance their visual content with advanced improvements. This includes &lt;strong&gt;precise adjustments&lt;/strong&gt;, creative &lt;strong&gt;filters&lt;/strong&gt;, effects, and blur options.&lt;br&gt;
Experience our new features by using the free Demo Design Editor, now live on the &lt;a href=&quot;https://apps.apple.com/us/app/img-ly-design-editor/id1672991141&quot;&gt;App Store&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;optimize-high-quality-asset-imports&quot;&gt;Optimize High-Quality Asset Imports&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;image-source-sets.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/image-source-sets_2pbluf.webp&quot; srcset=&quot;/_astro/image-source-sets_GfGVV.webp 640w, /_astro/image-source-sets_hvpDV.webp 750w, /_astro/image-source-sets_LVopW.webp 828w, /_astro/image-source-sets_2yxwN.webp 1080w, /_astro/image-source-sets_Z1jdBSr.webp 1280w, /_astro/image-source-sets_2pbluf.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Importing high-quality assets, like photos, can strain your device and impact your editor performance. Imagine editing on your phone and just importing a 5k pixels stock photo. Our smart engine steps in, creating an image source set with different resolutions: The intelligent engine selects the &lt;strong&gt;optimal target resolution&lt;/strong&gt; for your asset and seamlessly switches to it.&lt;/p&gt;
&lt;p&gt;The technical scoop? This update &lt;strong&gt;minimizes network bandwidth&lt;/strong&gt;, &lt;strong&gt;loading times&lt;/strong&gt;, and runtime &lt;strong&gt;memory usage&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;keep-designs-in-focus-on-android&quot;&gt;Keep Designs in Focus on Android&lt;/h3&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/videos/1-20/720/m_android-zoom-limits.mp4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;With our new Camera Zoom Limits, editing a design is simpler. Your page remains in focus to prevent accidental scrolling outside the design. The zoom level is set within a minimum and maximum range for a sensible, usable view at all times. Lastly, when you select an element in your design, it will remain in focus when opening adjustments or any other inspectors. This allows you to instantly view changes made to the selected element.&lt;br&gt;
Try our live Demo Design Editor for free, on &lt;a href=&quot;https://play.google.com/store/apps/details?id=ly.img.cesdk.catalog&quot;&gt;Google Play&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;exclusive-access-integrate-short-form-video-creation&quot;&gt;Exclusive Access: Integrate Short-Form Video Creation&lt;/h3&gt;
&lt;p&gt;&lt;img alt=&quot;integrate-video-editor-SDK.jpg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1480px) 1480px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1480&quot; height=&quot;643&quot; src=&quot;https://img.ly/_astro/integrate-video-editor-SDK_Z8qrxx.webp&quot; srcset=&quot;/_astro/integrate-video-editor-SDK_ZfoU81.webp 640w, /_astro/integrate-video-editor-SDK_1ST2CK.webp 750w, /_astro/integrate-video-editor-SDK_fFbPA.webp 828w, /_astro/integrate-video-editor-SDK_Z13XbRO.webp 1080w, /_astro/integrate-video-editor-SDK_1VofbM.webp 1280w, /_astro/integrate-video-editor-SDK_Z8qrxx.webp 1480w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Video continues to be the most effective method for attracting and retaining your audience. However, creating a video editor from scratch can be challenging. We’re excited to announce the upcoming release: your &lt;strong&gt;out-of-the-box&lt;/strong&gt; solution for &lt;strong&gt;video content creation&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://2498814.fs1.hubspotusercontent-na1.net/hubfs/2498814/Newsletter-Digest/nov-2023/IMGLY_VIDEO_SDK.pdf?utm_campaign=Video%20Content%20Creation&amp;#x26;utm_medium=releasenotes&amp;#x26;_hsmi=2&amp;#x26;utm_content=2&amp;#x26;utm_source=blog&quot;&gt;Download Feature Exposé (PDF)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Give your users the ability to arrange video, audio, text, and graphics seamlessly on a sleek video timeline with our new &lt;strong&gt;Camera&lt;/strong&gt;. This includes popular features like &lt;strong&gt;Voiceover&lt;/strong&gt;, &lt;strong&gt;Zoom&lt;/strong&gt;, &lt;strong&gt;Tap to Record&lt;/strong&gt;, and more.&lt;/p&gt;
&lt;p&gt;Additionally, we include the beloved &lt;strong&gt;Split Screen Modes&lt;/strong&gt; for Reactions and Duets, similar to those on &lt;strong&gt;TikTok&lt;/strong&gt; and &lt;strong&gt;Instagram&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://share.hsforms.com/1mrIXiBbURn6sMqYgZG9c6A1hk3i&quot;&gt;Gain exclusive early access&lt;/a&gt; to our Video Content Creation Release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! Join over 3000 app specialists, and &lt;a href=&quot;https://share.hsforms.com/1IgAOV1wASXGPnFG4ZPLejg1hk3i&quot;&gt;subscribe&lt;/a&gt; to our newsletter. We keep you in the loop with brand-new features and updates.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2024/01/creative-editor-sdk-img_ly-v120-2.jpg" medium="image"/><category>Release Notes</category><category>Design Editor</category><category>iOS App Development</category><category>Android</category><category>Photo Editing</category></item><item><title>Time-Based Sprites for VE.SDK on iOS and Android</title><link>https://img.ly/blog/time-based-sprites-for-ve-sdk-on-ios-and-android/</link><guid isPermaLink="true">https://img.ly/blog/time-based-sprites-for-ve-sdk-on-ios-and-android/</guid><description>Time-Based Sprites allow basic keyframing by setting the duration of your Text and Stickers in Videos. </description><pubDate>Mon, 05 Sep 2022 07:06:05 GMT</pubDate><content:encoded>&lt;p&gt;We are happy to extend VideoEditor SDK with a highly-requested feature: &lt;strong&gt;Time-Based Sprites&lt;/strong&gt;. This new feature sets the &lt;strong&gt;duration of text&lt;/strong&gt; and &lt;strong&gt;stickers&lt;/strong&gt; in your video timeline. Users may now place fun stickers and text at the right moment, and give their videos a special touch.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/basic-keyframe-video-editing.MP4&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;time-based-sprites&quot;&gt;Time-Based Sprites&lt;/h2&gt;
&lt;p&gt;The popular feature known from TikTok and Instagram Reels is now available in VE.SDK: set the starting and end point of your text or sticker. Tap your text or sticker and select &lt;em&gt;Duration&lt;/em&gt; to determine the duration of your asset.&lt;/p&gt;
&lt;p&gt;&lt;video src=&quot;https://storage.googleapis.com/imgly-static-assets/static/blog/video-editor-sdk-white-label-edit.MOV&quot; controls autoplay muted loop playsinline&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Unless you have specified a custom set of sticker or text actions, this feature is &lt;strong&gt;enabled by default&lt;/strong&gt; since VE.SDK v10.3.0 for Android and v11.3.0 for iOS. See the official &lt;a href=&quot;https://img.ly/docs/vesdk/ios/guides/trim/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;documentation for iOS&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/vesdk/android/guides/trim/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#trim-sprite-duration&quot;&gt;Android&lt;/a&gt; on Time-Based Sprites.&lt;/p&gt;
&lt;h3 id=&quot;why-is-video-important&quot;&gt;Why is Video Important?&lt;/h3&gt;
&lt;p&gt;Video content has become an essential medium for social media, marketing, sales, and support teams. According to marketing &lt;a href=&quot;https://www.wyzowl.com/video-marketing-statistics/&quot;&gt;statistics&lt;/a&gt;, people are watching an average of &lt;strong&gt;19 hours&lt;/strong&gt; of online video per week in 2022. &lt;strong&gt;88%&lt;/strong&gt; of people say that they felt convinced to buy a product or service by watching a brand’s video. The growing trend and preference of consumers are why businesses are shifting their attention toward including videos in their strategies and applications.&lt;/p&gt;
&lt;p&gt;We commit to extending our video editing features to help developers meet the demand, save resources and streamline the process of building great applications. Let us have a look at our &lt;strong&gt;previous VE.SDK releases&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Video Composition&lt;/strong&gt;&lt;br&gt;
Users may seamlessly edit their footage by trimming and adjusting video files with advanced filters. Finally, they can set the correct order of their video sequences to create a single composition.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audio Support&lt;/strong&gt;&lt;br&gt;
Replace or add sound in videos by loading audio files. Users can trim their audio according to their footage. Developers can provide media libraries for audio and video files. That way, users may access media by choosing from labeled folders, such as genre, theme, or artist names.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Force Trim&lt;/strong&gt;&lt;br&gt;
The VE.SDK trim tool allows users to determine the start and end frame of a video clip and change the duration of their footage. Now you can enforce a &lt;strong&gt;minimum and maximum length&lt;/strong&gt; of videos. Force Trim will come in handy for use cases, such as social media stories and posts popularly limited to bite size 15 or 60 seconds by widely loved apps – see TikTok or Instagram. Adopting a ready-to-use solution like &lt;a href=&quot;https://img.ly/video-sdk/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;VE.SDK&lt;/a&gt; will set you on your path to creating equally beautiful apps.&lt;/p&gt;
&lt;p&gt;Thanks for reading! Let us know what you think on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; – or check out our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt; for more accelerating updates.&lt;/p&gt;</content:encoded><dc:creator>Neslihan</dc:creator><media:content url="https://blog.img.ly/2022/08/video-editor-sdk-duration-trim-stickers-keyframes.png" medium="image"/><category>Release Notes</category><category>VE.SDK</category><category>Android</category><category>Android App Development</category><category>iOS</category><category>iOS App Development</category><category>Video Editing</category><category>Video App</category><category>Keyframe</category><category>Company</category></item><item><title>How to Remove Backgrounds Using Core ML</title><link>https://img.ly/blog/how-to-remove-backgrounds-using-coreml/</link><guid isPermaLink="true">https://img.ly/blog/how-to-remove-backgrounds-using-coreml/</guid><description>Bring background removal and replacement to your iOS application.</description><pubDate>Tue, 28 Jun 2022 15:26:53 GMT</pubDate><content:encoded>&lt;p&gt;In this tutorial, you will learn how to use machine learning to identify an image background and mask it out. This is particularly useful for making stickers or avatars or adding a fake background to a video call. The process of assigning the pixels in an image to a specific object is called “segmentation”. Apple provides an optimized method with pictures of people in its &lt;a href=&quot;https://developer.apple.com/documentation/vision&quot;&gt;Vision framework&lt;/a&gt;. For performing the same tasks with non-human subjects, you can use the &lt;a href=&quot;https://github.com/tensorflow/models/tree/master/research/deeplab&quot;&gt;DeepLabV3 machine learning model with Core ML&lt;/a&gt;. The code examples in this tutorial have been tested using Xcode 13 and Swift 5. Because of the use of Core ML, Vision, and CoreImage in this tutorial, you should run the demo code on a device, not on the Simulator. An iOS project with the demo code is &lt;a href=&quot;https://github.com/waltertyree/super-eureka&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Image segmentation is a different process and requires other machine learning models than image recognition. With recognition, the model produces bounding rectangles that the system believes to contain the entire object. With segmentation, the model identifies the actual pixels of the object.&lt;/p&gt;
&lt;p&gt;In this tutorial, you’ll start with an image of your subject. Then you’ll generate a mask for the background using Vision. Finally, you’ll use CoreImage filters to blend the original image, image mask, and the new image background.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;segmentation-process-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 700px) 700px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;700&quot; height=&quot;666&quot; src=&quot;https://img.ly/_astro/segmentation-process-1_Z2lEfI5.webp&quot; srcset=&quot;/_astro/segmentation-process-1_2i2R9H.webp 640w, /_astro/segmentation-process-1_Z2lEfI5.webp 700w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Whether using the Vision framework alone or supplementing Vision with another Core ML model – the process will be the same:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a Vision request object with some parameters.&lt;/li&gt;
&lt;li&gt;Create a Vision request handler with the image to be processed.&lt;/li&gt;
&lt;li&gt;Process the image with the handler and object.&lt;/li&gt;
&lt;li&gt;Process the results into the final image using CoreImage.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When working with still images, Apple’s CoreImage framework is usually the best option, mainly because of the large number of available filters and the ability to create reusable pipelines that you can run on the CPU or the GPU.&lt;/p&gt;
&lt;h2 id=&quot;using-vision-to-segment-people&quot;&gt;Using Vision to Segment People&lt;/h2&gt;
&lt;p&gt;Without an external model, Vision can only segment documents or people in an image. It is vital to understand that Vision will only identify a pixel in the image as “this pixel is part of a person” or “this pixel is not part of a person.” If an image contains a group of people, Vision will not be able to separate individuals.&lt;/p&gt;
&lt;p&gt;To segment the image, start by creating an instance of a &lt;code&gt;VNGeneratePersonSegmentationRequest&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; segmentationRequest &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VNGeneratePersonSegmentationRequest&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;segmentationRequest.qualityLevel &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .balanced&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This type of request has a few options you can set. &lt;code&gt;.qualityLevel&lt;/code&gt; can be &lt;code&gt;.fast&lt;/code&gt;, &lt;code&gt;.balanced&lt;/code&gt; or &lt;code&gt;.accurate&lt;/code&gt;. The level of &lt;code&gt;.accurate&lt;/code&gt; is the default. This will determine how closely the mask conforms to the boundaries of the original image. The different levels process at different speeds. The &lt;code&gt;.fast&lt;/code&gt; setting is intended for use in a video so that frames don’t get dropped. Using &lt;code&gt;.balanced&lt;/code&gt; or &lt;code&gt;.accurate&lt;/code&gt; causes noticeable delay in an app processing the image on most devices. Experiment with different settings depending on your needs.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;speed-compare&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 700px) 700px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;700&quot; height=&quot;516&quot; src=&quot;https://img.ly/_astro/speed-compare_Z1SlWxp.webp&quot; srcset=&quot;/_astro/speed-compare_ywD9u.webp 640w, /_astro/speed-compare_Z1SlWxp.webp 700w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The output of the segmentation request will be a &lt;code&gt;CVPixelBuffer&lt;/code&gt;. This is a structure that contains information for each pixel of the image. Most of Apple’s video frameworks as well as CoreImage can work with pixel buffers. The default for &lt;code&gt;VNGeneratePersonSegmentationRequest&lt;/code&gt; is a buffer where the color of each pixel is represented by an 8-bit number. Any pixel that Vision thinks contains part of a person will be white and any not-a-person will be black and represented by zero. This will be exactly what you want for generating a mask to work with &lt;code&gt;CIBlendWithMask&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Next, create a &lt;code&gt;VNImageRequestHandler&lt;/code&gt; with the image to be processed. The handler class has a &lt;a href=&quot;https://developer.apple.com/documentation/vision/vnimagerequesthandler&quot;&gt;number of initializers&lt;/a&gt; for different types of data. In this example we will use &lt;code&gt;CGImage&lt;/code&gt;, but you could also start with &lt;code&gt;CIImage&lt;/code&gt;, &lt;code&gt;CVPixelbuffer&lt;/code&gt;, &lt;code&gt;Data&lt;/code&gt;, or others. You can also specify options for the handler. In this tutorial we will not specify any, but one of the options is to pass in a &lt;code&gt;CIContext&lt;/code&gt;. This can help with performance as you can tell your app to do all of the processing with a single context on the GPU using Metal.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; originalCG &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; originalImage&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.cgImage &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;abort&lt;/span&gt;&lt;span&gt;() }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; handler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VNImageRequestHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cgImage&lt;/span&gt;&lt;span&gt;: originalCG)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;try?&lt;/span&gt;&lt;span&gt; handler.&lt;/span&gt;&lt;span&gt;perform&lt;/span&gt;&lt;span&gt;([segmentationRequest])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; maskPixelBuffer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  segmentationRequest.results&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.pixelBuffer &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; maskImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CGImage.&lt;/span&gt;&lt;span&gt;create&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;pixelBuffer&lt;/span&gt;&lt;span&gt;: maskPixelBuffer)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code above, the &lt;code&gt;originalImage&lt;/code&gt; (which happens to be a UIImage) gets converted to a CGImage. Then we use the image to initialize a request handler. The &lt;code&gt;VNImageRequestHandler&lt;/code&gt; has a method &lt;code&gt;.perform&lt;/code&gt; which takes an array of all of the Vision requests you want to use to process the image. The &lt;code&gt;.perform&lt;/code&gt; method will not return until all of the requests in the array have been completed. Depending on how you want to structure your code, you can either provide a completion handler to use with each of the requests or just process the results in-line. In this example, we’ll process in-line.&lt;/p&gt;
&lt;p&gt;If the &lt;code&gt;segmentationRequest&lt;/code&gt; found any people in the image, the &lt;code&gt;results&lt;/code&gt; array will contain a &lt;code&gt;.pixelBuffer&lt;/code&gt;. Create the mask we need by converting the &lt;code&gt;pixelBuffer&lt;/code&gt; into a CGImage. To convert to a CGImage, the example uses some helper methods that &lt;a href=&quot;https://github.com/hollance/CoreMLHelpers&quot;&gt;Matthijs Hollemans published to GitHub&lt;/a&gt; specifically for working with Core ML inputs and outputs.&lt;/p&gt;
&lt;p&gt;Now that we have the mask image, use &lt;code&gt;CIFilter&lt;/code&gt; to compose the final image. It will take a few steps. First, resize the new background, mask, and original image to the same size. Then, blend the three images.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Convert main image to a CIImage and get the size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; mainImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cgImage&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.originalImage&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.cgImage&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; originalSize &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; mainImage.extent.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Convert the maskimage to CIImage and set the size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//to be the same as the original&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; maskCI &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cgImage&lt;/span&gt;&lt;span&gt;: maskImage&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; scaleX &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; originalSize.width &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; maskCI.extent.width&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; scaleY &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; originalSize.height &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; maskCI.extent.height&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;maskCI &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; maskCI.&lt;/span&gt;&lt;span&gt;transformed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;by&lt;/span&gt;&lt;span&gt;: .&lt;/span&gt;&lt;span&gt;init&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scaleX&lt;/span&gt;&lt;span&gt;: scaleX, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: scaleY))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Convert the new background to a CIImage and set the size&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//to be the same as the original&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; backgroundUIImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;named&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;starfield&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;resized&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: originalSize)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; background &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cgImage&lt;/span&gt;&lt;span&gt;: backgroundUIImage.cgImage&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Use CIBlendWithMask to combine the three images&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; filter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;CIBlendWithMask&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(background, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputBackgroundImageKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(mainImage, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputImageKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(maskCI, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputMaskImageKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//Update the UI&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.filteredImageView.&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; UIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ciImage&lt;/span&gt;&lt;span&gt;: filter&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.outputImage&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code resizes the images to match the size of the original image and converts them to &lt;code&gt;CIImage&lt;/code&gt;. Many machine learning models commonly resize inputs and outputs. For example, the output of the &lt;code&gt;VNGeneratePersonSegmentationRequest&lt;/code&gt; using the demo image is 384x512.&lt;/p&gt;
&lt;p&gt;Both CIImage and UIImage formats describe an image but do not always have a bitmap representation. That is why each image gets converted to a CGImage first. Though this guarantees that the demo code will work, converting formats makes the code run slower. In your app, you should experiment with different methods to get your images into CIImage format for filtering. Also, the above code uses Matthijs’ &lt;code&gt;resized&lt;/code&gt; helper method to do some of the resizing.&lt;/p&gt;
&lt;p&gt;With everything resized, the &lt;a href=&quot;https://developer.apple.com/documentation/coreimage/ciblendwithmask&quot;&gt;CIBlendWithMask&lt;/a&gt; filter stitches the images together. In place of the black pixels in the mask, the final image will show the background – for white pixels, the foreground. Wherever the mask image has a gray pixel, the final image will blend background and foreground.&lt;/p&gt;
&lt;p&gt;Now let’s see how to use an additional &lt;code&gt;CoreML&lt;/code&gt; model to process images that don’t have a person as the main subject.&lt;/p&gt;
&lt;h2 id=&quot;choosing-a-model&quot;&gt;Choosing a Model&lt;/h2&gt;
&lt;p&gt;Apple provides a number of pre-built models for text and image processing. The DeepLab v3 Machine Learning model can perform segmentation requests on images that have subjects like dogs. You can download it from &lt;a href=&quot;https://developer.apple.com/machine-learning/models/&quot;&gt;Apple directly&lt;/a&gt; or also find it at the &lt;a href=&quot;https://github.com/tensorflow/models/tree/master/research/deeplab&quot;&gt;DeepLab repo on GitHub&lt;/a&gt;. Once you have downloaded the model, add it to your Xcode project the same as any other file. The DeepLab model will recognize people, the same as the &lt;code&gt;VNPersonSegmentationRequest&lt;/code&gt;, but also other objects. This does come at a cost in an increased filesize for your application. You may notice that Apple provides multiple versions of the model of different file sizes. They all perform the same task, but differ in how they represent the output and a few other things.&lt;/p&gt;
&lt;p&gt;Models on Apple’s website are packaged to work with Core ML and Xcode, so you can easily try a different model. The input and output method names will be the same. It is beyond the scope of this tutorial, yet Apple provides tutorials and example scripts on how to convert TensorFlow and other machine learning models to work with Core ML.&lt;/p&gt;
&lt;h3 id=&quot;core-ml-and-xcode&quot;&gt;Core ML and Xcode&lt;/h3&gt;
&lt;p&gt;When you are working with a Core ML model, Xcode provides some convenient tools. Access them by highlighting the name of the model in the File Navigator pane of Xcode. For instance, clicking “Preview” will let you test the model with your data.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;testing-screenshot&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 700px) 700px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;700&quot; height=&quot;412&quot; src=&quot;https://img.ly/_astro/testing-screenshot_Z2l2s2a.webp&quot; srcset=&quot;/_astro/testing-screenshot_8v5CS.webp 640w, /_astro/testing-screenshot_Z2l2s2a.webp 700w&quot;&gt;&lt;/p&gt;
&lt;p&gt;You can also use the “Predictions” tab to determine how the input needs to be formatted and what to expect for the output.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;predictions-screenshot&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 700px) 700px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;700&quot; height=&quot;355&quot; src=&quot;https://img.ly/_astro/predictions-screenshot_Z1Bf2h7.webp&quot; srcset=&quot;/_astro/predictions-screenshot_Zh7lHX.webp 640w, /_astro/predictions-screenshot_Z1Bf2h7.webp 700w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here you can see that the DeepLabV3 model expects images to be 513 x 513 and will output a multidimensional array of integers that is 513 x 513 in size.&lt;/p&gt;
&lt;p&gt;Finally, on this screen, you can see an entry for the “Model Class” in the headers. Double-click on the name to jump into the Swift wrapper class for the model to see how to call the model in your code and work with the inputs and outputs.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;model-class&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 418px) 418px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;418&quot; height=&quot;85&quot; src=&quot;https://img.ly/_astro/model-class_1pbhcQ.webp&quot; srcset=&quot;/_astro/model-class_1pbhcQ.webp 418w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-deeplabv3-model&quot;&gt;The DeepLabV3 Model&lt;/h3&gt;
&lt;p&gt;The DeepLab model input will be a color image that is 513 x 513 pixels. The Vision framework will handle resizing the input, but you can provide options on how that resize should work. The DeepLabV3 model has been trained to recognize and segment these items:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;aeroplane&lt;/li&gt;
&lt;li&gt;bicycle&lt;/li&gt;
&lt;li&gt;bird&lt;/li&gt;
&lt;li&gt;boat&lt;/li&gt;
&lt;li&gt;bottle&lt;/li&gt;
&lt;li&gt;bus&lt;/li&gt;
&lt;li&gt;car&lt;/li&gt;
&lt;li&gt;cat&lt;/li&gt;
&lt;li&gt;chair&lt;/li&gt;
&lt;li&gt;cow&lt;/li&gt;
&lt;li&gt;dining table&lt;/li&gt;
&lt;li&gt;dog&lt;/li&gt;
&lt;li&gt;horse&lt;/li&gt;
&lt;li&gt;motorbike&lt;/li&gt;
&lt;li&gt;person&lt;/li&gt;
&lt;li&gt;potted plant&lt;/li&gt;
&lt;li&gt;sheep&lt;/li&gt;
&lt;li&gt;sofa&lt;/li&gt;
&lt;li&gt;train&lt;/li&gt;
&lt;li&gt;tv or monitor&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Anything that the model does not recognize, it will consider a &lt;em&gt;background&lt;/em&gt;. After performing the recognition, the model returns a two-dimensional 513 x 513 array. Each entry in the array corresponds to one pixel in the original 513 x 513 input image. For instance, every pixel in the original image that shows a dog will be represented in the output by a &lt;code&gt;12&lt;/code&gt; in the corresponding array entry. Any pixel that is not a recognized object will be given a &lt;code&gt;0&lt;/code&gt; in the output array to represent a &lt;em&gt;background&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;If the model recognizes multiple objects, there will be multiple numbers in the array. If that is not what you want, you need to make changes to the array before creating the mask. For example, using an image of a dog riding a horse, some entries in the array will be &lt;code&gt;12&lt;/code&gt;, others will be &lt;code&gt;13&lt;/code&gt;, and the rest will be &lt;code&gt;0&lt;/code&gt;. To filter out the horse, you need to loop through the array and change any &lt;code&gt;13&lt;/code&gt;s to &lt;code&gt;0&lt;/code&gt;s.&lt;/p&gt;
&lt;h2 id=&quot;segmentation-with-deeplab&quot;&gt;Segmentation with DeepLab&lt;/h2&gt;
&lt;p&gt;To use the DeepLab model in your code, you again use a request to the Vision framework but this time you can specify a model.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; config &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; MLModelConfiguration&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; segmentationModel &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try!&lt;/span&gt;&lt;span&gt; DeepLabV3&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;configuration&lt;/span&gt;&lt;span&gt;: config)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; visionModel &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try?&lt;/span&gt;&lt;span&gt; VNCoreMLModel&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: segmentationModel.model) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  self&lt;/span&gt;&lt;span&gt;.request &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VNCoreMLRequest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;model&lt;/span&gt;&lt;span&gt;: visionModel)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  self&lt;/span&gt;&lt;span&gt;.request&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.imageCropAndScaleOption &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .scaleFill&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code above, we create an instance of the DeepLab Core ML object and then initialize a generic &lt;code&gt;VNCoreMLRequest&lt;/code&gt; with its model. The only option we set on the request is to tell it how to modify the image when it resizes it for input.&lt;/p&gt;
&lt;p&gt;Now the code is very similar to the first example.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; cgImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; originalImage&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.cgImage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; handler &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VNImageRequestHandler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cgImage&lt;/span&gt;&lt;span&gt;: cgImage, &lt;/span&gt;&lt;span&gt;options&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;try?&lt;/span&gt;&lt;span&gt; handler.&lt;/span&gt;&lt;span&gt;perform&lt;/span&gt;&lt;span&gt;([request])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; observations &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; request.results &lt;/span&gt;&lt;span&gt;as?&lt;/span&gt;&lt;span&gt; [VNCoreMLFeatureValueObservation],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; segmentationmap &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; observations.&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.featureValue.multiArrayValue {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; maskUIImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; segmentationmap.&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;min&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;max&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  applyBackgroundMask&lt;/span&gt;&lt;span&gt;(maskUIImage)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because we are using a generic &lt;code&gt;VNCoreMLRequest&lt;/code&gt; we have to cast the results as &lt;code&gt;VNCoreMLFeatureValueObservation&lt;/code&gt;. The DeepLab model returns a &lt;code&gt;.multiArrayValue&lt;/code&gt; instead of a &lt;code&gt;.pixelBuffer&lt;/code&gt; so we will again rely on the helper methods to convert it to an image. Now that the mask image has been created, applying the background is the same as in the original example.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;dog-in-space-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;519&quot; src=&quot;https://img.ly/_astro/dog-in-space-1_pxayi.webp&quot; srcset=&quot;/_astro/dog-in-space-1_pxayi.webp 400w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;This tutorial focused on still images, yet both the standard Vision segmentation requests and DeepLabV3 allow processing video input as they can work with &lt;code&gt;CVPixelBuffer&lt;/code&gt; and &lt;code&gt;VNSequenceRequestHandler&lt;/code&gt;.&lt;br&gt;
The rest of the process will be almost identical to our example. You will need to finetune the performance, or else you will experience frame drops.&lt;/p&gt;
&lt;p&gt;Apple provides another method for identifying the background and primary subject in a still image: Portrait mode. When a device renders Portrait mode, it takes pictures with all cameras on the device and stitches them together. This way, the depth of field can change, and you can adjust what items are in focus or blurred.&lt;/p&gt;
&lt;p&gt;If the DeepLab model does not cover your subject matter, you can train the DeepLabV3 model to recognize other objects using your own data.&lt;/p&gt;
&lt;p&gt;However, you may consider using SDKs such as &lt;a href=&quot;https://img.ly/products/photo-sdk&quot;&gt;PE.SDK&lt;/a&gt; and &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;VE.SDK&lt;/a&gt;. Enabling background removal for your users is easy — all it takes is a few lines in your configuration as described in the &lt;a href=&quot;https://img.ly/docs/pesdk/ios/guides/background-removal/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;official PE.SDK documentation for iOS&lt;/a&gt; and &lt;a href=&quot;https://img.ly/docs/pesdk/android/guides/background-removal/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;Android&lt;/a&gt;, which adds a control to the photo editor to toggle the setting.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;background-removal-photo-editor-app-pe-sdk-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 277px) 277px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;277&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/background-removal-photo-editor-app-pe-sdk-1_21B1QF.webp&quot; srcset=&quot;/_astro/background-removal-photo-editor-app-pe-sdk-1_21B1QF.webp 277w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Integrate a fully customizable photo editor with Background Removal into your app with PE.SDK.&lt;/p&gt;
&lt;p&gt;The latest &lt;a href=&quot;https://img.ly/blog/photo-editor-video-editor-sdk-v_10-11-release-notes/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VE.SDK release&lt;/a&gt; introduced Background Removal for stickers. This feature recognizes people in pictures and removes the background with one tap – no need for manual outlines or masks.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;background-removal-photo-editor-app-pe-sdk-video&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 277px) 277px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;277&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/background-removal-photo-editor-app-pe-sdk-video_1flT3l.webp&quot; srcset=&quot;/_astro/background-removal-photo-editor-app-pe-sdk-video_1flT3l.webp 277w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Let your users create beautiful visuals with Sticker Background Removal.&lt;/p&gt;
&lt;p&gt;This feature is available on Android and iOS 15.0 and higher only. See the official documentation for Sticker Background Removal on &lt;a href=&quot;https://img.ly/docs/vesdk/android/guides/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#background-removal&quot;&gt;Android&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/vesdk/ios/guides/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#background-removal&quot;&gt;iOS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using an SDK will simplify and accelerate your app development, as you can save time and resources, and focus on the growth and innovation of your application instead.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this tutorial, you saw how to use Vision and Core ML to segment an image of a person and remove the background. You also saw how to use DeepLab to work with images of non-persons.&lt;/p&gt;
&lt;p&gt;Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions.&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2022/06/background-remove-removal-editor-app-development.png" medium="image"/><category>How-To</category><category>Background Removal</category><category>Machine Learning</category><category>Photo Editing</category><category>App Development</category><category>iOS App Development</category><category>Core ML</category><category>Tutorial</category></item><item><title>Working with Large Video and Image Files on iOS with Swift</title><link>https://img.ly/blog/working-with-large-video-and-image-files-on-ios-with-swift/</link><guid isPermaLink="true">https://img.ly/blog/working-with-large-video-and-image-files-on-ios-with-swift/</guid><description>Find your best strategy for compressing and resizing images, videos, and other files with Swift.</description><pubDate>Tue, 10 May 2022 13:27:44 GMT</pubDate><content:encoded>&lt;p&gt;Video files and image files are among the largest files. The &lt;code&gt;AVFoundation&lt;/code&gt; and &lt;code&gt;CoreImage&lt;/code&gt; libraries that Apple supplies widely store data on the disk. They try to bring into memory only as much of a file as is needed to complete a task. As you are working on an application that uses files, there are times you will want to compress or resize them. Usually, this is to upload to a server or because you have noticed performance issues, such as stuttering when scrolling or long render times.&lt;/p&gt;
&lt;p&gt;This article will cover strategies for compressing and resizing images, videos, and other files. We will also be looking at using URLSession for file uploads and downloads.&lt;/p&gt;
&lt;p&gt;As iPhone cameras can take higher-quality pictures, the file sizes have grown quite large. For the last few years, Apple has been using a &lt;code&gt;12MP&lt;/code&gt; camera that takes pictures at &lt;code&gt;3024 x 4032&lt;/code&gt; resolution. Apple uses a special &lt;code&gt;HEIC&lt;/code&gt; compression to make each image about &lt;code&gt;1.7MB&lt;/code&gt;. The same image will generate a &lt;code&gt;15MB&lt;/code&gt; file when saved as a &lt;code&gt;.png&lt;/code&gt; and a &lt;code&gt;3MB&lt;/code&gt; file when saved as a &lt;code&gt;.jpeg&lt;/code&gt;. Though this will be a high-quality image, services like Instagram or Facebook will often only display images at less than half of that size. The extra pixel data will be compressed away. Perhaps the first and most important part of working with large image files in your app is planning around what size or quality you need. Social Media services similarly resize video files. For instance, TikTok displays images and video using &lt;code&gt;1080 x 1920&lt;/code&gt;, but any iPhone after X will capture video at a larger size.&lt;/p&gt;
&lt;h2 id=&quot;writing-an-image-to-disk&quot;&gt;Writing an Image to Disk&lt;/h2&gt;
&lt;p&gt;When working with a &lt;code&gt;UIImage&lt;/code&gt; or &lt;code&gt;CoreImage&lt;/code&gt; object, normally you can save it as an NSData object. These files are lossless (the image quality will not degrade), but they are only useful inside of your application. The standards on the Internet are &lt;code&gt;jpeg&lt;/code&gt; and &lt;code&gt;png&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;Fortunately, Apple provides methods to convert &lt;code&gt;UIImage&lt;/code&gt; to &lt;code&gt;.jpegData&lt;/code&gt; or &lt;code&gt;.pngData&lt;/code&gt;. There are also ways to convert &lt;code&gt;CIImage&lt;/code&gt; and &lt;code&gt;CGImage&lt;/code&gt;, but they are slightly more complicated to configure.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; imageToSave &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ciImage&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;someImage&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; \\&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; jpegData &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; imageToSave.&lt;/span&gt;&lt;span&gt;jpegData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;compressionQuality&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1.0&lt;/span&gt;&lt;span&gt;) \\&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; file &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; FileManager.default.&lt;/span&gt;&lt;span&gt;urls&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .documentDirectory, &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;: .userDomainMask).&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt; \\&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;do&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  try&lt;/span&gt;&lt;span&gt; jpegData&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: file&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;appendingPathComponent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;sleeping_dog.jpg&quot;&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; err) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  print&lt;/span&gt;&lt;span&gt;(err.localizedDescription) &lt;/span&gt;&lt;span&gt;//5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what’s going on in the code above:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First we need to convert our image to a &lt;code&gt;UIImage&lt;/code&gt;. The &lt;code&gt;UIImage&lt;/code&gt; class has several different initializers. Use &lt;code&gt;UIImage(named: &quot;some image name&quot;)&lt;/code&gt; to read an image from the app bundle. Use &lt;code&gt;UIImage(contentsOfFile: &quot;some filepath&quot;)&lt;/code&gt; to read from the disk. Use &lt;code&gt;UIImage(cgImage: &quot;some cgImage&quot;)&lt;/code&gt; and &lt;code&gt;UIImage(ciImage: &quot;some ciImage&quot;)&lt;/code&gt; when you have images already in memory. If you are storing images in a &lt;code&gt;CoreData&lt;/code&gt; store or somewhere as &lt;code&gt;Data&lt;/code&gt; you can use &lt;code&gt;UIImage(data: &quot;some data&quot;)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;However you get a &lt;code&gt;UIImage&lt;/code&gt; the next task is to convert it to either a &lt;code&gt;.jpg&lt;/code&gt; or &lt;code&gt;.png&lt;/code&gt; file. Generally, &lt;code&gt;.jpg&lt;/code&gt; is a better option when working with photographs and &lt;code&gt;.png&lt;/code&gt; produces higher quality for line art. The example above creates a &lt;code&gt;.jpg&lt;/code&gt; at the highest quality possible.&lt;/li&gt;
&lt;li&gt;Ask the &lt;code&gt;FileManager&lt;/code&gt; for a &lt;code&gt;URL&lt;/code&gt; to the user’s documents directory. That is generally a safe place to write files. Because of the way the function is formed, it returns an array of &lt;code&gt;URL&lt;/code&gt; items. In this case, there is only one, but you still need to remember to specify the &lt;code&gt;.first&lt;/code&gt; item.&lt;/li&gt;
&lt;li&gt;Writing &lt;code&gt;Data&lt;/code&gt; object to disk can throw errors, so it is good practice to place it in a &lt;code&gt;try&lt;/code&gt;…&lt;code&gt;catch&lt;/code&gt; block of code. The &lt;code&gt;URL&lt;/code&gt; to the user’s documents directory needs to have the actual filename appended.&lt;/li&gt;
&lt;li&gt;Any errors that the system throws can be handled here.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to create a &lt;code&gt;.png&lt;/code&gt; using the &lt;code&gt;imageToSave&lt;/code&gt; above, the line would be&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; pngData &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; imageToSave.&lt;/span&gt;&lt;span&gt;pngData&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.png&lt;/code&gt; filetype does not have any compression settings. The filetype is &lt;a href=&quot;https://en.wikipedia.org/wiki/Lossless_compression&quot;&gt;“lossless”&lt;/a&gt;, so it is always compressed as much as possible without losing any data.&lt;/p&gt;
&lt;h3 id=&quot;compression-options-for-jpeg&quot;&gt;Compression Options for JPEG&lt;/h3&gt;
&lt;p&gt;When generating &lt;code&gt;jpeg&lt;/code&gt; data above, we supplied a quality value. The value is a &lt;code&gt;Float&lt;/code&gt; between &lt;code&gt;0.0&lt;/code&gt; and &lt;code&gt;1.0&lt;/code&gt;. The &lt;code&gt;.jpg&lt;/code&gt; filetype uses a &lt;a href=&quot;https://en.wikipedia.org/wiki/Lossy_compression&quot;&gt;“lossy”&lt;/a&gt; compression algorithm that is specifically designed for photographs.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;compression-quality&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 800px) 800px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;800&quot; height=&quot;197&quot; src=&quot;https://img.ly/_astro/compression-quality_ZnCYfn.webp&quot; srcset=&quot;/_astro/compression-quality_2bNCSI.webp 640w, /_astro/compression-quality_Zi6PuL.webp 750w, /_astro/compression-quality_ZnCYfn.webp 800w&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the image above saving the &lt;code&gt;jpeg&lt;/code&gt; with a quality of &lt;code&gt;1.0&lt;/code&gt; produced the dog on the right and the file is &lt;code&gt;1.3MB&lt;/code&gt;. Saving the jpeg with a quality of &lt;code&gt;0.0&lt;/code&gt; produced the image on the left and the middle image is produced saving with a quality of &lt;code&gt;0.6&lt;/code&gt;. The file on the left is only about 6% of the size of the original.&lt;/p&gt;
&lt;p&gt;Considering what the images are being used for can help you decide on an optimal compression setting for a particular app.&lt;/p&gt;
&lt;h2 id=&quot;compressing-data-with-zlib&quot;&gt;Compressing Data with &lt;code&gt;zlib&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Though &lt;code&gt;png&lt;/code&gt; and &lt;code&gt;jpeg&lt;/code&gt; files are already compressed sometimes you will want to compress other types of files before uploading them (compressing a &lt;code&gt;png&lt;/code&gt;, &lt;code&gt;mov&lt;/code&gt; or &lt;code&gt;jpeg&lt;/code&gt; will often not affect the file size or will make the file &lt;em&gt;larger&lt;/em&gt;). A web server will often be able to accept a &lt;code&gt;POST&lt;/code&gt; that compresses its body data using &lt;code&gt;gzip&lt;/code&gt;. Additionally, some web servers will require that binary data is transformed into a &lt;code&gt;base64EncodedString&lt;/code&gt; before uploading. Base64 encoding will make data size grow by about 33%. Using &lt;code&gt;gzip&lt;/code&gt; will help mitigate that somewhat. Apple’s implementation of &lt;code&gt;gzip&lt;/code&gt; is called &lt;code&gt;.zlib&lt;/code&gt; when compressing data. Swift provides some other compression algorithms such as &lt;code&gt;.lzfse&lt;/code&gt; which will result in smaller files, but they are not widely adopted.&lt;/p&gt;
&lt;p&gt;For example, to encode something as base64 and then compress it with with &lt;code&gt;gzip&lt;/code&gt; so you can upload it, we can use code like below&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; jsonToUpload &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;{&quot;&lt;/span&gt;&lt;span&gt;books&lt;/span&gt;&lt;span&gt;&quot;:[{&quot;&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;The Three Musketeers&lt;/span&gt;&lt;span&gt;&quot;,&quot;&lt;/span&gt;&lt;span&gt;author&lt;/span&gt;&lt;span&gt;&quot;:&quot;&lt;/span&gt;&lt;span&gt;Dumas&lt;/span&gt;&lt;span&gt;&quot;}]}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; jsonAsData &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; jsonToUpload.&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt;: .&lt;/span&gt;&lt;span&gt;utf8&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; encodedString &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; jsonAsData&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;base64EncodedString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; compressedString &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try?&lt;/span&gt;&lt;span&gt; NSData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;: (encodedString&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt;: .&lt;/span&gt;&lt;span&gt;utf8&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;compressed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt;: .zlib) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; Data&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above creates a string of data in &lt;code&gt;JSON&lt;/code&gt; format. It then encodes the &lt;code&gt;String&lt;/code&gt; as a &lt;code&gt;Data&lt;/code&gt; object using &lt;code&gt;.utf8&lt;/code&gt; encoding. Using &lt;code&gt;.utf8&lt;/code&gt; is a standard. If the web server you are uploading to requires a different encoding, you can change it. In the final step, the encoded string is compressed using &lt;code&gt;gzip&lt;/code&gt;. Notice that the &lt;code&gt;.compressed(using:)&lt;/code&gt; method is on &lt;code&gt;NSData&lt;/code&gt; not on &lt;code&gt;Data&lt;/code&gt;. &lt;code&gt;NSData&lt;/code&gt; is an older library, so you convert the compressed &lt;code&gt;NSData&lt;/code&gt; object back to &lt;code&gt;Data&lt;/code&gt; by casting using &lt;code&gt;as Data&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The above example was just to show syntax. The &lt;code&gt;compressedString&lt;/code&gt; will be slightly larger than the &lt;code&gt;encodedString&lt;/code&gt;. Based on the kind of data your app works with and the capabilities of the web server, you will need to experiment with how to encode and compress to get the best results.&lt;/p&gt;
&lt;h2 id=&quot;transferring-data-with-a-server&quot;&gt;Transferring Data With a Server&lt;/h2&gt;
&lt;p&gt;Depending on the original format of the data, your web server may want the &lt;code&gt;.httpBody&lt;/code&gt; compressed. You can do that with some code like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; someJSONData &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Data&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; request &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URLRequest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;https://example.com/uploads/&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;request.httpMethod &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;POST&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//set the other header variables here using&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//request.setValue(&quot;value to set&quot;, forHTTPHeaderField: &quot;header field name&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; compressedJSONData &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; try?&lt;/span&gt;&lt;span&gt; NSData&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data&lt;/span&gt;&lt;span&gt;: someJSONData).&lt;/span&gt;&lt;span&gt;compressed&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;using&lt;/span&gt;&lt;span&gt;: .zlib) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; Data { &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  request.httpBody &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; compressedJSONData&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; task &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URLSession.shared.&lt;/span&gt;&lt;span&gt;dataTask&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;: request) {data, response, error &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //check for errors and response data when the task is done&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //response will contain the status and other header messages&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //data will contain any payload the server returns&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  task.&lt;/span&gt;&lt;span&gt;resume&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what the code above is doing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Get the payload into a &lt;code&gt;Data&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;URLRequest&lt;/code&gt; as a &lt;code&gt;var&lt;/code&gt; so that you can configure it. If you create it as a &lt;code&gt;let&lt;/code&gt; it will be a &lt;code&gt;GET&lt;/code&gt; request with default header fields.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;gzip&lt;/code&gt; to compress the data and if successful, assign it to the &lt;code&gt;.httpBody&lt;/code&gt; of the &lt;code&gt;request&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a data task for the upload. Upon completion, the &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;response&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; variables will have any response from the server.&lt;/li&gt;
&lt;li&gt;Actually start the task.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;uploading-a-large-file&quot;&gt;Uploading a Large File&lt;/h3&gt;
&lt;p&gt;An issue with the code above is that all of the initial data will be in memory. So for a large image or video upload, this may not work. However, Swift does provide a different method that will upload a file directly from the disk, reading into memory only what is needed at the time.&lt;/p&gt;
&lt;p&gt;Code to implement that would follow this form.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; request &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URLRequest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;https://example.com/uploads&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;request.httpMethod &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &quot;POST&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//set the other header variables here using&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//request.setValue(&quot;value to set&quot;, forHTTPHeaderField: &quot;header field name&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; documentsDirectory &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; FileManager.default.&lt;/span&gt;&lt;span&gt;urls&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .documentDirectory, &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;: .userDomainMask).&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; fileToUpload &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; documentsDirectory&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;appendingPathComponent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;the-big-giant-file.mp4&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; uploadTask &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URLSession.shared.&lt;/span&gt;&lt;span&gt;uploadTask&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;: request, &lt;/span&gt;&lt;span&gt;fromFile&lt;/span&gt;&lt;span&gt;: fileToUpload) {data, response, error &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//get some information from the server when the file has been uploaded&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;uploadTask.&lt;/span&gt;&lt;span&gt;resume&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks very similar to the previous example but notice that you do not set an &lt;code&gt;.httpBody&lt;/code&gt;. There will be header values to set though. Most web servers will want to know how large the file is, authentication credentials, and whether the data is multi-part, or raw or other metadata. These will all be set using &lt;code&gt;.setValue(forHTTPHeaderField:)&lt;/code&gt; in the &lt;code&gt;request&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;For larger files, it is often better to use a &lt;code&gt;URLSessionDataDelegate&lt;/code&gt; instead of the single closure with &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;response&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;. When using the delegate, you can write code to monitor the progress of the file transfer as well as respond to authentication challenges and make decisions about caching. You can also implement &lt;code&gt;URLSessionDelegate&lt;/code&gt; and &lt;code&gt;URLSessionTaskDelegate&lt;/code&gt; methods. All of the delegate methods are options, so you only need to implement ones that are important to your application.&lt;/p&gt;
&lt;h3 id=&quot;configuring-background-transfers&quot;&gt;Configuring Background Transfers&lt;/h3&gt;
&lt;p&gt;Another benefit of using the &lt;code&gt;uploadTask&lt;/code&gt; (and its related &lt;code&gt;downloadTask&lt;/code&gt;) instead of the &lt;code&gt;dataTask&lt;/code&gt; is that you can configure the transfer to continue for a time even if the app goes to the background. In that case, instead of using the &lt;code&gt;URLSession.shared&lt;/code&gt; object, you will want to configure a session that has some special properties to allow it to work in the background.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; configuration &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URLSessionConfiguration.&lt;/span&gt;&lt;span&gt;background&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;withIdentifier&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;some.unique.identifier&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; session &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URLSession&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;configuration&lt;/span&gt;&lt;span&gt;: configuration, &lt;/span&gt;&lt;span&gt;delegate&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;delegateQueue&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; uploadTask &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; session.&lt;/span&gt;&lt;span&gt;uploadTask&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;: request, &lt;/span&gt;&lt;span&gt;fromFile&lt;/span&gt;&lt;span&gt;: fileToUpload)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Completely configuring and supporting the downloads and backgrounding is covered in &lt;a href=&quot;https://developer.apple.com/documentation/foundation/url_loading_system/downloading_files_in_the_background&quot;&gt;this article by Apple&lt;/a&gt;. It will require implementing some delegate methods for the session as well as updating some of the methods in the main application delegate. The most important is the &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622941-application&quot;&gt;&lt;code&gt;handleEventsForBackgroundURLSession&lt;/code&gt;&lt;/a&gt; which is how the system will wake up your app with information about the upload or download.&lt;/p&gt;
&lt;p&gt;Additionally, download tasks can be paused and restarted. The &lt;code&gt;cancel(byProducingResumeData:)&lt;/code&gt; method on &lt;code&gt;URLSessionDownloadTask&lt;/code&gt; will cancel a download and optionally provide some resume data you can use to resume the download at a later time. You can call this method on the task when the user taps a button or when the app goes to the background or similar. The web server must support byte-range requests and &lt;code&gt;ETag&lt;/code&gt; or &lt;code&gt;Last-Modified&lt;/code&gt; headers. The &lt;a href=&quot;https://developer.apple.com/documentation/foundation/urlsessiondownloadtask/1411634-cancel&quot;&gt;documentation for pausing and restarting&lt;/a&gt; explains how to pause the download and how to resume using &lt;code&gt;downloadTaks(withResumeData:)&lt;/code&gt;. There is not a similar structure for pausing and resuming an upload.&lt;/p&gt;
&lt;h2 id=&quot;resizing-an-image&quot;&gt;Resizing an image&lt;/h2&gt;
&lt;p&gt;Perhaps the simplest way to make a file smaller is to reduce its dimensions. Using &lt;code&gt;CoreImage&lt;/code&gt; you can use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Lanczos_resampling&quot;&gt;Lanczos resampling&lt;/a&gt; algorithm to resize an image while keeping high quality.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; url &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;sleeping-dog&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jpeg&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; image &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;contentsOf&lt;/span&gt;&lt;span&gt;: url&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; filter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;CILanczosScaleTransform&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(image, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputImageKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputScaleKey) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; result &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputImage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; converter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ciImage&lt;/span&gt;&lt;span&gt;: result&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what this code is doing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;CIImage&lt;/code&gt; from a &lt;code&gt;URL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;CILanczosScaleTransform&lt;/code&gt; filter.&lt;/li&gt;
&lt;li&gt;Configure the filter to reduce the image dimensions by 50%.&lt;/li&gt;
&lt;li&gt;Convert the scaled image to a &lt;code&gt;UIImage&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recently, Apple has provided a &lt;code&gt;UIGraphicsImageRenderer&lt;/code&gt; that will work directly on &lt;code&gt;UIImage&lt;/code&gt; objects. The code below will resize a &lt;code&gt;UIImage&lt;/code&gt; to 50%. Notice that the &lt;code&gt;UIGraphicsImageRenderer&lt;/code&gt; can also use an explicit value for size (unlike the &lt;code&gt;CILanczosScaleTransform&lt;/code&gt;)&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; imageToSave &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;named&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;sleeping-dog.jpeg&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; //1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; newSize &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; imageToSave.&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;applying&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;CGAffineTransform&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;scaleX&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0.5&lt;/span&gt;&lt;span&gt;)) &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; renderer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIGraphicsImageRenderer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;: newSize) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; scaledImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; renderer.&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt; { (context) &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; rect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CGRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt;: CGPoint.zero, &lt;/span&gt;&lt;span&gt;size&lt;/span&gt;&lt;span&gt;: newSize)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  imageToSave.&lt;/span&gt;&lt;span&gt;draw&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;: rect) &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what this code is doing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Load the image as a &lt;code&gt;UIImage&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Scale the size down by half.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;UIGraphicsImageRenderer&lt;/code&gt; that will render in this new size.&lt;/li&gt;
&lt;li&gt;Actually draw the image into the renderer at the new size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition to rendering the image back as a &lt;code&gt;UIImage&lt;/code&gt; the renderer can create &lt;code&gt;jpegData&lt;/code&gt; and &lt;code&gt;pngData&lt;/code&gt; which would save a few steps if you are resizing and converting. The &lt;a href=&quot;https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer&quot;&gt;full documentation for &lt;code&gt;UIGraphicsImageRenderer&lt;/code&gt;&lt;/a&gt; explains the details.&lt;/p&gt;
&lt;h2 id=&quot;resizing-a-video&quot;&gt;Resizing a Video&lt;/h2&gt;
&lt;p&gt;Though the &lt;code&gt;UIGraphicsImageRenderer&lt;/code&gt; is faster, using the older &lt;code&gt;CoreImage&lt;/code&gt; resizing has a benefit. &lt;code&gt;CoreImage&lt;/code&gt; filters can be applied to video files as a composition. Large video files can be scaled down. The code below will create a composition that will scale a video asset by 50%. Remember that you will need to &lt;code&gt;import AVFoundation&lt;/code&gt; to use these tools.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; newAsset &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVAsset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jumping-man&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var&lt;/span&gt;&lt;span&gt; newSize &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;some size that you&apos;ve calculated&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; //2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; resizeComposition &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVMutableVideoComposition&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;: newAsset, &lt;/span&gt;&lt;span&gt;applyingCIFiltersWithHandler&lt;/span&gt;&lt;span&gt;: { request &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; filter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;CILanczosScaleTransform&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(request.sourceImage, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputImageKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&amp;#x3C;&lt;/span&gt;&lt;span&gt;some scale factor&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputScaleKey) &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; resultImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; filter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputImage&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  request.&lt;/span&gt;&lt;span&gt;finish&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;: resultImage, &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;resizeComposition.renderSize &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; newSize &lt;/span&gt;&lt;span&gt;//5&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s what the code above is doing to create a new composition:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create an &lt;code&gt;AVAsset&lt;/code&gt; from a &lt;code&gt;URL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a variable &lt;code&gt;newSize&lt;/code&gt; to hold the final size.&lt;/li&gt;
&lt;li&gt;In the composition, configure the &lt;code&gt;CIFilter&lt;/code&gt; that will be applied to each frame.&lt;/li&gt;
&lt;li&gt;Calculate the scale factor based on the &lt;code&gt;newSize&lt;/code&gt; variable and the actual size of the &lt;code&gt;request.sourceImage.extent.size&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set the &lt;code&gt;renderSize&lt;/code&gt; property of the composition to the new size.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don’t set the &lt;code&gt;renderSize&lt;/code&gt; then there will be a black letterbox around the video.&lt;/p&gt;
&lt;p&gt;With the resizing composition, you can now export the &lt;code&gt;AVAsset&lt;/code&gt; as a &lt;code&gt;.mov&lt;/code&gt; or &lt;code&gt;.m4v&lt;/code&gt; file using an &lt;code&gt;AVExportSession&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; asset &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVAsset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jumping-man&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; outputMovieURL &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; FileManager.default.&lt;/span&gt;&lt;span&gt;urls&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt;: .documentDirectory, &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt;: .userDomainMask).&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;appendingPathComponent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;exported.mov&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//create exporter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; exporter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVAssetExportSession&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;: asset, &lt;/span&gt;&lt;span&gt;presetName&lt;/span&gt;&lt;span&gt;: AVAssetExportPresetHighestQuality)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//configure exporter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.videoComposition &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &amp;#x3C;&lt;/span&gt;&lt;span&gt;the composition you created above&lt;/span&gt;&lt;span&gt;&gt;&lt;/span&gt;&lt;span&gt; //3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputURL &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; outputMovieURL&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputFileType &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .mov&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//export!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exportAsynchronously&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;completionHandler&lt;/span&gt;&lt;span&gt;: { [&lt;/span&gt;&lt;span&gt;weak&lt;/span&gt;&lt;span&gt; exporter] &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;span&gt; //4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  DispatchQueue.main.&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; error &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;failed &lt;/span&gt;&lt;span&gt;\(error.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;file saved at &lt;/span&gt;&lt;span&gt;\(outputMovieURL)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s what the code above is doing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Load an &lt;code&gt;AVAsset&lt;/code&gt; from a &lt;code&gt;URL&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;URL&lt;/code&gt; to save the resized movie.&lt;/li&gt;
&lt;li&gt;Apply the resizing composition to an &lt;code&gt;AVAssetExportSession&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Asynchronously export the asset to disk.&lt;/li&gt;
&lt;li&gt;Print the destination &lt;code&gt;URL&lt;/code&gt; of the new movie when it is saved.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another method to resize a video is to use one of the &lt;code&gt;AVAssetExportSession&lt;/code&gt; presets. Apple offers several &lt;a href=&quot;https://developer.apple.com/documentation/avfoundation/avassetexportsession/export_presets&quot;&gt;size and quality presets&lt;/a&gt; for export sessions. In the line above that creates the &lt;code&gt;exporter&lt;/code&gt;, replace &lt;code&gt;AVAssetExportPresetHighestQuality&lt;/code&gt; with one of the other values. There are generic quality presets: &lt;code&gt;AVAssetExportPresetLowQuality&lt;/code&gt;, &lt;code&gt;AVAssetExportPresetMediumQuality&lt;/code&gt; and there are size presets including: &lt;code&gt;AVAssetExportPreset1280x720&lt;/code&gt; and &lt;code&gt;AVAssetExportPreset640x480&lt;/code&gt;. When using one of the presets you do not need to use a composition unless you want to do further manipulation or unless you need a size or quality combination that is not provided by any preset. As with images, experiment with different settings until you get a balance between quality and size that works for you.&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;Some other strategies for working with large files are to chunk data file or split up video files. However, to use this strategy, you will need to coordinate with someone to stitch them back together after they are uploaded or downloaded.&lt;/p&gt;
&lt;p&gt;Using the &lt;code&gt;AVAssetExportSession&lt;/code&gt; above, you could call it multiple times and pass in a time range perhaps using code like this&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; timeRange &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CMTimeRangeFromTimeToTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;: startTime, &lt;/span&gt;&lt;span&gt;end&lt;/span&gt;&lt;span&gt;: endTime)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//some code to create the exporter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.timeRange &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; timeRange&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depending on the video and audio of the &lt;code&gt;AVAsset&lt;/code&gt; the splits in the video may be noticable. Because of modern frame rates, the audio would probably be more likely to be noticed. Time ranges can be sub-second, so you would need to experiment. However, iOS would manage the file for you so that the entire &lt;code&gt;AVAsset&lt;/code&gt; is never loaded into memory.&lt;/p&gt;
&lt;p&gt;It is also possible to split &lt;code&gt;Data&lt;/code&gt; objects using &lt;code&gt;.subData(in:)&lt;/code&gt; and looping through the bytes of the object. Again, you would need to also write code to stitch them back together later. Additionally, you would likely need to bring the entire &lt;code&gt;Data&lt;/code&gt; object into a memory buffer, which might not be desirable.&lt;/p&gt;
&lt;p&gt;Another way to handle large video files is to use Apple’s &lt;a href=&quot;https://developer.apple.com/streaming/&quot;&gt;HTTP Live Streaming&lt;/a&gt; tools. These create files that you can upload to any web server that is recognized by iOS and Mac devices. The tools will segment the video as well as create multiple versions so that your users with different bandwidth capabilities can see different quality streams. Most Android devices and web browsers will at least be able to see the video, even if they cannot dynamically switch between streams.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this tutorial, you saw some different strategies for shrinking and compressing the large image and video files that an iPhone camera can produce. You also saw some strategies for transferring larger data payloads with a web server. As mentioned in the beginning, consider what sizes the images and videos will display and use that to determine what sizes of images to store. Also, consider storing multiple versions of images or using thumbnails when displaying a gallery or list of images.&lt;/p&gt;
&lt;p&gt;Looking to integrate video capabilities into your app? Check out our &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;Video Editor SDK&lt;/a&gt;, &lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation&quot;&gt;Short Video Creation&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/camera-sdk&quot;&gt;Camera SDK&lt;/a&gt;!&lt;/p&gt;
&lt;h3 id=&quot;thanks-for-reading-let-us-know-what-you-think-on-twitter-or-stay-in-the-loop-with-our-newsletter&quot;&gt;Thanks for reading! Let us know what you think on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt;, or stay in the loop with our &lt;a href=&quot;https://img.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/h3&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2022/05/large-file-swift-crop-optimize.png" medium="image"/><category>How-To</category><category>Swift</category><category>iOS App Development</category><category>Tutorial</category></item><item><title>PE.SDK and VE.SDK 10 for Android and 11 for iOS Release</title><link>https://img.ly/blog/photo-editor-video-editor-sdk-v_10-11-release-notes/</link><guid isPermaLink="true">https://img.ly/blog/photo-editor-video-editor-sdk-v_10-11-release-notes/</guid><description>This major update brings Background Removal and more to PhotoEditor SDK and VideoEditor SDK.</description><pubDate>Wed, 04 May 2022 12:46:21 GMT</pubDate><content:encoded>&lt;p&gt;We are thrilled to announce the release of PhotoEditor SDK and VideoEditor SDK 10 for Android and 11 for iOS. This new major version is packed with features and improvements.&lt;/p&gt;
&lt;p&gt;This release is adding:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Custom Watermarks&lt;/li&gt;
&lt;li&gt;Background Removal for Photos&lt;/li&gt;
&lt;li&gt;Background Removal for Stickers&lt;/li&gt;
&lt;li&gt;Custom Sticker Libraries&lt;/li&gt;
&lt;li&gt;Giphy Integration&lt;/li&gt;
&lt;li&gt;And More Updates for Android and iOS&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;custom-watermarks&quot;&gt;Custom Watermarks&lt;/h2&gt;
&lt;p&gt;With our new support for custom watermarks, you can add your company logo or any other image to photo and video exports. You can place your watermark in either corner or the center of your photo and video. Additionally, you can specify the size of the watermark and determine its distance from the outline if you choose to place it near a corner. Your users will not be able to remove nor modify the watermark.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Set your Custom Watermark for all exports.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 277px) 277px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;277&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/watermark-video-editor-custom-sdk_Z12Y3l4.webp&quot; srcset=&quot;/_astro/watermark-video-editor-custom-sdk_Z12Y3l4.webp 277w&quot;&gt;&lt;/p&gt;
&lt;p&gt;For more details on Custom Watermarks, take a look at our documentation for &lt;a href=&quot;https://img.ly/docs/pesdk/android/customization/watermark/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;Android&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/pesdk/ios/customization/watermark/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;iOS&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;background-removal-for-photos&quot;&gt;Background Removal for Photos&lt;/h2&gt;
&lt;p&gt;You have unwaveringly requested this feature: we are excited to offer the automatic removal of backgrounds for static content! Background Removal is fully running &lt;strong&gt;on-device&lt;/strong&gt; and currently specializes in images containing a person only. PE.SDK enables this feature exclusively when it detects a person in the photo.&lt;/p&gt;
&lt;p&gt;The removed background will turn transparent if your selected output supports transparency, such as PNG. In other cases, such as JPG, your background removal will be tinted black.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Integrate Background Removal into your application.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 277px) 277px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;277&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/background-removal-photo-editor-app-pe-sdk-1_2jocg8.webp&quot; srcset=&quot;/_astro/background-removal-photo-editor-app-pe-sdk-1_2jocg8.webp 277w&quot;&gt;&lt;/p&gt;
&lt;p&gt;This feature is available on Android and iOS 15.0 and higher only. Easily enable Background Removal for Photos with our &lt;a href=&quot;https://img.ly/docs/pesdk/android/features/background_removal/&quot;&gt;Android guide&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/pesdk/ios/features/background_removal/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;iOS guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;background-removal-for-stickers&quot;&gt;Background Removal for Stickers&lt;/h2&gt;
&lt;p&gt;A more common use case for automatic background removal is with stickers, so we’ve also added this functionality. Select a sticker including a person and tap the &lt;em&gt;Remove BG&lt;/em&gt; button. Simple!&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Let your users create beautiful visuals with Sticker Background Removal.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 277px) 277px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;277&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/background-removal-photo-editor-app-pe-sdk-video_2nmAzB.webp&quot; srcset=&quot;/_astro/background-removal-photo-editor-app-pe-sdk-video_2nmAzB.webp 277w&quot;&gt;&lt;/p&gt;
&lt;p&gt;This feature is available on Android and iOS 15.0 and higher only. Easily enable Background Removal for Stickers with our &lt;a href=&quot;https://img.ly/docs/vesdk/android/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#background-removal&quot;&gt;Android documentation&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/vesdk/ios/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#background-removal&quot;&gt;iOS documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;custom-sticker-libraries&quot;&gt;Custom Sticker Libraries&lt;/h2&gt;
&lt;p&gt;We wanted to make it easier for you to provide your users with more sticker tool content. So far, you were able to create multiple sticker categories with different stickers that could either be part of your app or stored on a server. Additionally, you could &lt;a href=&quot;https://img.ly/docs/pesdk/ios/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#personal-stickers&quot;&gt;enable our personal stickers feature on iOS&lt;/a&gt; or &lt;a href=&quot;https://img.ly/docs/pesdk/ios/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#personal-stickers&quot;&gt;Android&lt;/a&gt;), giving your users the option to select stickers from their camera roll.&lt;/p&gt;
&lt;p&gt;We have now added more ways to provide your users with stickers.&lt;/p&gt;
&lt;h3 id=&quot;android&quot;&gt;Android&lt;/h3&gt;
&lt;p&gt;You can now create sticker categories that do not present our sticker selection interface, but will instead load any arbitrary Fragment. That gives you complete control over the sticker selection screen, and you can use it to load and display any content. When the user selects any of your stickers, all you have to do is pass it back to the SDK with a callback. Get started with your &lt;a href=&quot;https://img.ly/docs/vesdk/android/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#adding-custom-stickers&quot;&gt;Custom Sticker Library with our Android documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;ios&quot;&gt;iOS&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;You can now create sticker categories that do not present our sticker selection interface, but will instead load any arbitrary view controller that conforms to the &lt;code&gt;StickerCollection&lt;/code&gt; protocol. That gives you complete control over the sticker selection screen, and you can use it to load and display any content. When the user selects any of your stickers, simply pass it back to the SDK using a delegate method.&lt;/li&gt;
&lt;li&gt;Additionally, we’ve created a new &lt;code&gt;StickerProviderCategory&lt;/code&gt; that you can pass any object conforming to the &lt;code&gt;StickerProvider&lt;/code&gt; protocol. This category provides most of the features required to interact with sticker content providers, such as search and pagination. You can use this category to very quickly integrate any sticker service.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Find more information on &lt;a href=&quot;https://img.ly/docs/vesdk/ios/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#custom-sticker-view-controller&quot;&gt;Custom Sticker Libraries in the iOS documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;giphy-integration&quot;&gt;GIPHY Integration&lt;/h2&gt;
&lt;p&gt;This online database and search engine for funny GIFs and beautiful animations is a staple in many social applications. Easily integrate GIPHY into your app:&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 277px) 277px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;277&quot; height=&quot;600&quot; src=&quot;https://img.ly/_astro/integrate-giphy-video-editor-photo-sdk_ZNfYn8.webp&quot; srcset=&quot;/_astro/integrate-giphy-video-editor-photo-sdk_ZNfYn8.webp 277w&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;android-1&quot;&gt;Android&lt;/h3&gt;
&lt;p&gt;Using our new Custom Sticker Library, we’ve also created an integration for GIPHY on Android. All you have to do is head over to &lt;a href=&quot;https://developers.giphy.com&quot;&gt;GIPHY&lt;/a&gt;, create your API key, pass it to your &lt;code&gt;GiphySettings&lt;/code&gt; and create a &lt;code&gt;GiphyStickerCategoryItem&lt;/code&gt;. Your users will then immediately have access to all the content GIPHY has to offer.&lt;/p&gt;
&lt;p&gt;For more details, visit our &lt;a href=&quot;https://img.ly/docs/vesdk/android/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#adding-giphy&quot;&gt;documentation on adding GIPHY&lt;/a&gt; for Android.&lt;/p&gt;
&lt;h3 id=&quot;ios-1&quot;&gt;iOS&lt;/h3&gt;
&lt;p&gt;Using the new &lt;code&gt;StickerProviderCategory&lt;/code&gt;, we added GIPHY support on iOS. All you have to do is head over to &lt;a href=&quot;https://developers.giphy.com&quot;&gt;GIPHY&lt;/a&gt;, create your API key, and pass this to the GIPHY sticker category. Your users will then immediately have access to all the content GIPHY has to offer.&lt;/p&gt;
&lt;p&gt;For more details, visit our documentation &lt;a href=&quot;https://img.ly/docs/vesdk/ios/features/stickers/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#giphy&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;more-updates-for-android&quot;&gt;More Updates for Android&lt;/h2&gt;
&lt;p&gt;Additionally, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Increased the minimum API level for PhotoEditor SDK from 16 (Android 4) to 21 (Android 5), which ensures support for 98% of all phones in use.&lt;/li&gt;
&lt;li&gt;Added support for headless video exporting.&lt;/li&gt;
&lt;li&gt;Increased video playback stability.&lt;/li&gt;
&lt;li&gt;Fixed a number of issues with degraded audio quality in videos.&lt;/li&gt;
&lt;li&gt;And added the ability to choose a theme at runtime.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;more-updates-for-ios&quot;&gt;More Updates for iOS&lt;/h2&gt;
&lt;p&gt;Additionally, we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Increased our deployment target from iOS 9 to iOS 13.&lt;/li&gt;
&lt;li&gt;Migrated all usage of OpenGL to Metal.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/vesdk/ios/introduction/migration/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes&quot;&gt;Added a better result API&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://img.ly/docs/vesdk/ios/introduction/getting_started/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=releasenotes#swiftui&quot;&gt;Added support for SwiftUI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enhanced our video export to display a progress indicator and the option to cancel the export.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;thanks-for-reading-let-us-know-what-you-think-on-twitter-or-stay-in-the-loop-with-our-newsletter&quot;&gt;Thanks for reading! Let us know what you think on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt;, or stay in the loop with our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/h3&gt;</content:encoded><dc:creator>Sascha</dc:creator><media:content url="https://blog.img.ly/2022/05/photo-editor-sdk-video-editor-sdk-background-removal.png" medium="image"/><category>Release Notes</category><category>Photo Editor</category><category>Video Editor</category><category>Android App Development</category><category>iOS App Development</category><category>App Development</category></item><item><title>How to Merge Videos in iOS with Swift</title><link>https://img.ly/blog/combine-video-clips-into-a-new-file-in-ios-with-swift/</link><guid isPermaLink="true">https://img.ly/blog/combine-video-clips-into-a-new-file-in-ios-with-swift/</guid><description>Learn how to combine videos in iOS with Swift and jump into advanced creations.</description><pubDate>Wed, 12 Jan 2022 12:12:55 GMT</pubDate><content:encoded>&lt;p&gt;Combining video clips or parts of video clips into a single video may appear complicated, but the things that add complexity also provide flexibility. In this tutorial, you will see how to combine a few short clips into a single video. You will also gain an understanding of building on this basic pattern to make more advanced creations. This tutorial was created and tested with Xcode 13 and Swift 5.&lt;/p&gt;
&lt;p&gt;As with most &lt;code&gt;AVFoundation&lt;/code&gt; code, the Xcode simulator is not always the best platform for running the code. Test on an actual device if you can. A demo project with code that supports this tutorial is available on &lt;a href=&quot;https://github.com/waltertyree/animated-robot&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;making-a-composition&quot;&gt;Making a Composition&lt;/h2&gt;
&lt;p&gt;Whenever you want to edit media or add effects, the first place to look is &lt;code&gt;AVComposition&lt;/code&gt;. This class in &lt;code&gt;AVFoundation&lt;/code&gt; has the purpose of arranging different assets and types of assets into a single asset for playback or processing. Asset types can include audio and video subtitles, metadata, and text. When working with &lt;code&gt;AVComposition&lt;/code&gt; it is helpful to think about &lt;code&gt;AVAsset&lt;/code&gt; and &lt;code&gt;AVAssetTrack&lt;/code&gt; objects. Unfortunately, because these items are so similar (&lt;code&gt;AVComposition&lt;/code&gt; is a subclass of &lt;code&gt;AVAsset&lt;/code&gt;) it is easy to get confused. Try to remember that the &lt;code&gt;AVTrack&lt;/code&gt; is a wrapper around the actual pixel, sound-wave, or other data and that &lt;code&gt;AVAsset&lt;/code&gt; is a collection of &lt;code&gt;AVTrack&lt;/code&gt; objects. When we want to combine and manipulate &lt;code&gt;AVAsset&lt;/code&gt; objects, an &lt;code&gt;AVComposition&lt;/code&gt; helps us do that. Changes made to any &lt;code&gt;AVAsset&lt;/code&gt; by an &lt;code&gt;AVComposition&lt;/code&gt; will not impact the original media file.&lt;/p&gt;
&lt;p&gt;As an extra layer, Apple keeps the editing features of an &lt;code&gt;AVComposition&lt;/code&gt; separate from the playback features by creating an &lt;code&gt;AVMutableComposition&lt;/code&gt; object. You will work with an &lt;code&gt;AVMutableComposition&lt;/code&gt; object in this tutorial.&lt;/p&gt;
&lt;p&gt;Each track in our composition will have a start time and a duration. The composition will ensure that all of the data it holds displays at the right time and that the tracks stay in sync.&lt;/p&gt;
&lt;p&gt;At the simplest, a video clip is a single &lt;code&gt;AVTrack&lt;/code&gt; of audio data and a single &lt;code&gt;AVTrack&lt;/code&gt; of video data.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;avasset&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 414px) 414px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;414&quot; height=&quot;373&quot; src=&quot;https://img.ly/_astro/avasset_dtjkd.webp&quot; srcset=&quot;/_astro/avasset_dtjkd.webp 414w&quot;&gt;&lt;/p&gt;
&lt;p&gt;So, to set up an empty &lt;code&gt;AVComposition&lt;/code&gt; for combining clips, use code such as this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; movie &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVMutableComposition&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; videoTrack &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; movie.&lt;/span&gt;&lt;span&gt;addMutableTrack&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;withMediaType&lt;/span&gt;&lt;span&gt;: .video, &lt;/span&gt;&lt;span&gt;preferredTrackID&lt;/span&gt;&lt;span&gt;: kCMPersistentTrackID_Invalid)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; audioTrack &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; movie.&lt;/span&gt;&lt;span&gt;addMutableTrack&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;withMediaType&lt;/span&gt;&lt;span&gt;: .audio, &lt;/span&gt;&lt;span&gt;preferredTrackID&lt;/span&gt;&lt;span&gt;: kCMPersistentTrackID_Invalid)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above creates a new, empty &lt;code&gt;AVMutableComposition&lt;/code&gt; and then adds two tracks. One track will be able to hold &lt;code&gt;.video&lt;/code&gt; assets, and the other will hold &lt;code&gt;.audio&lt;/code&gt; assets. &lt;code&gt;AVFoundation&lt;/code&gt; supports many different kinds of audio and video formats, but the &lt;code&gt;.video&lt;/code&gt; track cannot hold &lt;code&gt;.audio&lt;/code&gt; formats. The &lt;code&gt;kCMPersistendTrackID_Invalid&lt;/code&gt; signals that you want a new, unique track id generated.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;empty-composition&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 130px) 130px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;130&quot; height=&quot;450&quot; src=&quot;https://img.ly/_astro/empty-composition_2frt4p.webp&quot; srcset=&quot;/_astro/empty-composition_2frt4p.webp 130w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;AVFoundation&lt;/code&gt; can manipulate any of the ISO media formats. The Quicktime (&lt;code&gt;.mov&lt;/code&gt;) and MPEG (&lt;code&gt;.mp4&lt;/code&gt;) are the most common, however, &lt;code&gt;.heif&lt;/code&gt;, &lt;code&gt;.3gp&lt;/code&gt; and other formats are all supported.&lt;/p&gt;
&lt;h2 id=&quot;adding-video-clips&quot;&gt;Adding Video Clips&lt;/h2&gt;
&lt;p&gt;After creation, the &lt;code&gt;AVMutableComposition&lt;/code&gt; has a start time of &lt;code&gt;CMTime.zero&lt;/code&gt; and a duration of &lt;code&gt;CMTime.zero&lt;/code&gt;. Use the &lt;code&gt;insertTimeRange(_ timeRange: CMTimeRange, of track: AVAssetTrack, at startTime: CMTime) throws&lt;/code&gt; function on the audio and again on the video track to add data from the clip. In order to add data to the tracks, you load an &lt;code&gt;AVAsset&lt;/code&gt;, determine what range of time will go into the new composition, and then call the insert function. Your code might look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; beachMovie &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVURLAsset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;beach&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; beachAudioTrack &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; beachMovie.&lt;/span&gt;&lt;span&gt;tracks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;withMediaType&lt;/span&gt;&lt;span&gt;: .audio).&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; //2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; beachVideoTrack &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; beachMovie.&lt;/span&gt;&lt;span&gt;tracks&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;withMediaType&lt;/span&gt;&lt;span&gt;: .video).&lt;/span&gt;&lt;span&gt;first&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; beachRange &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CMTimeRangeMake&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt;: CMTime.zero, &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;: beachMovie.duration) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  try&lt;/span&gt;&lt;span&gt; videoTrack&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;insertTimeRange&lt;/span&gt;&lt;span&gt;(beachRange, &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;: beachVideoTrack, &lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;span&gt;: CMTime.zero) &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  try&lt;/span&gt;&lt;span&gt; audioTrack&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;insertTimeRange&lt;/span&gt;&lt;span&gt;(beachRange, &lt;/span&gt;&lt;span&gt;of&lt;/span&gt;&lt;span&gt;: beachAudioTrack, &lt;/span&gt;&lt;span&gt;at&lt;/span&gt;&lt;span&gt;: CMTime.zero)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what this code does:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Load a video file from a local URL. In the example, the video is in the application bundle, but it could also be a URL that points to a file somewhere else. This will not work with a streaming URL. It needs to be a video file.&lt;/li&gt;
&lt;li&gt;Extract the main videos and audio tracks from the video file. There may be multiple video and audio tracks in a file, but this code just grabs the first one.&lt;/li&gt;
&lt;li&gt;Create a time range. In this example, the range is the same duration as the clip we loaded.&lt;/li&gt;
&lt;li&gt;Insert the video track from the clip at the beginning of the video track of the composition and insert the audio track from the clip at the beginning of the audio track of the composition. A common mistake in this step is to use the &lt;code&gt;.duration&lt;/code&gt; property of the composition to insert the audio and video at the end. However, as soon as media is inserted into any track, the duration of the entire composition will change. So the audio and video would be played one after the other and you would see a blank screen when the audio is playing, and have no audio when the video is playing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Repeat the steps for each clip that you want to add to the final composition.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;completed-composition&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 630px) 630px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;630&quot; height=&quot;398&quot; src=&quot;https://img.ly/_astro/completed-composition_AtrLx.webp&quot; srcset=&quot;/_astro/completed-composition_AtrLx.webp 630w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;using-the-composition&quot;&gt;Using the Composition&lt;/h2&gt;
&lt;p&gt;After all of the clips have been combined into the composition, it can be played in an &lt;code&gt;AVPlayer&lt;/code&gt; or exported using an &lt;code&gt;AVAssetExportSession&lt;/code&gt;. The code for these is the exact same as for any other &lt;code&gt;AVAsset&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  self&lt;/span&gt;&lt;span&gt;.player &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;playerItem&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;AVPlayerItem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;: movie)) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; playerLayer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayerLayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;player&lt;/span&gt;&lt;span&gt;: player) &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  playerLayer.frame &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; playerView.layer.bounds&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  playerLayer.videoGravity &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .resizeAspect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  playerView.layer.&lt;/span&gt;&lt;span&gt;addSublayer&lt;/span&gt;&lt;span&gt;(playerLayer) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  player&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what the code above is doing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an &lt;code&gt;AVPlayer&lt;/code&gt; using a &lt;code&gt;movie&lt;/code&gt; object, which is the &lt;code&gt;AVMutableComposition&lt;/code&gt; that was created earlier.&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;playerLayer&lt;/code&gt; to display the video and set its aspect ratio&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;playerLayer&lt;/code&gt; to &lt;code&gt;playerView&lt;/code&gt; which is a regular &lt;code&gt;UIView&lt;/code&gt; in a &lt;code&gt;UIViewController&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Play the video clip&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Alternatively you may want to export the new composition as a new movie file. Again, because the composition is an &lt;code&gt;AVAsset&lt;/code&gt; using a standard &lt;code&gt;AVAssetExportSession&lt;/code&gt; will write the movie to disk.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //create exporter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; exporter &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVAssetExportSession&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;asset&lt;/span&gt;&lt;span&gt;: movie,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                 presetName&lt;/span&gt;&lt;span&gt;: AVAssetExportPresetHighestQuality) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //configure exporter&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputURL &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; outputMovieURL &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.outputFileType &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .mov&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //export!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;exportAsynchronously&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;completionHandler&lt;/span&gt;&lt;span&gt;: { [&lt;/span&gt;&lt;span&gt;weak&lt;/span&gt;&lt;span&gt; exporter] &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    DispatchQueue.main.&lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; error &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; exporter&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;failed &lt;/span&gt;&lt;span&gt;\(error.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;movie has been exported to &lt;/span&gt;&lt;span&gt;\(outputMovieURL)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what this code is doing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an export session using the &lt;code&gt;movie&lt;/code&gt; composition you made earlier.&lt;/li&gt;
&lt;li&gt;Set the save location to some file URL on your device.&lt;/li&gt;
&lt;li&gt;Wait for the exporter to finish and display the result.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Don’t forget that exporting an &lt;code&gt;AVAsset&lt;/code&gt; can take a long time and is asynchronous. You will want to display a spinner or a message to your user.&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;You can now combine clips into a longer clip for playback and export. However, trimming the original clips, adding filters, and rearranging the order will require more work before your application is ready for the store. Using an SDK like the IMG.LY’s &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;VideoEditor SDK&lt;/a&gt; can help you provide an app that has the features users will expect in addition to stitching clips together.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;VideoEditorSDK&lt;/code&gt; offers a &lt;a href=&quot;https://img.ly/docs/vesdk/ios/guides/video-composition/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;Video Composition Controller&lt;/a&gt; which will allow the users to stitch clips together. Additionally, it will allow the user to add new clips and edit clips using the other editing tools. Because the Video Composition Controller is a subclass of &lt;code&gt;UIViewController&lt;/code&gt; it can just be configured and dropped into your application. Instead of the code in our example, you pass the videos to the controller:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; videoClips &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;stream&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;beach&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  Bundle.main.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forResource&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mountain&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;withExtension&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mov&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;].&lt;/span&gt;&lt;span&gt;compactMap&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;$0&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;AVURLAsset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;$0&lt;/span&gt;&lt;span&gt;) } }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; video &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; Video&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;assets&lt;/span&gt;&lt;span&gt;: videoClips)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; videoEditViewController &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; VideoEditViewController&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;videoAsset&lt;/span&gt;&lt;span&gt;: video, &lt;/span&gt;&lt;span&gt;configuration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Configuration&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;present&lt;/span&gt;&lt;span&gt;(videoEditViewController, &lt;/span&gt;&lt;span&gt;animated&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;completion&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above loads the same video clips as our demo application and then uses the &lt;code&gt;VideoEditViewController&lt;/code&gt; to present them. From there the user can make further edits preview and export the composition.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;stitch-clips&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 414px) 414px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;414&quot; height=&quot;896&quot; src=&quot;https://img.ly/_astro/stitch-clips_Ceb6W.webp&quot; srcset=&quot;/_astro/stitch-clips_Ceb6W.webp 414w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this example, you saw how to combine several video clips, each with one video and one audio track into a single movie file with one video and one audio track. To make more complex movie files consider building on this tutorial by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a second audio track, &lt;code&gt;AVFoundation&lt;/code&gt; will play sounds from both tracks at times specified at equal volume. Use &lt;code&gt;AVAudioMix&lt;/code&gt; to adjust the volume of the tracks at different times (the sample project has an example of adding a second audio track).&lt;/li&gt;
&lt;li&gt;Add more video tracks and use &lt;code&gt;AVMutableCompositionLayerInstruction&lt;/code&gt; to determine which track is visible at any time in the final video and to make fancy transitions between tracks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions!&lt;/p&gt;
&lt;p&gt;Looking to integrate video capabilities into your app? Check out our &lt;a href=&quot;https://img.ly/products/video-sdk&quot;&gt;Video Editor SDK&lt;/a&gt;, &lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation&quot;&gt;Short Video Creation&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/camera-sdk&quot;&gt;Camera SDK&lt;/a&gt;!&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2022/01/combine-merge-clips-iOS-swift.png" medium="image"/><category>Video Editing</category><category>iOS App Development</category><category>iOS</category><category>Swift</category><category>How-To</category><category>Tutorial</category></item><item><title>How to Add a Filter to a Video Stream in iOS</title><link>https://img.ly/blog/how-to-add-a-filter-to-a-video-stream-in-ios/</link><guid isPermaLink="true">https://img.ly/blog/how-to-add-a-filter-to-a-video-stream-in-ios/</guid><description>Applying stunning filters to videos requires different methods than applying them to files in iOS. Find out how!</description><pubDate>Mon, 08 Nov 2021 15:33:55 GMT</pubDate><content:encoded>&lt;p&gt;Using filters and effects with video streams requires different strategies than applying them to files. In this tutorial, you will see how to apply effects and filters to video streams in real-time. The code in this tutorial compiles with Xcode 13.&lt;/p&gt;
&lt;p&gt;As with most &lt;code&gt;AVFoundation&lt;/code&gt; code, the Xcode simulator is not the best platform for running the code. Test on an actual device. A project with code that supports this tutorial can be found &lt;a href=&quot;https://github.com/waltertyree/filter-video-stream&quot;&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;streams-or-files&quot;&gt;Streams or Files&lt;/h2&gt;
&lt;p&gt;For video files stored on the device, you can use an &lt;code&gt;AVMutableVideoComposition&lt;/code&gt; to apply filters. When streaming video from a remote location, this strategy will not work. The &lt;code&gt;AVMutuableVideoComposition&lt;/code&gt; classes are not designed to work in real-time with streams. Apple provides a way to extract the pixels from the stream at regular intervals. You can manipulate the pixels and then render them to the screen. An &lt;code&gt;AVPlayerItemVideoOutput&lt;/code&gt; object gives access to the pixels that make up a video frame. Apple also supplies a &lt;code&gt;CADisplayLink&lt;/code&gt; object to ensure you are extracting the right pixels at the right time. The display link is a specialized timer that will fire in sync with the redraw rate of the current display.&lt;/p&gt;
&lt;p&gt;So, to filter a video stream, the strategy becomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Set up an AVPlayer as normal and assign it the URL of the stream&lt;/li&gt;
&lt;li&gt;Attach an &lt;code&gt;AVPlayerItemVideoOutput&lt;/code&gt; object to the stream&lt;/li&gt;
&lt;li&gt;Attach a &lt;code&gt;CADisplayLink&lt;/code&gt; object to the run loop&lt;/li&gt;
&lt;li&gt;Extract the current pixel buffer from the AVPlayerItem every time the screen redraws&lt;/li&gt;
&lt;li&gt;Convert the pixel buffer to a CoreImage object&lt;/li&gt;
&lt;li&gt;Apply filters&lt;/li&gt;
&lt;li&gt;Display the image&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;setting-up-avplayer&quot;&gt;Setting up AVPlayer&lt;/h2&gt;
&lt;p&gt;You won’t use the &lt;code&gt;AVPlayerLayer&lt;/code&gt; or &lt;code&gt;AVPlayerViewController&lt;/code&gt; to display the video, but the &lt;code&gt;AVPlayer&lt;/code&gt; plays a central role. The &lt;code&gt;AVPlayer&lt;/code&gt; keeps the video in sync with the audio, handles decoding whatever format the stream uses, controls buffering, and more. The basic setup is the same as with any other project:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//create a player&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; videoItem &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayerItem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: streamURL)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.player &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;playerItem&lt;/span&gt;&lt;span&gt;: videoItem)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The URL can either point to a local resource or a stream.&lt;/p&gt;
&lt;h2 id=&quot;using-avplayeritemvideooutput&quot;&gt;Using AVPlayerItemVideoOutput&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;AVPlayerItemVideoOutput&lt;/code&gt; allows you to query the &lt;code&gt;AVPlayerItem&lt;/code&gt; for the pixel buffer (one screen worth of pixels) at any given time. You can specify different pixel formats and other options for the output. For this tutorial, you will you can create the object with a simple instantiation using the default formats.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let playerItemVideoOutput = AVPlayerItemVideoOutput()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Later you will add this video output to your player item. Then you can ask it to generate the pixel buffers as needed. In this tutorial, you will ask for the pixel buffer of the current frame. You could ask for the frame for any timestamp though.&lt;/p&gt;
&lt;h2 id=&quot;creating-the-display-link&quot;&gt;Creating the Display Link&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;CADisplayLink&lt;/code&gt; class is a specialized &lt;code&gt;NSTimer&lt;/code&gt; that synchronizes itself to the screen refresh rate of the display. This ensures that the pixel buffer you extract will be from the correct time of your video stream. It will get rendered at the correct time in the screen refresh. Syncing a standard &lt;code&gt;NSTimer&lt;/code&gt; to the screen refresh has always been problematic. With the new variable refresh rate screens that Apple makes, it becomes impossible. However, &lt;code&gt;CADisplayLink&lt;/code&gt; adjusts its rate as the screen refresh rate changes. Apple explains how this works in the &lt;a href=&quot;https://developer.apple.com/videos/play/wwdc2021/10147/&quot;&gt;2021 WWDC Session &lt;em&gt;Optimize for Variable Refresh Rate Displays&lt;/em&gt;&lt;/a&gt;. When you create the display link, you give it the name of a function to call each time it executes. A sample for creating a link could look like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;lazy&lt;/span&gt;&lt;span&gt; var&lt;/span&gt;&lt;span&gt; displayLink: CADisplayLink &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CADisplayLink&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                                  selector&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#selector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;displayLinkFired&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;link:&lt;/span&gt;&lt;span&gt;)))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Elsewhere in the code, a function called &lt;code&gt;displayLinkFired(link: CADisplayLink)&lt;/code&gt; extracts the pixel buffer from the &lt;code&gt;AVPlayerItem&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;starting-the-player&quot;&gt;Starting the Player&lt;/h2&gt;
&lt;p&gt;Apple notes that loading a video file takes a measurable amount of time. It can take even longer when the video file is streaming across a network. AVFoundation allows your program to continue to execute while the setup steps and initial buffering are occurring in the background. You can run into issues if you load a video into AVFoundation and then immediately attempt to work with it. AVFoundation may not show an error or status message, but it will not perform as expected either.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;most important&lt;/em&gt; step in this entire process is using an observer to wait until the AVPlayerItem has a &lt;code&gt;.readyToPlay&lt;/code&gt; status before attaching the output and displaying link objects. In the example below &lt;code&gt;statusOberserver&lt;/code&gt;, &lt;code&gt;player&lt;/code&gt;, &lt;code&gt;playerItemVideoOutput&lt;/code&gt; and &lt;code&gt;displayLink&lt;/code&gt; are all declared at the class level. They need to persist the entire time the video is being used.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//create a player&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; videoItem &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayerItem&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;: streamURL&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.player &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; AVPlayer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;playerItem&lt;/span&gt;&lt;span&gt;: videoItem) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;//*important* add the display link and the output only after it is ready to play&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.statusObserver &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; videoItem.&lt;/span&gt;&lt;span&gt;observe&lt;/span&gt;&lt;span&gt;(\.status, &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                       options&lt;/span&gt;&lt;span&gt;: [.new, .old],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                 changeHandler&lt;/span&gt;&lt;span&gt;: { playerItem, change &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; playerItem.status &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; .readyToPlay { &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      playerItem.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.playerItemVideoOutput)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      self&lt;/span&gt;&lt;span&gt;.displayLink.&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: .main, &lt;/span&gt;&lt;span&gt;forMode&lt;/span&gt;&lt;span&gt;: .common)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      self&lt;/span&gt;&lt;span&gt;.player&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;play&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what to notice about the code above&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creating the player will take a large amount of time, but the program execution will not stop and wait&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NSKeyValueObservation&lt;/code&gt; is how swift defines key value observers the code will execute every time the &lt;code&gt;videoItem.status&lt;/code&gt; property changes values&lt;/li&gt;
&lt;li&gt;Checks to see if the &lt;code&gt;.status&lt;/code&gt; property is &lt;code&gt;.readyToPlay&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;After adding the display link and the video output objects start playing the video&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;extracting-the-pixel-buffer&quot;&gt;Extracting the Pixel Buffer&lt;/h2&gt;
&lt;p&gt;Once the video begins to play, the display link function gets called for each screen refresh. Here is an example of how to extract the pixel buffer and render it to a &lt;code&gt;UIImageView&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;@objc&lt;/span&gt;&lt;span&gt; func&lt;/span&gt;&lt;span&gt; displayLinkFired&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;link&lt;/span&gt;&lt;span&gt;: CADisplayLink) { &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  let&lt;/span&gt;&lt;span&gt; currentTime &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; playerItemVideoOutput.&lt;/span&gt;&lt;span&gt;itemTime&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forHostTime&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;CACurrentMediaTime&lt;/span&gt;&lt;span&gt;())&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; playerItemVideoOutput.&lt;/span&gt;&lt;span&gt;hasNewPixelBuffer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forItemTime&lt;/span&gt;&lt;span&gt;: currentTime) { &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; buffer &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; playerItemVideoOutput.&lt;/span&gt;&lt;span&gt;copyPixelBuffer&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;forItemTime&lt;/span&gt;&lt;span&gt;: currentTime, &lt;/span&gt;&lt;span&gt;itemTimeForDisplay&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;nil&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      let&lt;/span&gt;&lt;span&gt; frameImage &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;cvImageBuffer&lt;/span&gt;&lt;span&gt;: buffer) &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      //4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      let&lt;/span&gt;&lt;span&gt; pixelate &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CIFilter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;CIPixellate&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      pixelate.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(frameImage, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputImageKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      pixelate.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.filterSlider.&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputScaleKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      pixelate.&lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;CIVector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;: frameImage.extent.midX, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: frameImage.extent.midY), &lt;/span&gt;&lt;span&gt;forKey&lt;/span&gt;&lt;span&gt;: kCIInputCenterKey)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     let&lt;/span&gt;&lt;span&gt; newFrame &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; pixelate.outputImage&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;cropped&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: frameImage.extent)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     self&lt;/span&gt;&lt;span&gt;.videoView.&lt;/span&gt;&lt;span&gt;image&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; UIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ciImage&lt;/span&gt;&lt;span&gt;: newFrame) &lt;/span&gt;&lt;span&gt;//5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is what the code is doing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Marks the &lt;code&gt;func&lt;/code&gt; with &lt;code&gt;@objc&lt;/code&gt; so that it works with the display link selector&lt;/li&gt;
&lt;li&gt;Asks if there is a new pixel buffer to display. Depending on the refresh rate of the screen and the frame rate of the video, there will not always be a new buffer.&lt;/li&gt;
&lt;li&gt;After extracting the pixel buffer, convert it to a CoreImage object&lt;/li&gt;
&lt;li&gt;Apply any filters. This example applies the &lt;code&gt;CIPixellate&lt;/code&gt; filter. It uses a slider to let the user change the intensity as the video plays&lt;/li&gt;
&lt;li&gt;Convert the filtered image to a &lt;code&gt;UIImage&lt;/code&gt; and assign it to the &lt;code&gt;UIImageView&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now as the pixellate scale changes the image will be filtered but the video will continue to play smoothly.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;video-intensity-iOS-filter-video-stream&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 1000px) 1000px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;1000&quot; height=&quot;497&quot; src=&quot;https://img.ly/_astro/video-intensity-iOS-filter-video-stream_Z29kdA1.webp&quot; srcset=&quot;/_astro/video-intensity-iOS-filter-video-stream_VOjmc.webp 640w, /_astro/video-intensity-iOS-filter-video-stream_Z2eFwGs.webp 750w, /_astro/video-intensity-iOS-filter-video-stream_1oxxcg.webp 828w, /_astro/video-intensity-iOS-filter-video-stream_Z29kdA1.webp 1000w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;Using the strategy above you should be able to apply different filters to the video stream in real-time. Remember that you only have a few milliseconds though, then the video will begin to stutter. CoreImage filters are optimized to make use of the GPU and should be fast enough in most cases. For better performance, you can render the filtered image to an &lt;code&gt;MTKView&lt;/code&gt; and use Metal. Apple has also begun to create Metal Performance Shaders which are even more efficient than CoreImage filters when used with a MetalKit view. As of now, there are many more CoreImage filters, so that may give you the most flexibility.&lt;br&gt;
The strategy in this tutorial is adequate for filtering video streams. If you want to give your users more flexibility with filters and other video tweaks, an editor such as &lt;a href=&quot;https://img.ly/products/video-sdk/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VideoEditorSDK&lt;/a&gt; can let you focus on your application’s core functions and let a team of professionals worry about the video. Using VideoEditorSDK your users can apply filters to video as well as text annotations and more.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;stream-edit-ios&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 600px) 600px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;600&quot; height=&quot;640&quot; src=&quot;https://img.ly/_astro/stream-edit-ios_Z1MTqzv.webp&quot; srcset=&quot;/_astro/stream-edit-ios_Z1MTqzv.webp 600w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Looking for more video capabilities? Check out our solutions for &lt;a href=&quot;https://img.ly/use-cases/story-reels-short-video-creation&quot;&gt;Short Video Creation&lt;/a&gt;, and &lt;a href=&quot;https://img.ly/products/camera-sdk&quot;&gt;Camera SDK&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions or suggestions.&lt;/p&gt;
&lt;p&gt;You like what we do? Check our &lt;a href=&quot;https://img.ly/company/careers&quot;&gt;careers page&lt;/a&gt;. Even if you can’t find your role listed, we might be interested since you are already here!&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2021/11/video-filter-ios-video-stream.jpg" medium="image"/><category>iOS App Development</category><category>iOS</category><category>Video Editing</category><category>App Development</category><category>Mobile App Development</category><category>Video Filter</category><category>Apple</category><category>Developer Tools</category><category>How-To</category><category>Tutorial</category></item><item><title>How To Record Screen Video Using ReplayKit for iOS</title><link>https://img.ly/blog/record-screen-with-swift-replaykit/</link><guid isPermaLink="true">https://img.ly/blog/record-screen-with-swift-replaykit/</guid><description>Use Swift and ReplayKit to add screen recording to your application. Also use the new rolling clips feature introduced in iOS 15!</description><pubDate>Tue, 21 Sep 2021 15:05:08 GMT</pubDate><content:encoded>&lt;p&gt;In this article, you will see how to use Swift and &lt;code&gt;ReplayKit&lt;/code&gt; to add screen recording to your application. You will also see how to use the new rolling clips feature, introduced in iOS 15. The code in this article uses Swift 5 and Xcode 13. Clone &lt;a href=&quot;https://github.com/waltertyree/replay-screen-video&quot;&gt;this repository&lt;/a&gt; for a sample project and example code.&lt;/p&gt;
&lt;h2 id=&quot;replaykit-basics&quot;&gt;ReplayKit Basics&lt;/h2&gt;
&lt;p&gt;Apple introduced the &lt;code&gt;ReplayKit&lt;/code&gt; framework in 2015 with iOS 9. They have refined and expanded it since then, but it still has a small API. &lt;code&gt;ReplayKit&lt;/code&gt; uses the same shared components as AirPlay and the device screen recorder (accessible from the device Control Center). For this reason, &lt;code&gt;ReplayKit&lt;/code&gt; functions will not work when using AirPlay or playing video. Also, the screen recording functions do not work on the Xcode simulators. You should test using a physical device.&lt;/p&gt;
&lt;p&gt;Your app makes all its calls to the &lt;code&gt;RPScreenRecorder.shared()&lt;/code&gt; object. This is a singleton object for your app that &lt;code&gt;ReplayKit&lt;/code&gt; provides. There is no need to store it in a variable or pass it around in your code.&lt;/p&gt;
&lt;h2 id=&quot;rolling-clip-recording&quot;&gt;Rolling Clip Recording&lt;/h2&gt;
&lt;p&gt;In iOS 15, Apple introduced rolling clip recording. &lt;code&gt;ReplayKit&lt;/code&gt; will maintain an updated video buffer of the most recent fifteen seconds. Your app can request the video at any time. &lt;code&gt;ReplayKit&lt;/code&gt; comes from &lt;code&gt;GameCenter&lt;/code&gt;. Apple’s idea for this feature is for creating a short, sharable video of the moments before an exciting event in a game. Another use can be for a user to record the steps leading up to a defect during testing. When the user is unlikely to know in advance that they will want a screen recording, rolling clips may be a good solution.&lt;/p&gt;
&lt;p&gt;To start rolling clip buffering, include a method such as this one&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RPScreenRecorder.&lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;startClipBuffering&lt;/span&gt;&lt;span&gt; { err &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; err &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; err {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //either the user denied permission or something like&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    //AVPlayer or AirPlay is using the shared recorder resources.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Error attempting to start buffering &lt;/span&gt;&lt;span&gt;\(err.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Clip buffering started.&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Screen recording has privacy implications. &lt;code&gt;ReplayKit&lt;/code&gt; presents the user with a modal, requesting to record before it starts. If the user denies the request or if the buffering is unable to start, &lt;code&gt;ReplayKit&lt;/code&gt; passes an &lt;code&gt;Error&lt;/code&gt; object to the closure. Unlike other privacy modals, you do not have an opportunity to add your own language to the message. The modal appears as soon as you execute the &lt;code&gt;.startClipBuffering&lt;/code&gt; command, so be sure that the user is not surprised by it. Execute the command at a logical transition in your application. After the user grants permissions, the modal will not appear again unless eight minutes pass before the next call to &lt;code&gt;.startClipBuffering&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;permissions-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 250px) 250px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;250&quot; height=&quot;193&quot; src=&quot;https://img.ly/_astro/permissions-1_2qx6ET.webp&quot; srcset=&quot;/_astro/permissions-1_2qx6ET.webp 250w&quot;&gt;&lt;/p&gt;
&lt;p&gt;While the clip buffering runs, at any time, you can save the buffer to a URL on the device. The clip you save can be any duration up to fifteen seconds. If the recorder is unable to write the clip to disk, it will send an error.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; clipURL: URL &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fileURLWithPath&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;NSTemporaryDirectory&lt;/span&gt;&lt;span&gt;()).&lt;/span&gt;&lt;span&gt;appendingPathComponent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\(&lt;/span&gt;&lt;span&gt;UUID&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.mov&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RPScreenRecorder.&lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;exportClip&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;to&lt;/span&gt;&lt;span&gt;: clipURL, &lt;/span&gt;&lt;span&gt;duration&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;TimeInterval&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;15&lt;/span&gt;&lt;span&gt;)) { err &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; err &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; err {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;An error? &lt;/span&gt;&lt;span&gt;\(err.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt; )&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Clip saved to url &lt;/span&gt;&lt;span&gt;\(clipURL)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above stores a fifteen-second clip at a URL in the user’s temporary directory. The temporary directory gets cleared by the system, but not while the app is running. To ensure that the files are not deleted except by the user, store them in the user’s documents directory instead.&lt;br&gt;
When your app is done collecting video, use &lt;code&gt;.stopClipBuffering&lt;/code&gt; to allow the system to clean up resources and stop the recording.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RPScreenRecorder.&lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;stopClipBuffering&lt;/span&gt;&lt;span&gt; { err &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; err &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; err {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Error attempting to stop buffering &lt;/span&gt;&lt;span&gt;\(err.&lt;/span&gt;&lt;span&gt;localizedDescription&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  } &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Clip buffering stopped.&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;recording-screen-and-app-audio&quot;&gt;Recording Screen and App Audio&lt;/h2&gt;
&lt;p&gt;Rolling clip buffering runs in the background. You can also allow the user to control when to start and stop screen recording. The API looks similar and is available since iOS 10.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RPScreenRecorder.&lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;startRecording&lt;/span&gt;&lt;span&gt; { err &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  guard&lt;/span&gt;&lt;span&gt; err &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; nil&lt;/span&gt;&lt;span&gt; else&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(err.&lt;/span&gt;&lt;span&gt;debugDescription&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //code to display recording indicator&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As with the rolling clip buffering, the system will display a permissions dialog. If the user denies permission you will get an error and the recording will not start.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;recording-permissions-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 250px) 250px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;250&quot; height=&quot;193&quot; src=&quot;https://img.ly/_astro/recording-permissions-1_ZEkXHU.webp&quot; srcset=&quot;/_astro/recording-permissions-1_ZEkXHU.webp 250w&quot;&gt;&lt;/p&gt;
&lt;p&gt;The same eight-minute rule applies as for rolling clip recording. But, since the user has just taken some action to start recording, they should expect to see this modal.&lt;br&gt;
Apple provides a basic view controller for editing and sharing the video.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ReplayKit&lt;/code&gt; passes the &lt;code&gt;RPPreviewViewController&lt;/code&gt; to the &lt;code&gt;.stopRecording&lt;/code&gt; handler.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RPScreenRecorder.&lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;stopRecording&lt;/span&gt;&lt;span&gt; { preview, err &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  guard&lt;/span&gt;&lt;span&gt; let&lt;/span&gt;&lt;span&gt; preview &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; preview &lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;print&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;no preview window&quot;&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //update recording controls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  preview.modalPresentationStyle &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; .overFullScreen&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  preview.previewControllerDelegate &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  self&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;present&lt;/span&gt;&lt;span&gt;(preview, &lt;/span&gt;&lt;span&gt;animated&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;preview&lt;/code&gt; object above is the &lt;code&gt;RPPreviewViewController&lt;/code&gt; editor. You can display it as a modal or as a popover depending on your needs. The Apple provided editor will allow the user to save to their camera roll or share the clip after editing.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;apple-editor-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;866&quot; src=&quot;https://img.ly/_astro/apple-editor-1_5tScc.webp&quot; srcset=&quot;/_astro/apple-editor-1_5tScc.webp 400w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notice in the code above that there is a &lt;code&gt;previewControllerDelegate&lt;/code&gt;. You will need to add some code to the delegate to cleanup and dismiss the editor when the user is done sharing or saving. An example is below.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;func&lt;/span&gt;&lt;span&gt; previewControllerDidFinish&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt; previewController: RPPreviewViewController) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  previewController.&lt;/span&gt;&lt;span&gt;dismiss&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;animated&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;) { [&lt;/span&gt;&lt;span&gt;weak&lt;/span&gt;&lt;span&gt; self&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //reset the UI and show the recording controls&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;using-many-uiwindow-objects&quot;&gt;Using Many &lt;code&gt;UIWindow&lt;/code&gt; Objects&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ReplayKit&lt;/code&gt; recording will only capture the main window of your application. When designing for screen recording, you can place any UI elements in a separate &lt;code&gt;UIWindow&lt;/code&gt; object. Although applications typically only have one &lt;code&gt;UIWindow&lt;/code&gt;, iOS allows many windows. A &lt;code&gt;UIWindow&lt;/code&gt; requires a &lt;code&gt;.rootViewController&lt;/code&gt; to provide content. Then use it like any other view in your application.&lt;br&gt;
The UI for this application consists of three &lt;code&gt;UIWindow&lt;/code&gt; objects.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;combined-windows&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 300px) 300px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;300&quot; height=&quot;649&quot; src=&quot;https://img.ly/_astro/combined-windows_ZrRaan.webp&quot; srcset=&quot;/_astro/combined-windows_ZrRaan.webp 300w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Apple places the status bar in a separate window from your application. The recording controls are in a separate window from the main application and will not appear in the screen recording.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;exploded-windows&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 503px) 503px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;503&quot; height=&quot;360&quot; src=&quot;https://img.ly/_astro/exploded-windows_1AjROT.webp&quot; srcset=&quot;/_astro/exploded-windows_1AjROT.webp 503w&quot;&gt;&lt;/p&gt;
&lt;p&gt;To add some recording controls or a recording indicator to your app. Create a new &lt;code&gt;UIWindow&lt;/code&gt; and add it to the same &lt;code&gt;windowScene&lt;/code&gt; as your view.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;controlsWindow &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIWindow&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;frame&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;CGRect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;width&lt;/span&gt;&lt;span&gt;: view.frame.width, &lt;/span&gt;&lt;span&gt;height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;45&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; view.safeAreaInsets.top)) &lt;/span&gt;&lt;span&gt;//1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;controlsWindow&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.windowScene &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; view.window&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.windowScene &lt;/span&gt;&lt;span&gt;//2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;controlsWindow&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;makeKeyAndVisible&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;//3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; recordingIndicator &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIButton.&lt;/span&gt;&lt;span&gt;systemButton&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;UIImage&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;systemName&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;record.circle&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;action&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;#selector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;recordingToggled&lt;/span&gt;&lt;span&gt;(_:))) &lt;/span&gt;&lt;span&gt;//4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; vc &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; UIViewController&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;controlsWindow&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt;.rootViewController &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; vc &lt;/span&gt;&lt;span&gt;//5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;vc.&lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;addSubview&lt;/span&gt;&lt;span&gt;(recordingIndicator) &lt;/span&gt;&lt;span&gt;//6&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;recordingIndicator.center &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; CGPoint&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;: vc.&lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt;.center.x, &lt;/span&gt;&lt;span&gt;y&lt;/span&gt;&lt;span&gt;: vc.&lt;/span&gt;&lt;span&gt;view&lt;/span&gt;&lt;span&gt;.center.y &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; 20&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above creates the red/blue recording button and banner at the top of the view. It relies on a &lt;code&gt;controlsWindow&lt;/code&gt; variable created at the class level. Here is what it is doing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Creates a new &lt;code&gt;UIWindow&lt;/code&gt; that is the width of the view and tall enough for the button and the safe area (the area around the notch and status bar)&lt;/li&gt;
&lt;li&gt;Adds the window to the same &lt;code&gt;windowScene&lt;/code&gt; as the current view&lt;/li&gt;
&lt;li&gt;Makes the window visible (&lt;code&gt;UIWindow&lt;/code&gt; objects are &lt;code&gt;hidden&lt;/code&gt; when created) and brings it to the front of the stack of windows&lt;/li&gt;
&lt;li&gt;Creates a button and links it to a method in your class &lt;code&gt;recordingToggled(_:)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Adds a view controller to the window so that there will be some content&lt;/li&gt;
&lt;li&gt;Adds the button to the view controller&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Because the recording indicator and button are in a separate window, they will not appear in any screen recording. Be mindful when showing modal views or alert views, as they may appear behind the extra window. A good strategy is to hide the extra window when showing these views. You can use a similar strategy to hide any other UI elements that should not appear in screen recordings.&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;Though Apple provides a basic editor, you can provide your own editing tools. The examples above showed the rolling clip editor writes the video to the disk. The regular recorder can also write the video to disk. You can stop recording using this code:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;swift&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; clipURL: URL &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; URL&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;fileURLWithPath&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;NSTemporaryDirectory&lt;/span&gt;&lt;span&gt;()).&lt;/span&gt;&lt;span&gt;appendingPathComponent&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\(&lt;/span&gt;&lt;span&gt;UUID&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;.mov&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;RPScreenRecorder.&lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;stopRecording&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;withOutput&lt;/span&gt;&lt;span&gt;: clipURL) { err &lt;/span&gt;&lt;span&gt;in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  //check for an error and process the url&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method would replace the &lt;code&gt;.stopRecording { preview, err in&lt;/code&gt; method shown above.&lt;br&gt;
With the video saved, you can use an editor such as the &lt;a href=&quot;https://img.ly/products/video-sdk?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VideoEditor SDK&lt;/a&gt; to allow your users to add annotations, text and filters to their clips. They can even add audio or combine clips to make a great creation.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;with-editor&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 400px) 400px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;400&quot; height=&quot;866&quot; src=&quot;https://img.ly/_astro/with-editor_26cWBv.webp&quot; srcset=&quot;/_astro/with-editor_26cWBv.webp 400w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this article, you saw how to capture a rolling screen recording as well as how to capture the screen manually. Further, you saw how using an SDK such as &lt;a href=&quot;https://img.ly/products/video-sdk?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;VideoEditor SDK&lt;/a&gt; allows you to annotate and enhance your clips before sharing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions!&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2021/09/screen-record-video-on-app.jpg" medium="image"/><category>iOS App Development</category><category>iOS</category><category>ReplayKit</category><category>Apple</category><category>Mobile App Development</category><category>Video Editing</category><category>How-To</category><category>Tutorial</category></item><item><title>How to Create Image Filters for iOS</title><link>https://img.ly/blog/how-to-create-image-filters-in-ios/</link><guid isPermaLink="true">https://img.ly/blog/how-to-create-image-filters-in-ios/</guid><description>Write a custom `CIFilter` in Metal and Swift, usable in any CoreImage pipeline.</description><pubDate>Tue, 24 Aug 2021 08:09:37 GMT</pubDate><content:encoded>&lt;p&gt;In this tutorial, you will learn how to write a custom &lt;code&gt;CIFilter&lt;/code&gt; in Metal and Swift. You can use this filter in any CoreImage pipeline.&lt;/p&gt;
&lt;p&gt;Apple offers over 200 image filters in the CoreImage framework, but sometimes you need a little extra to make your images perfect. Apple provides two ways to create customize image filters. You can chain &lt;code&gt;CIFilter&lt;/code&gt;s together in a &lt;code&gt;CIFilter&lt;/code&gt; subclass or wrap a &lt;code&gt;CIKernel&lt;/code&gt; in a &lt;code&gt;CIFilter&lt;/code&gt; subclass. Either method creates a filter that CoreImage can execute on the GPU. That makes the filter fast. CoreImage filters can filter live video without impacting the frame rate. Before iOS 11 the only way to write a custom &lt;code&gt;CIKernel&lt;/code&gt; was to pass a string containing the code to the GPU at runtime using the &lt;a href=&quot;https://www.khronos.org/opengl/wiki/OpenGL_Shading_Language&quot;&gt;OpenGL Shading Language&lt;/a&gt;. This had two drawbacks: it was difficult to find errors in the string of commands and the kernel was not compiled until runtime. This tutorial will show you how to add a custom filter with a Metal-based &lt;code&gt;CIKernel&lt;/code&gt; to any Swift project in Xcode. The code samples in this tutorial use Xcode 12.5 and Swift 5.&lt;/p&gt;
&lt;p&gt;Clone &lt;a href=&quot;https://github.com/waltertyree/core-image-metal&quot;&gt;this repository&lt;/a&gt; for a sample project and example code that supports this tutorial. The repo also contains some other custom filters, demonstrating other aspects of Metal filters.&lt;/p&gt;
&lt;h2 id=&quot;adding-metal-support-to-an-xcode-project&quot;&gt;Adding Metal Support to an Xcode Project&lt;/h2&gt;
&lt;p&gt;The first step is to add a Metal source code file to your project.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;new-file&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 738px) 738px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;738&quot; height=&quot;529&quot; src=&quot;https://img.ly/_astro/new-file_2hEg5C.webp&quot; srcset=&quot;/_astro/new-file_Z1TNBBR.webp 640w, /_astro/new-file_2hEg5C.webp 738w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Find the “Metal File” template, select it and click &lt;code&gt;Next&lt;/code&gt;. Name the file and save it. At compile time, Xcode combines all of the &lt;code&gt;.metal&lt;/code&gt; files in your project into a single &lt;code&gt;.metallib&lt;/code&gt; file. So, organize your Metal code into lots of files or just one file as you prefer. The last step before you start writing code is to add compiler flags for &lt;code&gt;CIKernel&lt;/code&gt; objects.&lt;/p&gt;
&lt;p&gt;In the Build Settings, find the &lt;code&gt;other Metal Compiler Flags&lt;/code&gt; and add an entry of &lt;code&gt;-fcikernel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;build-settings-1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 630px) 630px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;630&quot; height=&quot;485&quot; src=&quot;https://img.ly/_astro/build-settings-1_1ww0y8.webp&quot; srcset=&quot;/_astro/build-settings-1_1ww0y8.webp 630w&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then find the &lt;code&gt;Other Metal Linker Flags&lt;/code&gt; and add an entry of &lt;code&gt;-cikernel&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;other-metal-linker-flag&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 630px) 630px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;630&quot; height=&quot;145&quot; src=&quot;https://img.ly/_astro/other-metal-linker-flag_Z2a5E9F.webp&quot; srcset=&quot;/_astro/other-metal-linker-flag_Z2a5E9F.webp 630w&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Important note:&lt;/em&gt; any project without &lt;code&gt;.metal&lt;/code&gt; files hides the Metal Compiler and Linker sections from build Settings.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-a-metal-file-for-coreimage-kernels&quot;&gt;Setting Up a Metal File for CoreImage Kernels&lt;/h2&gt;
&lt;p&gt;Metal is a general-purpose technology for writing code that will execute on the GPU. The compiler flags tell Metal that you will be writing code to work with &lt;code&gt;CoreImage&lt;/code&gt;. With a Metal source file in your project and the compiler flags set, you’re finally ready to write some code! Open the Metal file you created above. It should be empty except for&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#include &amp;#x3C;metal_stdlib&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;using namespace metal;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Below these lines, import the CoreImage headers by adding:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;#include &amp;#x3C;CoreImage/CoreImage.h&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and add a stub for your filter code with:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;extern &quot;C&quot; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  namespace coreimage {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    // KERNEL GOES HERE&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Prefix your kernel code with &lt;code&gt;extern &quot;C&quot;&lt;/code&gt; and the CoreImage namespace. You write kernel code in the &lt;a href=&quot;https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf&quot;&gt;Metal Shader Language&lt;/a&gt;, which is a variation of C. If you only know Swift, you should be able to rely on Xcode’s code-completion and symbol lookup to help you as you’re learning to write Metal code. In addition to the general language reference, Apple provides a &lt;a href=&quot;https://developer.apple.com/metal/MetalCIKLReference6.pdf&quot;&gt;Metal Shading Language for Core Image Kernels&lt;/a&gt; document.&lt;/p&gt;
&lt;p&gt;The example below creates a &lt;code&gt;CIColorKernel&lt;/code&gt;. This kernel is optimized for changing the color value of each pixel in an image. It will change each pixel to a grayscale version of itself. A filter applying his kernel will send the color value of each pixel of the image to the kernel.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;float4 grayscaleFilterKernel(sample_t s) { //1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  float gray = (s.r + s.g + s.b) / 3;      //2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  return float4(gray, gray, gray, s.a);    //3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is how this kernel works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The filter provides the color of the current pixel to the kernel as a &lt;code&gt;sample_t&lt;/code&gt; type. The kernel will return the new color for that pixel as a &lt;code&gt;float4&lt;/code&gt; type. The &lt;code&gt;sample_t&lt;/code&gt; type is equivalent to a &lt;code&gt;float4&lt;/code&gt; type and is only named differently because of historical convention. A &lt;code&gt;float4&lt;/code&gt; type contains four float values. In the case of color, they are the red, green, blue and alpha values for the pixel. Each float has a value between zero and 1.&lt;/li&gt;
&lt;li&gt;Calculate a grayscale version of the pixel by averaging the three color channels.&lt;/li&gt;
&lt;li&gt;Create a new &lt;code&gt;float4&lt;/code&gt; with the averaged value for the three color channels and the original alpha value for the fourth.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CoreImage also provides a &lt;code&gt;CIWarpKernel&lt;/code&gt; class that is optimized for changing the position of each pixel and a &lt;code&gt;CIBlendKernel&lt;/code&gt; for blending two images. For more complex tasks, CoreImage also provides a general &lt;code&gt;CIKernel&lt;/code&gt;. Apple suggests that chaining together optimized kernels in a pipeline is usually more efficient than trying to write a larger, general kernel. A color-optimized kernel only has access to the color of the current pixel. A transform-optimized kernel can only affect the position of the current pixel. A general kernel can impact color and position as well as read other values from the source image.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-a-cikernel-with-a-cifilter&quot;&gt;Wrapping a `CIKernel` with a `CIFilter`&lt;/h2&gt;
&lt;p&gt;Now you’ll make a &lt;code&gt;CIFilter&lt;/code&gt; subclass as the interface between the Metal code and your Swift code. Every &lt;code&gt;CIFilter&lt;/code&gt; has an &lt;code&gt;outputImage&lt;/code&gt; variable that returns a &lt;code&gt;CIImage&lt;/code&gt;. Generally, input variables for a filter begin with &lt;code&gt;input&lt;/code&gt;. The grayscale filter you are making has a single input and a single output. Create a new Swift file in your project and title it “GrayscaleFilter.swift”. Then be sure to import CoreImage by adding&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import CoreImage&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to the top of the file. Then create the filter as a subclass of &lt;code&gt;CIFilter&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class GrayscaleFilter: CIFilter {&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, create a &lt;code&gt;kernel&lt;/code&gt; variable as either &lt;code&gt;static&lt;/code&gt; or &lt;code&gt;lazy&lt;/code&gt;, so that it only gets created one time, regardless of how often it’s called.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;static var kernel: CIColorKernel = { () -&gt; CIColorKernel in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let url = Bundle.main.url(forResource: &quot;default&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                            withExtension: &quot;metallib&quot;)! //1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    let data = try! Data(contentsOf: url)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return try! CIColorKernel(functionName: &quot;grayscaleFilterKernel&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                      fromMetalLibraryData: data) //2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Look in the bundle for the compiled metal code. The metal code will have a filename of “default”.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.metalib&lt;/code&gt; may contain many kernels so use the one called “grayscaleFilterKernel”&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now add a variable to hold the input image.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;var inputImage: CIImage?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally override the &lt;code&gt;outputImage&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;override var outputImage: CIImage? {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    guard let inputImage = inputImage else { return .none }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    return GrayscaleFilter.kernel.apply(extent: inputImage.extent,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                   roiCallback: { (index, rect) -&gt; CGRect in&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                                  return rect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;                                  }, arguments: [inputImage])&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;apply&lt;/code&gt; function of the kernel takes an &lt;code&gt;extent&lt;/code&gt; argument. For a &lt;code&gt;CIImage&lt;/code&gt; the &lt;code&gt;extent&lt;/code&gt; property is the width and height of the image. The function has an &lt;code&gt;roiCallback&lt;/code&gt; that allows you to specify what part of the image the kernel uses for each calculation. For a color filter, you generally just pass back the &lt;code&gt;rect&lt;/code&gt;. Finally, pass in any &lt;code&gt;arguments&lt;/code&gt; as an array. Notice that there is not any type checking here. It is up to you to pass in the correct types in the correct order.&lt;/p&gt;
&lt;h2 id=&quot;using-the-filter&quot;&gt;Using the Filter&lt;/h2&gt;
&lt;p&gt;With the Metal code wrapped in a &lt;code&gt;CIFilter&lt;/code&gt; you can now apply the filter images the same as using one of the built-in filters. Create an instance of the filter, then assign the &lt;code&gt;inputImage&lt;/code&gt; variable a &lt;code&gt;CIImage&lt;/code&gt; and display the &lt;code&gt;outputImage&lt;/code&gt;. Your code might look something like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;let filter = GrayscaleFilter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;filter.inputImage = image&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;displayView.image = UIImage(ciImage: (filter.outputImage ?? image) ?? CIImage())&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and it can render a grayscale version of an image, like in this example.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;filter-example&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 820px) 820px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;820&quot; height=&quot;766&quot; src=&quot;https://img.ly/_astro/filter-example_Z1xaewd.webp&quot; srcset=&quot;/_astro/filter-example_5m9Hh.webp 640w, /_astro/filter-example_ZL1iK4.webp 750w, /_astro/filter-example_Z1xaewd.webp 820w&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot;&gt;Going Further&lt;/h2&gt;
&lt;p&gt;You can write custom filters and chain them to the built-in filters to invent new effects. Many of the math formulas for effects are available on the Internet. Most are easy to translate into the Metal Shader Language to make them perfect for your application. Writing an entire image or video editing application to showcase your filters is a much larger task. Using an SDK like the &lt;a href=&quot;https://img.ly/?utm_source=imgly&amp;#x26;utm_medium=blog&amp;#x26;utm_campaign=howtos&quot;&gt;IMG.LY&lt;/a&gt; &lt;code&gt;PhotoEditorSDK&lt;/code&gt; or &lt;code&gt;VideoEditorSDK&lt;/code&gt; can help you provide an app that has the features that users will expect in addition to your cool filters.&lt;/p&gt;
&lt;p&gt;To add your filter to the &lt;code&gt;PhotoEditorSDK&lt;/code&gt; you first wrap your &lt;code&gt;CIFilter&lt;/code&gt; with an &lt;code&gt;Effect&lt;/code&gt;. For a basic filter, you only need to override the &lt;code&gt;newEffectFilter&lt;/code&gt; variable.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;import PhotoEditorSDK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;class GrayscaleEffect: Effect {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  override var newEffectFilter: CIFilter? {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      GrayscaleFilter()&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then add the filter to the &lt;code&gt;Effect.all&lt;/code&gt; array when configuring the SDK, using something like&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Effect.all = [NoEffect(), GrayscaleEffect(identifier: &quot;grayscaleFilter&quot;, displayName: &quot;Best Gray&quot;)]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now your filter appears the same as the other 60+ filters that ship with the SDK.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 316px) 316px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;316&quot; height=&quot;640&quot; src=&quot;https://img.ly/_astro/create-filter-for-iOS-2_1EN1kE.webp&quot; srcset=&quot;/_astro/create-filter-for-iOS-2_1EN1kE.webp 316w&quot;&gt;&lt;/p&gt;
&lt;p&gt;It applies to live camera previews as well as still images.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;In this tutorial, you learned how to configure Xcode for custom Metal code. You also learned how to create a color filter kernel in Metal and wrap it in a &lt;code&gt;CIFilter&lt;/code&gt;. By combining your filters with Apple’s filters in pipelines, you can create unique effects for your next app. Further, using an SDK such as &lt;code&gt;PhotoEditorSDK&lt;/code&gt; or &lt;code&gt;VideoEditorSDK&lt;/code&gt; allows you to showcase your filters in a full-featured image or video editing application. The PhotoEditor SDK for iOS &lt;a href=&quot;https://img.ly/docs/pesdk/ios/introduction/overview/&quot;&gt;documentation&lt;/a&gt; will give you deeper look into all other image adjustments including, ofcourse, the image filtering we discussed above.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! We hope that you found this tutorial helpful. Feel free to reach out to us on &lt;a href=&quot;https://twitter.com/imgly&quot;&gt;Twitter&lt;/a&gt; with any questions, comments, or suggestions.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Walter</dc:creator><media:content url="https://blog.img.ly/2021/08/create-a-photo-filter-for-iOS.jpg" medium="image"/><category>App Development</category><category>iOS App Development</category><category>iOS</category><category>Photo Editing</category><category>Tutorial</category><category>Development</category><category>Mobile App Development</category><category>Photo Filter</category><category>How-To</category></item><item><title>Case Study: Vapiano &amp; PhotoEditor SDK</title><link>https://img.ly/blog/case-study-vapiano-photoeditor-sdk-7f335ecc6ac7/</link><guid isPermaLink="true">https://img.ly/blog/case-study-vapiano-photoeditor-sdk-7f335ecc6ac7/</guid><description>Restaurant experience gone App </description><pubDate>Thu, 24 Aug 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The German casual dining restaurant chain &lt;a href=&quot;https://vapiano.com/&quot;&gt;Vapiano&lt;/a&gt; facilitates and streamlines their customer’s visits with a feature-rich app that lets its users: check in at the restaurant, order beverages and desserts from their seat, pay after their visit, collect loyalty points, browse the menu, save favorite dishes, locate the nearest subsidiary and even create images for social media postings.&lt;/p&gt;
&lt;p&gt;With 180 restaurants in 31 countries, Vapiano is one of the biggest fast-casual dining chains around. Especially at lunchtime and early evening hours each restaurant wines and dines plenty of hungry customers, with Vapiano’s current approach (ordering at the counter, receiving the meal, finding a table and after that checking out on a regular register) that sometimes resulted in long lines and consequently frustration among their guests. With the Vapiano app, that is a thing of the past as the visit can almost solely be handled by the app from check-in to check-out. As Vapiano’s target audience is very photography and social media affine, their app holds another gem for their users to play around with. When taking a picture of themselves and their food in a Vapiano restaurant, the users can directly add Vapiano stickers to their pictures and then post them online. Said feature was realized by &lt;a href=&quot;https://mobilabsolutions.com/&quot;&gt;MobiLab Solutions&lt;/a&gt; utilizing the &lt;a href=&quot;https://img.ly/photo-sdk&quot;&gt;PhotoEditor SDK&lt;/a&gt;. We sat down with Max Afflerbach and Robert Rabe of MobiLab Solutions GmbH to talk about their experiences with PhotoEditor SDK.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 800px) 800px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;800&quot; height=&quot;686&quot; src=&quot;https://img.ly/_astro/1-lH2pCjVBFT4HH2MKY6yF3Q_1LBv8g.webp&quot; srcset=&quot;/_astro/1-lH2pCjVBFT4HH2MKY6yF3Q_Z1p42nx.webp 640w, /_astro/1-lH2pCjVBFT4HH2MKY6yF3Q_LL6J.webp 750w, /_astro/1-lH2pCjVBFT4HH2MKY6yF3Q_1LBv8g.webp 800w&quot;&gt;&lt;/p&gt;
&lt;p&gt;“Vapiano’s vision regarding the photo feature is to spread their brand, enhance brand recognition and broaden the brand’s visibility,” Max Afflerbach, Chief Customer Officer at MobiLab Solutions explains. “Basically, they wanted to provide their guests with a tool that facilitates what they’re already doing when dining at Vapiano, that is taking pictures of their food and then uploading them to Snapchat, Instagram, Facebook, you name it. And so, we came up with a built-in branding feature that uses custom stickers.”&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“…on StackOverflow or Quora, &lt;a href=&quot;https://img.ly/photo-sdk&quot;&gt;PhotoEditor SDK&lt;/a&gt; is listed and praised as the only real alternative to Aviary”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As they were on a schedule and didn’t have the time to develop the feature themselves the folks at MobiLab started searching for a third-party solution. “We’ve been looking for an SDK that does exactly what we imagined the feature to be capable of, namely taking a picture, adding assets and then exporting it,” says Robert Rabe, iOS developer at MobiLab Solutions. “We first considered Adobe Creative SDK because it is the best-known SDK in that field. However, it lacked a crucial feature for us that PhotoEditor SDK luckily has and that is the possibility to upload custom stickers,” says Max Afflerbach. “Also,” Rabe adds, “we weren’t 100% sure whether this whole thing would work without the user having to log into the Adobe Cloud.” “When you’re searching for other image processing SDKs on StackOverflow or Quora, PhotoEditor SDK is listed and praised as the only real alternative to Aviary. And besides, there aren’t exactly a lot of SDKs out there that cover both iOS and Android. So, that wasn’t a tough decision at all,” Afflerbach explains.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; sizes=&quot;(min-width: 800px) 800px, 100vw&quot; data-astro-image=&quot;constrained&quot; data-astro-image-pos=&quot;center&quot; width=&quot;800&quot; height=&quot;686&quot; src=&quot;https://img.ly/_astro/1-ReYFlxaZFXGtSClTwKYlRw_Z2bRyNU.webp&quot; srcset=&quot;/_astro/1-ReYFlxaZFXGtSClTwKYlRw_ZilXvM.webp 640w, /_astro/1-ReYFlxaZFXGtSClTwKYlRw_17tOXu.webp 750w, /_astro/1-ReYFlxaZFXGtSClTwKYlRw_Z2bRyNU.webp 800w&quot;&gt;&lt;/p&gt;
&lt;p&gt;From the initial decision to work with PhotoEditor SDK until the feature was finished and ready for rollout, only a short period passed as Rabe explains: “Working with the SDK is really straightforward. The implementation is fast, and it’s all well documented. The SDK is a powerful tool and holds a lot of features with a great many possibilities. Setting up the feature exactly as we wanted it to be didn’t take longer than three working days tops. And if we had any issues or questions, we had no problems whatsoever getting in touch with the folks at PhotoEditor SDK which is really nice.”&lt;/p&gt;
&lt;p&gt;“I really like the product, it’s working very well, and especially the communication with the PhotoEditor SDK team was highly pleasant,” Robert Rabe concludes. “Working together with the team of &lt;a href=&quot;https://img.ly/photo-sdk&quot;&gt;PhotoEditor SDK&lt;/a&gt; was super easygoing, the few requests and inquiries we had were answered quickly, and the technical licensing was a cinch as well. We are delighted with the product, the integration was very simple, and our developers are happy, I guess you couldn’t ask for more from an agency’s perspective,” Max Afflerbach adds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Thanks for reading! To stay in the loop, subscribe to our &lt;a href=&quot;https://photoeditorsdk.us13.list-manage.com/subscribe?u=dc9f652839dbb620d14d6d28d&amp;#x26;id=04a306e4b2&quot;&gt;Newsletter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;</content:encoded><dc:creator>Felix</dc:creator><media:content url="https://blog.img.ly/2020/04/image-41.png" medium="image"/><category>Android App Development</category><category>iOS App Development</category><category>Social Media</category><category>Mobile Photography</category><category>Case Study</category><category>Case Studies</category></item></channel></rss>