1 // Copyright 2022-present 650 Industries. All rights reserved.
2 
3 import ExpoModulesCore
4 import SDWebImage
5 import SDWebImageWebPCoder
6 import SDWebImageAVIFCoder
7 import SDWebImageSVGCoder
8 
9 public final class ImageModule: Module {
10   lazy var prefetcher = SDWebImagePrefetcher.shared
11 
12   public func definition() -> ModuleDefinition {
13     Name("ExpoImage")
14 
15     OnCreate {
16       ImageModule.registerCoders()
17       ImageModule.registerLoaders()
18     }
19 
20     View(ImageView.self) {
21       Events(
22         "onLoadStart",
23         "onProgress",
24         "onError",
25         "onLoad"
26       )
27 
28       Prop("source") { (view, sources: [ImageSource]?) in
29         view.sources = sources
30       }
31 
32       Prop("placeholder") { (view, placeholders: [ImageSource]?) in
33         view.placeholderSources = placeholders ?? []
34       }
35 
36       Prop("contentFit") { (view, contentFit: ContentFit?) in
37         view.contentFit = contentFit ?? .cover
38       }
39 
40       Prop("contentPosition") { (view, contentPosition: ContentPosition?) in
41         view.contentPosition = contentPosition ?? .center
42       }
43 
44       Prop("transition") { (view, transition: ImageTransition?) in
45         view.transition = transition
46       }
47 
48       Prop("blurRadius") { (view, blurRadius: Double?) in
49         view.blurRadius = blurRadius ?? .zero
50       }
51 
52       Prop("tintColor") { (view, tintColor: UIColor?) in
53         view.imageTintColor = tintColor ?? .clear
54       }
55 
56       Prop("priority") { (view, priority: ImagePriority?) in
57         view.loadingOptions.remove([.lowPriority, .highPriority])
58 
59         if let priority = priority?.toSDWebImageOptions() {
60           view.loadingOptions.insert(priority)
61         }
62       }
63 
64       Prop("cachePolicy") { (view, cachePolicy: ImageCachePolicy?) in
65         view.cachePolicy = cachePolicy ?? .disk
66       }
67 
68       Prop("enableLiveTextInteraction") { (view, enableLiveTextInteraction: Bool?) in
69         view.enableLiveTextInteraction = enableLiveTextInteraction ?? false
70       }
71 
72       Prop("accessible") { (view, accessible: Bool?) in
73         view.sdImageView.isAccessibilityElement = accessible ?? false
74       }
75 
76       Prop("accessibilityLabel") { (view, label: String?) in
77         view.sdImageView.accessibilityLabel = label
78       }
79 
80       OnViewDidUpdateProps { view in
81         view.reload()
82       }
83     }
84 
85     Function("prefetch") { (urls: [URL]) in
86       SDWebImagePrefetcher.shared.prefetchURLs(urls)
87     }
88 
89     AsyncFunction("clearMemoryCache") { () -> Bool in
90       SDImageCache.shared.clearMemory()
91       return true
92     }
93 
94     AsyncFunction("clearDiskCache") { (promise: Promise) in
95       SDImageCache.shared.clearDisk {
96         promise.resolve(true)
97       }
98     }
99   }
100 
101   static func registerCoders() {
102     if #available(iOS 14.0, *) {
103       // By default Animated WebP is not supported
104       SDImageCodersManager.shared.addCoder(SDImageAWebPCoder.shared)
105     } else {
106       // This coder is much slower, but it's the only one that works in iOS 13
107       SDImageCodersManager.shared.addCoder(SDImageWebPCoder.shared)
108     }
109     SDImageCodersManager.shared.addCoder(SDImageAVIFCoder.shared)
110     SDImageCodersManager.shared.addCoder(SDImageSVGCoder.shared)
111     SDImageCodersManager.shared.addCoder(SDImageHEICCoder.shared)
112   }
113 
114   static func registerLoaders() {
115     SDImageLoadersManager.shared.addLoader(BlurhashLoader())
116     SDImageLoadersManager.shared.addLoader(PhotoLibraryAssetLoader())
117   }
118 }
119