Slide 1

Slide 1 text

High-performance GIF playback iOSDC25 day1 Track D noppe 1

Slide 2

Slide 2 text

Who am I noppe ! ‣ iOSDC Speaker (8 years) ‣ Senior iOS Developer at DeNA ‣ Indie App Developer 2

Slide 3

Slide 3 text

DAWN for Mastodon My indie app for Mastodon ‣ Normal iOS design ‣ Animation images (GIF, APNG, WebP) ‣ High-performance scrolling ‣ Multi-instance support 3

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

Decentralized social network ‣ Open-source platform (2016) ‣ Anyone can run a server ‣ Servers form federated network ‣ Compatible with Threads, Misskey 5

Slide 6

Slide 6 text

6 https://en.wikipedia.org/wiki/Mastodon%28socialnetwork%29

Slide 7

Slide 7 text

Custom Emojis User-uploaded animated emojis ‣ Like Slack custom emojis ‣ Each server has unique collection ‣ GIF, APNG, WebP support ‣ Used in posts and reactions 7

Slide 8

Slide 8 text

The Challenge Timeline filled with animated emojis ‣ Dozens of GIFs simultaneously ‣ Mixed formats & frame rates ‣ Performance nightmare ! 8 Beware of flashing lights / ఺໓ʹ͓ؾΛ͚͍ͭͩ͘͞

Slide 9

Slide 9 text

9

Slide 10

Slide 10 text

DAWN's Solution ✅ Smooth scrolling with dozens of animated emojis ✅ Memory efficient rendering ✅ Support for GIF, APNG, WebP ✅ Responsive UI under heavy load 10

Slide 11

Slide 11 text

Agenda 1. How to show GIF animation in UIKit. 2. How to improve performance. 11

Slide 12

Slide 12 text

How to show GIF animation in UIKit. 12

Slide 13

Slide 13 text

// try to show GIF image. let image = UIImage(named: "sample.gif") let imageView = UIImageView(image: image) view.addSubview(imageView) 13

Slide 14

Slide 14 text

Not Working! UIKit doesn't support animated images 14

Slide 15

Slide 15 text

15 https://www.nyan.cat

Slide 16

Slide 16 text

// Extract Frame Images // GIF, APNG, WEBP file let fileURL = ... let source = CGImageSourceCreateWithURL( fileURL as CFURL, nil ) let count = CGImageSourceGetCount(gifSource) var images: [UIImage] = [] for index in 0..

Slide 17

Slide 17 text

// Playback animation images let imageView = UIImageView() imageView.animationImages = images imageView.startAnimating() 17

Slide 18

Slide 18 text

18

Slide 19

Slide 19 text

Memory Usage Problem 340KB GIF = 25MB RAM! ‣ Same as 8K JPEG memory usage ‣ Exponential memory growth ‣ App crashes with multiple GIFs 19

Slide 20

Slide 20 text

Memory Calculation Formula for memory usage: 480×400×4×34 = 25,958,400Byte ≈ 25MB ‣ W: Width in pixels (480) ‣ H: Height in pixels (400) ‣ C: Color (4 for ARGB) ‣ N: Number of frames (34) 20 8bit, ARGB image case

Slide 21

Slide 21 text

Performance Tuning 21

Slide 22

Slide 22 text

My Approach Performance optimization methodology 22

Slide 23

Slide 23 text

1. Identify User Pain Start with user experience ‣ What issues are users facing? ‣ Where do they struggle? ‣ What frustrates them? 23

Slide 24

Slide 24 text

2. Define Core Value What's your app's mission? ‣ What makes it irreplaceable? ‣ What would users miss most? 24

Slide 25

Slide 25 text

3. Measure Everything Quantify with profiling tools ‣ Use Instruments, Xcode ‣ Memory, CPU, frame rate ‣ Identify bottlenecks ‣ Evaluate improvements 25

Slide 26

Slide 26 text

Tips: Instruments Profile specific tests Right-click test → "Profile" 26

Slide 27

Slide 27 text

4. Smart Trade-offs A puzzle of priorities ‣ Lower quality of unimportant things ‣ Raise quality of what matters 27

Slide 28

Slide 28 text

28

Slide 29

Slide 29 text

29

Slide 30

Slide 30 text

AnimatedImage github.com/noppefoxwolf/AnimatedImage ‣ Specialized UIKit component for high-performance GIF playback ‣ Supports GIF, APNG, WebP formats ‣ Memory-efficient frame caching ‣ Background processing pipeline 30

Slide 31

Slide 31 text

31

Slide 32

Slide 32 text

View Request Image at timestamp Image? ImageProvider UpdateLink setNeedsDisplay 32

Slide 33

Slide 33 text

Synchronization 33

Slide 34

Slide 34 text

UIUpdateLink ! ‣ iOS17+ ‣ An object you use to observe, participate in, and affect the UI update process. // Update y every frame. let updateLink = UIUpdateLink(view: view) updateLink.addAction { link, info in // Code that runs each UI update, after processing input events, // but before `CADisplayLink` callbacks. self.view.center.y = sin(info.modelTime) * 100 + self.view.bounds.midY } 34

Slide 35

Slide 35 text

ImageProvider Store frames ImageProcessor Cache 35 https://developer.apple.com/documentation/Foundation/NSCache

Slide 36

Slide 36 text

36

Slide 37

Slide 37 text

Resizing 37

Slide 38

Slide 38 text

38

Slide 39

Slide 39 text

Drop Frames 39

Slide 40

Slide 40 text

40

Slide 41

Slide 41 text

41

Slide 42

Slide 42 text

42

Slide 43

Slide 43 text

Rendering 43

Slide 44

Slide 44 text

Is Rendering Necessary? 44

Slide 45

Slide 45 text

Lazy Decompression Issue DGifDecompress runs on main thread Blocks smooth scrolling! 45

Slide 46

Slide 46 text

Solution: Pre-decompress Decompress on background thread // UIKit let decompressedImage = await uiImage.byPreparingForDisplay() // CoreGraphics let context = CGContext(...)! context.draw(image, in: rect) let decodedImage = context.makeImage() 46

Slide 47

Slide 47 text

47

Slide 48

Slide 48 text

48

Slide 49

Slide 49 text

Next steps ‣ Explore AnimatedImage features ‣ Try AnimatedImage in your projects ‣ Deep dive into WebP ࡞ֶͬͯͿWebPೖ໳ day1 13:00 Track A 49