react-native-attachment-kit 1.0.2 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +356 -213
  3. package/android/build.gradle +3 -0
  4. package/android/src/main/libs/rnattachmentkit-release.aar +0 -0
  5. package/android/src/newarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +308 -10
  6. package/android/src/oldarch/java/com/rnattachmentkit/RNAttachmentKitModule.kt +311 -9
  7. package/ios/RNAttachmentKit.m +44 -0
  8. package/ios/RNAttachmentKit.swift +307 -23
  9. package/ios/RNAttachmentKit.xcframework/Info.plist +5 -5
  10. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +30 -0
  11. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.abi.json +20349 -13021
  12. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.private.swiftinterface +118 -1
  13. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  14. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios.swiftinterface +118 -1
  15. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/RNAttachmentKit +0 -0
  16. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  17. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  18. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  19. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  20. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  21. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  22. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  23. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  24. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  25. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  26. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  27. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
  28. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  29. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  30. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  31. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  32. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  33. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
  34. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  35. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
  36. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  37. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  38. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  39. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  40. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  41. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  42. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  43. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  44. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  45. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  46. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  47. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  48. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  49. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
  50. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
  51. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
  52. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
  53. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeResources +192 -60
  54. package/ios/RNAttachmentKit.xcframework/ios-arm64/RNAttachmentKit.framework/_CodeSignature/CodeSignature +0 -0
  55. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Headers/RNAttachmentKit-Swift.h +60 -0
  56. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.abi.json +20349 -13021
  57. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.private.swiftinterface +118 -1
  58. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  59. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +118 -1
  60. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +20349 -13021
  61. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.private.swiftinterface +118 -1
  62. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  63. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/Modules/RNAttachmentKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +118 -1
  64. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/RNAttachmentKit +0 -0
  65. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  66. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  67. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  68. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  69. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  70. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  71. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  72. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  73. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  74. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  75. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  76. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_back~light.webp +0 -0
  77. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  78. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  79. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  80. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  81. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  82. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_check~light.webp +0 -0
  83. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  84. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_close~light.webp +0 -0
  85. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  86. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  87. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  88. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  89. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  90. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  91. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  92. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  93. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  94. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  95. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  96. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  97. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  98. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_editor_text~light.webp +0 -0
  99. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~dark.webp +0 -0
  100. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/ToolbarIcons/ic_text_add~light.webp +0 -0
  101. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeDirectory +0 -0
  102. package/ios/RNAttachmentKit.xcframework/ios-arm64_x86_64-simulator/RNAttachmentKit.framework/_CodeSignature/CodeResources +202 -70
  103. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~dark.webp +0 -0
  104. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_reflect~light.webp +0 -0
  105. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~dark.webp +0 -0
  106. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_crop_rotate~light.webp +0 -0
  107. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~dark.webp +0 -0
  108. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_delete~light.webp +0 -0
  109. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~dark.webp +0 -0
  110. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_forward~light.webp +0 -0
  111. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~dark.webp +0 -0
  112. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_drawing_reward~light.webp +0 -0
  113. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~dark.webp +0 -0
  114. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_back~light.webp +0 -0
  115. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~dark.webp +0 -0
  116. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_blur~light.webp +0 -0
  117. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~dark.webp +0 -0
  118. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check_small~light.webp +0 -0
  119. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~dark.webp +0 -0
  120. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_check~light.webp +0 -0
  121. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~dark.webp +0 -0
  122. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_close~light.webp +0 -0
  123. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~dark.webp +0 -0
  124. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_crop~light.webp +0 -0
  125. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~dark.webp +0 -0
  126. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_drawing~light.webp +0 -0
  127. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~dark.webp +0 -0
  128. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_effect~light.webp +0 -0
  129. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~dark.webp +0 -0
  130. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_emoji~light.webp +0 -0
  131. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~dark.webp +0 -0
  132. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_eraser~light.webp +0 -0
  133. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~dark.webp +0 -0
  134. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_mosaic~light.webp +0 -0
  135. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~dark.webp +0 -0
  136. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_editor_text~light.webp +0 -0
  137. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~dark.webp +0 -0
  138. package/ios/RNAttachmentKitAssets/ToolbarIcons/ic_text_add~light.webp +0 -0
  139. package/package.json +1 -1
  140. package/src/NativeAttachmentKit.ts +10 -0
  141. package/src/index.d.ts +5 -56
  142. package/src/index.ts +188 -35
  143. package/src/types.ts +207 -11
@@ -8,6 +8,7 @@
8
8
  import UIKit
9
9
  import Photos
10
10
  import ImageIO
11
+ import CoreLocation
11
12
  import RNAttachmentKit
12
13
 
13
14
  #if RCT_NEW_ARCH_ENABLED
@@ -64,7 +65,8 @@ class RNAttachmentKit: RCTEventEmitter {
64
65
  @objc
65
66
  override func supportedEvents() -> [String]! {
66
67
  return ["onSelectionChange", "onImageProcessed", "onPageSelected", "onViewerWillClose", "onImageDeleted",
67
- "onGalleryItemDeleted", "onGalleryNeedPage", "onGalleryClosed", "onGalleryGridSizeChanged"]
68
+ "onGalleryItemDeleted", "onGalleryNeedPage", "onGalleryClosed", "onGalleryGridSizeChanged",
69
+ "onLocationSelected", "onFilesSelected"]
68
70
  }
69
71
 
70
72
  // MARK: - Public Methods
@@ -171,6 +173,213 @@ class RNAttachmentKit: RCTEventEmitter {
171
173
  }
172
174
  }
173
175
 
176
+ // MARK: - Map Viewer (위치 메시지 → 지도)
177
+
178
+ private weak var activeMapViewerVC: MapViewerViewController?
179
+ /// updateMapViewerSourceRect 로 갱신되는 닫힘 위치 (window points)
180
+ private var mapViewerSourceRect: CGRect?
181
+
182
+ private static func cgFloat(_ v: Any?) -> CGFloat? {
183
+ if let n = v as? NSNumber { return CGFloat(truncating: n) }
184
+ if let d = v as? Double { return CGFloat(d) }
185
+ return nil
186
+ }
187
+
188
+ private static func parseRect(_ v: Any?) -> CGRect? {
189
+ guard let d = v as? NSDictionary,
190
+ let x = cgFloat(d["x"]), let y = cgFloat(d["y"]),
191
+ let w = cgFloat(d["width"]), let h = cgFloat(d["height"]), w > 0, h > 0 else { return nil }
192
+ return CGRect(x: x, y: y, width: w, height: h)
193
+ }
194
+
195
+ /// 말풍선 이미지(정적 지도 스냅샷) 로드. 메모리/디스크 캐시 → file:// → 다운로드 순.
196
+ /// 실패하거나 너무 오래 걸리면 nil (이미지 없이 지도만 엶)
197
+ /// completion(이미지, 마커 뺀 이미지인지). renderLocationThumbnail 결과면 마커 뺀 짝을 씀 → 뷰어가 마커를 따로 그려
198
+ /// 말풍선 속 크기 ↔ 지도 마커 크기로 자연스럽게 바꿈
199
+ private func loadMapSourceImage(_ uri: String?, completion: @escaping (UIImage?, Bool) -> Void) {
200
+ guard let uri = uri, !uri.isEmpty else { completion(nil, false); return }
201
+ if uri.hasPrefix("file://") || uri.hasPrefix("/") {
202
+ let path = uri.hasPrefix("file://") ? (URL(string: uri)?.path ?? String(uri.dropFirst(7))) : uri
203
+ DispatchQueue.global(qos: .userInitiated).async {
204
+ if let base = LocationThumbnailRenderer.markerlessFile(for: path), let img = UIImage(contentsOfFile: base) {
205
+ DispatchQueue.main.async { completion(img, true) }
206
+ return
207
+ }
208
+ let image = UIImage(contentsOfFile: path)
209
+ DispatchQueue.main.async { completion(image, false) }
210
+ }
211
+ return
212
+ }
213
+ if let cached = ViewerImageCache.shared.getMemoryImage(for: uri) { completion(cached, false); return }
214
+ var done = false
215
+ let finish: (UIImage?) -> Void = { image in
216
+ DispatchQueue.main.async {
217
+ guard !done else { return }
218
+ done = true
219
+ completion(image, false)
220
+ }
221
+ }
222
+ // 다운로드가 늦으면 이미지 없이 바로 엶 (탭 반응이 늦어지지 않게)
223
+ DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { finish(nil) }
224
+ DispatchQueue.global(qos: .userInitiated).async {
225
+ if let disk = ViewerImageCache.shared.getDiskImageSynchronously(for: uri) { finish(disk); return }
226
+ RemoteImageViewerViewController.prefetchImage(from: uri) { finish($0) }
227
+ }
228
+ }
229
+
230
+ @objc
231
+ func openMapViewer(_ options: NSDictionary,
232
+ resolve: @escaping RCTPromiseResolveBlock,
233
+ reject: @escaping RCTPromiseRejectBlock) -> Void {
234
+ guard let lat = RNAttachmentKit.cgFloat(options["latitude"]),
235
+ let lng = RNAttachmentKit.cgFloat(options["longitude"]) else {
236
+ reject("ERROR", "latitude and longitude are required", nil)
237
+ return
238
+ }
239
+ let sourceRect = RNAttachmentKit.parseRect(options["sourceRect"])
240
+ let imageUri = options["sourceImageUri"] as? String
241
+ DispatchQueue.main.async {
242
+ // 위치 정보만으로 여는 경우엔 이미지 로드를 기다리지 않음
243
+ let load: (@escaping (UIImage?, Bool) -> Void) -> Void = { done in
244
+ if sourceRect == nil { done(nil, false) } else { self.loadMapSourceImage(imageUri, completion: done) }
245
+ }
246
+ load { image, markerless in
247
+ guard let root = self.getRootViewController() else {
248
+ reject("ERROR", "Unable to get root view controller", nil)
249
+ return
250
+ }
251
+ var accent = UIColor(red: 1, green: 0.42, blue: 0.21, alpha: 1)
252
+ if let hex = options["themeColor"] as? String, let c = UIColor(hexString: hex) { accent = c }
253
+ let vc = MapViewerViewController(
254
+ latitude: Double(lat), longitude: Double(lng),
255
+ title: options["title"] as? String,
256
+ address: options["address"] as? String,
257
+ markerType: options["markerType"] as? String,
258
+ placeCategory: options["placeCategory"] as? String,
259
+ accentColor: accent,
260
+ languageCode: options["languageCode"] as? String ?? "en")
261
+ vc.overrideUserInterfaceStyle = RNAttachmentKit.interfaceStyle(options)
262
+ vc.showsDirections = (options["showDirections"] as? Bool) ?? true
263
+ vc.addressLanguageCode = (options["addressLanguageCode"] as? String).flatMap { $0.isEmpty ? nil : $0 }
264
+ if let span = RNAttachmentKit.cgFloat(options["spanMeters"]), span > 0 { vc.spanMeters = Double(span) }
265
+ if let r = RNAttachmentKit.cgFloat(options["sourceCornerRadius"]) { vc.sourceCornerRadius = r }
266
+ self.mapViewerSourceRect = sourceRect
267
+ if let rect = sourceRect {
268
+ vc.sourceRect = rect
269
+ vc.sourceImage = image
270
+ vc.sourceImageIsMarkerless = markerless && image != nil
271
+ vc.sourceRectProvider = { [weak self] in self?.mapViewerSourceRect }
272
+ }
273
+ vc.onClosed = { [weak self] in
274
+ self?.mapViewerSourceRect = nil
275
+ resolve(nil)
276
+ }
277
+ self.activeMapViewerVC = vc
278
+ root.present(vc, animated: sourceRect == nil)
279
+ }
280
+ }
281
+ }
282
+
283
+ @objc
284
+ func updateMapViewerSourceRect(_ rect: NSDictionary,
285
+ resolve: @escaping RCTPromiseResolveBlock,
286
+ reject: @escaping RCTPromiseRejectBlock) -> Void {
287
+ let parsed = RNAttachmentKit.parseRect(rect)
288
+ DispatchQueue.main.async {
289
+ if self.activeMapViewerVC != nil { self.mapViewerSourceRect = parsed }
290
+ resolve(nil)
291
+ }
292
+ }
293
+
294
+ /// 위치 메시지 말풍선 지도 썸네일을 이 기기에서 그려 캐시 (보낸 이미지 없이 위치 정보만으로)
295
+ @objc
296
+ func renderLocationThumbnail(_ options: NSDictionary,
297
+ resolve: @escaping RCTPromiseResolveBlock,
298
+ reject: @escaping RCTPromiseRejectBlock) -> Void {
299
+ guard let lat = RNAttachmentKit.cgFloat(options["latitude"]),
300
+ let lng = RNAttachmentKit.cgFloat(options["longitude"]),
301
+ let w = RNAttachmentKit.cgFloat(options["width"]), w > 0 else {
302
+ reject("E_INVALID_ARGS", "latitude, longitude and width are required", nil)
303
+ return
304
+ }
305
+ DispatchQueue.main.async {
306
+ // 비율(aspect)이 있으면 세로는 가로에서 계산, 없으면 height 그대로
307
+ guard let h = LocationThumbnailRenderer.height(forWidth: w, aspect: options["aspect"] as? String)
308
+ ?? RNAttachmentKit.cgFloat(options["height"]), h > 0 else {
309
+ reject("E_INVALID_ARGS", "height (or aspect) is required", nil)
310
+ return
311
+ }
312
+ var accent = UIColor(red: 1, green: 0.42, blue: 0.21, alpha: 1)
313
+ if let hex = options["themeColor"] as? String, let c = UIColor(hexString: hex) { accent = c }
314
+ var style = RNAttachmentKit.interfaceStyle(options)
315
+ if style == .unspecified {
316
+ // "system": 지금 앱 화면 모드
317
+ style = UIApplication.shared.connectedScenes.compactMap { ($0 as? UIWindowScene)?.windows.first(where: \.isKeyWindow) }
318
+ .first?.traitCollection.userInterfaceStyle ?? UITraitCollection.current.userInterfaceStyle
319
+ }
320
+ var me: CLLocationCoordinate2D?
321
+ if let mlat = RNAttachmentKit.cgFloat(options["myLatitude"]), let mlng = RNAttachmentKit.cgFloat(options["myLongitude"]) {
322
+ me = CLLocationCoordinate2D(latitude: Double(mlat), longitude: Double(mlng))
323
+ }
324
+ let span = RNAttachmentKit.cgFloat(options["spanMeters"]).map { Double($0) }
325
+ var req = LocationThumbnailRequest(
326
+ latitude: Double(lat), longitude: Double(lng),
327
+ size: CGSize(width: w, height: h), scale: min(UIScreen.main.scale, 3),
328
+ isDark: style == .dark,
329
+ markerType: options["markerType"] as? String,
330
+ placeCategory: options["placeCategory"] as? String,
331
+ spanMeters: (span ?? 0) > 0 ? span : nil,
332
+ accentColor: accent,
333
+ myLocation: me)
334
+ // 결과 이미지 최대 크기 (px). 둘 다 없으면 512 × 512, 하나만 주면 다른 쪽은 제한 없음
335
+ // (예: maxWidth 512 만 주고 세로가 기본 512 에 잘려 작아지는 일이 없게)
336
+ let maxW = RNAttachmentKit.cgFloat(options["maxWidth"]).flatMap { $0 > 0 ? $0 : nil }
337
+ let maxH = RNAttachmentKit.cgFloat(options["maxHeight"]).flatMap { $0 > 0 ? $0 : nil }
338
+ if maxW != nil || maxH != nil {
339
+ req.maxPixelWidth = maxW ?? .greatestFiniteMagnitude
340
+ req.maxPixelHeight = maxH ?? .greatestFiniteMagnitude
341
+ }
342
+ LocationThumbnailRenderer.render(req) { result in
343
+ switch result {
344
+ case .success(let dict): resolve(dict)
345
+ case .failure(let error): reject("E_THUMBNAIL_FAILED", error.localizedDescription, error)
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ // MARK: - Reverse geocode (기기 지오코더, 디스크 캐시)
352
+
353
+ @objc
354
+ func reverseGeocode(_ options: NSDictionary,
355
+ resolve: @escaping RCTPromiseResolveBlock,
356
+ reject: @escaping RCTPromiseRejectBlock) -> Void {
357
+ let lat = (options["latitude"] as? NSNumber)?.doubleValue ?? .nan
358
+ let lng = (options["longitude"] as? NSNumber)?.doubleValue ?? .nan
359
+ let lang = (options["languageCode"] as? String).flatMap { $0.isEmpty ? nil : $0 } ?? "en"
360
+ ReverseGeocoder.shared.reverseGeocode(latitude: lat, longitude: lng, languageCode: lang) { result in
361
+ switch result {
362
+ case .success(let r): resolve(r.dictionary)
363
+ case .failure(let e): reject(e.code, e.message, nil)
364
+ }
365
+ }
366
+ }
367
+
368
+ @objc
369
+ func clearReverseGeocodeCache(_ resolve: @escaping RCTPromiseResolveBlock,
370
+ reject: @escaping RCTPromiseRejectBlock) -> Void {
371
+ ReverseGeocoder.shared.clearCache { resolve(nil) }
372
+ }
373
+
374
+ @objc
375
+ func clearLocationThumbnailCache(_ resolve: @escaping RCTPromiseResolveBlock,
376
+ reject: @escaping RCTPromiseRejectBlock) -> Void {
377
+ DispatchQueue.global(qos: .utility).async {
378
+ LocationThumbnailRenderer.clearCache()
379
+ resolve(nil)
380
+ }
381
+ }
382
+
174
383
  @objc
175
384
  func openViewer(_ options: NSDictionary,
176
385
  resolve: @escaping RCTPromiseResolveBlock,
@@ -560,30 +769,42 @@ class RNAttachmentKit: RCTEventEmitter {
560
769
  resolve: @escaping RCTPromiseResolveBlock,
561
770
  reject: @escaping RCTPromiseRejectBlock) -> Void {
562
771
  let startTime = CFAbsoluteTimeGetCurrent()
772
+ #if DEBUG
563
773
  print("✅ [DEBUG] received options: \(options)")
564
774
  print("🔵 [DEBUG] openGallery 시작 - 시간: \(startTime)")
565
-
775
+ #endif
776
+
777
+ // 예전에 파일/위치 탭에서 임시 폴더에 복사해 두고 24시간이 지난 것 정리 (백그라운드, UI 와 무관)
778
+ DispatchQueue.global(qos: .utility).async {
779
+ TempFileCleaner.cleanupStaleTempFiles()
780
+ }
781
+
566
782
  // Main thread에서 실행
567
783
  DispatchQueue.main.async { [weak self] () -> Void in
568
784
  guard let self = self else {
569
785
  reject("ERROR", "Module deallocated", nil)
570
786
  return
571
787
  }
572
-
788
+
789
+ #if DEBUG
573
790
  print("🔵 [DEBUG] workItem 실행 시작 - 경과: \(String(format: "%.3f", CFAbsoluteTimeGetCurrent() - startTime))초")
574
-
791
+ #endif
792
+
575
793
  let step1 = CFAbsoluteTimeGetCurrent()
576
794
  guard let rootViewController = self.getRootViewController() else {
577
795
  print("❌ [DEBUG] getRootViewController 실패")
578
796
  reject("ERROR", "Unable to get root view controller", nil)
579
797
  return
580
798
  }
799
+ #if DEBUG
581
800
  print("🔵 [DEBUG] getRootViewController 완료 - 경과: \(String(format: "%.3f", CFAbsoluteTimeGetCurrent() - step1))초")
582
-
801
+ #endif
802
+
583
803
  let step2 = CFAbsoluteTimeGetCurrent()
584
- print("🔵 [DEBUG] GalleryViewController 생성 시작...")
585
804
  let galleryVC = GalleryViewController()
805
+ #if DEBUG
586
806
  print("🔵 [DEBUG] GalleryViewController 생성 완료 - 경과: \(String(format: "%.3f", CFAbsoluteTimeGetCurrent() - step2))초")
807
+ #endif
587
808
 
588
809
  // 현재 갤러리 참조 저장
589
810
  self.currentGalleryVC = galleryVC
@@ -679,6 +900,8 @@ class RNAttachmentKit: RCTEventEmitter {
679
900
  editorVC.onCancel = { [weak currentGallery] in
680
901
  currentGallery?.selectedAssets.removeAll()
681
902
  currentGallery?.refreshSelectionAfterEdit()
903
+ // 결과 전달 책임이 갤러리로 돌아옴 → 닫기가 다시 취소(reject)를 보낼 수 있게
904
+ currentGallery?.hasReturnedImages = false
682
905
  }
683
906
 
684
907
  var savedDetent: UISheetPresentationController.Detent.Identifier? = nil
@@ -714,7 +937,35 @@ class RNAttachmentKit: RCTEventEmitter {
714
937
  }
715
938
  }
716
939
 
717
- galleryVC.onImagesSelected = { [weak self] selectedImages in
940
+ // 첨부 메뉴(갤러리 / 위치 / 파일). 기본 false — 켜지 않으면 기존과 같음
941
+ galleryVC.showsAttachMenu = options["showAttachMenu"] as? Bool ?? false
942
+ // 위치 보내기 때 지도 캡처 이미지 (기본 true). false 면 위치 정보만 — 받는 쪽이 renderLocationThumbnail 로 그림
943
+ galleryVC.capturesLocationImage = options["captureLocationImage"] as? Bool ?? true
944
+ // 위치 탭에서 보내기 → onLocationSelected 이벤트로 결과를 넘기고, openGallery 는 빈 배열로 끝냄
945
+ galleryVC.onLocationSelected = { [weak self, weak galleryVC] location in
946
+ self?.currentGalleryVC = nil
947
+ self?.sendEvent(withName: "onLocationSelected", body: location.dictionary)
948
+ resolve([])
949
+ galleryVC?.releaseAfterReturn()
950
+ }
951
+ // 파일 탭에서 보내기 → onFilesSelected 이벤트 { files: [{uri, path, name, size, mimeType}] }, openGallery 는 빈 배열
952
+ // 일부 파일만 복사에 실패하면 (추가 필드) failed: [{name, error}] 를 함께 보냄 (files 는 그대로)
953
+ galleryVC.onFilesSelectedWithFailures = { [weak self, weak galleryVC] files, failures in
954
+ self?.currentGalleryVC = nil
955
+ var body: [String: Any] = ["files": files.map { $0.dictionary }]
956
+ if !failures.isEmpty { body["failed"] = failures.map { $0.dictionary } }
957
+ self?.sendEvent(withName: "onFilesSelected", body: body)
958
+ resolve([])
959
+ galleryVC?.releaseAfterReturn()
960
+ }
961
+
962
+ // ⚠️ 클로저 안에서 galleryVC 를 강하게 잡으면 galleryVC → onImagesSelected → galleryVC 순환 참조가 되어
963
+ // (성공 경로는 viewDidDisappear 가 정리를 deinit 으로 미루므로) 갤러리가 영영 해제되지 않았습니다.
964
+ // 필요한 값은 미리 상수로 꺼내고, galleryVC 는 weak 로만 씁니다.
965
+ let galleryMaxWidth = galleryVC.maxWidth
966
+ let galleryMaxHeight = galleryVC.maxHeight
967
+ let galleryFormat = galleryVC.outputFormat // "webp" | "jpg" | "png"
968
+ galleryVC.onImagesSelected = { [weak self, weak galleryVC] selectedImages in
718
969
  guard let self = self else { resolve([]); return }
719
970
 
720
971
  guard !selectedImages.isEmpty else {
@@ -725,16 +976,22 @@ class RNAttachmentKit: RCTEventEmitter {
725
976
 
726
977
  var results: [[String: Any]] = []
727
978
  let tempDir = self.prepareCacheDirectory()
728
- let maxWidth = galleryVC.maxWidth
729
- let maxHeight = galleryVC.maxHeight
730
- let format = galleryVC.outputFormat // "webp" | "jpg" | "png"
979
+ let maxWidth = galleryMaxWidth
980
+ let maxHeight = galleryMaxHeight
981
+ let format = galleryFormat
731
982
  let mimeType = self.mimeType(for: format)
732
983
  let needResize = (maxWidth > 0 || maxHeight > 0)
984
+ let total = selectedImages.count
733
985
 
734
986
  let resultsLock = NSLock()
735
-
987
+ // 처리한 이미지는 바로 놓아 주도록 (전체 UIImage 배열을 끝까지 들고 있지 않게) 옵셔널 배열로 옮김
988
+ var pending: [(PHAsset?, UIImage)?] = selectedImages.map { $0 }
989
+
736
990
  DispatchQueue.global(qos: .userInitiated).async {
737
- for (index, pair) in selectedImages.enumerated() {
991
+ for index in 0..<total {
992
+ guard let pair = pending[index] else { continue }
993
+ pending[index] = nil
994
+ autoreleasepool {
738
995
  let (asset, image) = pair
739
996
  let timestamp = Int(Date().timeIntervalSince1970 * 1000)
740
997
  let originalSize = image.size
@@ -778,7 +1035,7 @@ class RNAttachmentKit: RCTEventEmitter {
778
1035
 
779
1036
  self.sendEvent(withName: "onImageProcessed", body: [
780
1037
  "index": index,
781
- "total": selectedImages.count,
1038
+ "total": total,
782
1039
  "image": currentResult
783
1040
  ])
784
1041
  } else {
@@ -828,15 +1085,18 @@ class RNAttachmentKit: RCTEventEmitter {
828
1085
 
829
1086
  self.sendEvent(withName: "onImageProcessed", body: [
830
1087
  "index": index,
831
- "total": selectedImages.count,
1088
+ "total": total,
832
1089
  "image": currentResult
833
1090
  ])
834
1091
  }
1092
+ } // end autoreleasepool
835
1093
  } // end for
836
1094
 
837
1095
  DispatchQueue.main.async {
838
1096
  self.currentGalleryVC = nil
839
1097
  resolve(results)
1098
+ // 결과를 넘긴 뒤: 갤러리의 PHCachingImageManager 캐시·콜백 해제 (닫힘 애니메이션에는 영향 없음)
1099
+ galleryVC?.releaseAfterReturn()
840
1100
  }
841
1101
  }
842
1102
  }
@@ -877,13 +1137,16 @@ class RNAttachmentKit: RCTEventEmitter {
877
1137
  }
878
1138
  }
879
1139
  }
1140
+ #if DEBUG
880
1141
  print("🔵 [DEBUG] Sheet 설정 완료 - 경과: \(String(format: "%.3f", CFAbsoluteTimeGetCurrent() - step3))초")
1142
+ #endif
881
1143
 
882
1144
  let step4 = CFAbsoluteTimeGetCurrent()
883
- print("🔵 [DEBUG] present 시작...")
884
1145
  rootViewController.present(navigationController, animated: true)
1146
+ #if DEBUG
885
1147
  print("🔵 [DEBUG] present 완료 - 경과: \(String(format: "%.3f", CFAbsoluteTimeGetCurrent() - step4))초")
886
1148
  print("✅ [DEBUG] openGallery 전체 완료 - 총 경과: \(String(format: "%.3f", CFAbsoluteTimeGetCurrent() - startTime))초")
1149
+ #endif
887
1150
  }
888
1151
  }
889
1152
 
@@ -902,6 +1165,13 @@ class RNAttachmentKit: RCTEventEmitter {
902
1165
  return
903
1166
  }
904
1167
 
1168
+ // openGallery 약속이 아직 안 풀렸으면 (닫기 버튼과 같은 경로로) 취소를 먼저 전달 → reject("CANCELLED")
1169
+ // 안 그러면 아래 cleanupMemory 가 콜백을 지워 버려 JS 약속이 영영 안 풀렸습니다.
1170
+ if !galleryVC.hasReturnedImages {
1171
+ galleryVC.hasReturnedImages = true
1172
+ galleryVC.onImagesSelected?([])
1173
+ }
1174
+
905
1175
  // 갤러리 닫기 전 강제 정리
906
1176
  galleryVC.forceCleanupBeforeDismiss()
907
1177
 
@@ -1032,14 +1302,18 @@ class RNAttachmentKit: RCTEventEmitter {
1032
1302
  let options: [CFString: Any] = [kCGImageDestinationLossyCompressionQuality: quality]
1033
1303
  CGImageDestinationAddImage(destination, cgImage, options as CFDictionary)
1034
1304
  if CGImageDestinationFinalize(destination) {
1305
+ #if DEBUG
1035
1306
  NSLog("✅ [encodeWebP] iOS 16 네이티브 WebP 인코딩 성공 - %d bytes", data.length)
1307
+ #endif
1036
1308
  return data as Data
1037
1309
  }
1038
1310
  }
1039
1311
  }
1040
1312
  }
1041
1313
 
1314
+ #if DEBUG
1042
1315
  NSLog("🟣 [encodeWebP] SDImageWebPCoder 런타임 폴백 시도")
1316
+ #endif
1043
1317
  // iOS 14, 15이거나 네이티브 실패 시 SDImageWebPCoder 런타임 호출
1044
1318
  if let coderClass = NSClassFromString("SDImageWebPCoder") as? NSObject.Type {
1045
1319
  if coderClass.responds(to: NSSelectorFromString("sharedCoder")) {
@@ -1057,7 +1331,9 @@ class RNAttachmentKit: RCTEventEmitter {
1057
1331
  // format: 3 (SDImageFormatWebP)
1058
1332
  let options: [String: Any] = ["encodeCompressionQuality": quality]
1059
1333
  if let data = encode(sharedCoder, encodeSel, image, 3, options) {
1334
+ #if DEBUG
1060
1335
  NSLog("✅ [encodeWebP] SDImageWebPCoder 런타임 인코딩 성공 - %d bytes", data.count)
1336
+ #endif
1061
1337
  return data
1062
1338
  } else {
1063
1339
  NSLog("❌ [encodeWebP] SDImageWebPCoder 런타임 인코딩 실패 (nil 반환)")
@@ -1237,6 +1513,7 @@ class RNAttachmentKit: RCTEventEmitter {
1237
1513
  DispatchQueue.main.async {
1238
1514
  self.currentGalleryVC = nil
1239
1515
  reject("ERROR", "Failed to load image from editor", nil)
1516
+ galleryVC?.releaseAfterReturn()
1240
1517
  }
1241
1518
  return
1242
1519
  }
@@ -1275,6 +1552,7 @@ class RNAttachmentKit: RCTEventEmitter {
1275
1552
  DispatchQueue.main.async {
1276
1553
  self.currentGalleryVC = nil
1277
1554
  resolve([result])
1555
+ galleryVC?.releaseAfterReturn()
1278
1556
  }
1279
1557
  }
1280
1558
  }
@@ -1289,10 +1567,12 @@ class RNAttachmentKit: RCTEventEmitter {
1289
1567
  DispatchQueue.global(qos: .userInitiated).async {
1290
1568
  let photoManager = PhotoManager.shared
1291
1569
  var results: [[String: Any]] = []
1292
- let dispatchGroup = DispatchGroup()
1293
1570
  let resultsLock = NSLock()
1294
1571
 
1572
+ // 한 장씩 순서대로 처리 (동시에 전부 디코딩하면 메모리가 치솟음). 해상도는 줄이지 않음
1295
1573
  for (index, asset) in assets.enumerated() {
1574
+ autoreleasepool {
1575
+ let dispatchGroup = DispatchGroup()
1296
1576
  dispatchGroup.enter()
1297
1577
  let timestamp = Int(Date().timeIntervalSince1970 * 1000)
1298
1578
  let originalUri = "ph://\(asset.localIdentifier)"
@@ -1367,10 +1647,10 @@ class RNAttachmentKit: RCTEventEmitter {
1367
1647
  }
1368
1648
  }
1369
1649
  }
1650
+ dispatchGroup.wait()
1651
+ } // end autoreleasepool
1370
1652
  }
1371
1653
 
1372
- dispatchGroup.wait()
1373
-
1374
1654
  DispatchQueue.main.async {
1375
1655
  self.currentGalleryVC = nil
1376
1656
  if results.isEmpty {
@@ -1379,6 +1659,7 @@ class RNAttachmentKit: RCTEventEmitter {
1379
1659
  // 순서를 유지하기 위해
1380
1660
  resolve(results)
1381
1661
  }
1662
+ galleryVC.releaseAfterReturn()
1382
1663
  }
1383
1664
  }
1384
1665
  }
@@ -1674,8 +1955,11 @@ fileprivate extension RNAttachmentKit {
1674
1955
  self.editorTransitionDelegate?.onCurrentAssetChanged = { [weak currentGallery] currentAsset in
1675
1956
  currentGallery?.setHiddenAsset(currentAsset, scrollIntoView: true)
1676
1957
  }
1677
- self.editorTransitionDelegate?.onEditorClosed = { [weak currentGallery] in
1958
+ self.editorTransitionDelegate?.onEditorClosed = { [weak self, weak currentGallery] in
1678
1959
  currentGallery?.setHiddenAsset(nil)
1960
+ // 편집 화면이 닫혔으니 sourceImage/uncroppedImage 를 들고 있는 전환 델리게이트를 놓아 줌
1961
+ // (호출 중인 객체이므로 다음 런루프에서)
1962
+ DispatchQueue.main.async { self?.editorTransitionDelegate = nil }
1679
1963
  }
1680
1964
  currentGallery.setHiddenAsset(asset)
1681
1965
 
@@ -1686,10 +1970,10 @@ fileprivate extension RNAttachmentKit {
1686
1970
  editorVC.themeColor = color
1687
1971
  }
1688
1972
 
1689
- editorVC.onConfirmMulti = { [weak currentGallery] (assets, croppedImages, filterStates) in
1973
+ editorVC.onConfirmMulti = { [weak currentGallery, weak editorVC] (assets, croppedImages, filterStates) in
1690
1974
  guard let currentGallery = currentGallery else { return }
1691
1975
 
1692
- editorVC.dismiss(animated: true) {
1976
+ editorVC?.dismiss(animated: true) {
1693
1977
  DispatchQueue.global(qos: .userInitiated).async {
1694
1978
  var newlyEditedAssets: [PHAsset] = []
1695
1979
 
@@ -1731,7 +2015,7 @@ fileprivate extension RNAttachmentKit {
1731
2015
  }
1732
2016
 
1733
2017
  // patch398: 편집 화면의 전송 버튼 — 편집본을 갤러리에 반영하고 선택한 사진을 바로 전송(편집 화면/갤러리 모두 닫힘)
1734
- editorVC.onSendMulti = { [weak currentGallery] (assets, croppedImages, filterStates) in
2018
+ editorVC.onSendMulti = { [weak currentGallery, weak editorVC] (assets, croppedImages, filterStates) in
1735
2019
  guard let currentGallery = currentGallery else { return }
1736
2020
  for asset in assets {
1737
2021
  guard var img = croppedImages[asset] else { continue }
@@ -1748,7 +2032,7 @@ fileprivate extension RNAttachmentKit {
1748
2032
  // patch399: 편집 화면의 이미지가 갤러리 자리로 돌아가는 애니메이션이 끝난 뒤에 전송합니다.
1749
2033
  currentGallery.refreshThumbnails(for: assets)
1750
2034
  currentGallery.updateSelectedCellNumbers()
1751
- editorVC.dismiss(animated: true) { [weak currentGallery] in
2035
+ editorVC?.dismiss(animated: true) { [weak currentGallery] in
1752
2036
  DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
1753
2037
  currentGallery?.sendSelectionFromEditor()
1754
2038
  }
@@ -8,32 +8,32 @@
8
8
  <key>BinaryPath</key>
9
9
  <string>RNAttachmentKit.framework/RNAttachmentKit</string>
10
10
  <key>LibraryIdentifier</key>
11
- <string>ios-arm64</string>
11
+ <string>ios-arm64_x86_64-simulator</string>
12
12
  <key>LibraryPath</key>
13
13
  <string>RNAttachmentKit.framework</string>
14
14
  <key>SupportedArchitectures</key>
15
15
  <array>
16
16
  <string>arm64</string>
17
+ <string>x86_64</string>
17
18
  </array>
18
19
  <key>SupportedPlatform</key>
19
20
  <string>ios</string>
21
+ <key>SupportedPlatformVariant</key>
22
+ <string>simulator</string>
20
23
  </dict>
21
24
  <dict>
22
25
  <key>BinaryPath</key>
23
26
  <string>RNAttachmentKit.framework/RNAttachmentKit</string>
24
27
  <key>LibraryIdentifier</key>
25
- <string>ios-arm64_x86_64-simulator</string>
28
+ <string>ios-arm64</string>
26
29
  <key>LibraryPath</key>
27
30
  <string>RNAttachmentKit.framework</string>
28
31
  <key>SupportedArchitectures</key>
29
32
  <array>
30
33
  <string>arm64</string>
31
- <string>x86_64</string>
32
34
  </array>
33
35
  <key>SupportedPlatform</key>
34
36
  <string>ios</string>
35
- <key>SupportedPlatformVariant</key>
36
- <string>simulator</string>
37
37
  </dict>
38
38
  </array>
39
39
  <key>CFBundlePackageType</key>
@@ -347,7 +347,9 @@ extern "C" {
347
347
  #pragma clang diagnostic ignored "-Watimport-in-framework-header"
348
348
  #endif
349
349
  @import CoreFoundation;
350
+ @import CoreLocation;
350
351
  @import Foundation;
352
+ @import MapKit;
351
353
  @import ObjectiveC;
352
354
  @import UIKit;
353
355
  #endif
@@ -529,6 +531,34 @@ SWIFT_CLASS("_TtC15RNAttachmentKit25ImageEditorViewController")
529
531
  - (void)scrollViewDidEndScrollingAnimation:(UIScrollView * _Nonnull)scrollView;
530
532
  @end
531
533
 
534
+ SWIFT_CLASS("_TtC15RNAttachmentKit23MapViewerViewController")
535
+ @interface MapViewerViewController : UIViewController
536
+ - (nullable instancetype)initWithCoder:(NSCoder * _Nonnull)coder SWIFT_UNAVAILABLE;
537
+ - (void)viewDidLoad;
538
+ - (void)viewDidLayoutSubviews;
539
+ @property (nonatomic, readonly) UIStatusBarStyle preferredStatusBarStyle;
540
+ - (void)viewWillAppear:(BOOL)animated;
541
+ - (void)viewDidAppear:(BOOL)animated;
542
+ - (nonnull instancetype)initWithNibName:(NSString * _Nullable)nibNameOrNil bundle:(NSBundle * _Nullable)nibBundleOrNil SWIFT_UNAVAILABLE;
543
+ @end
544
+
545
+ @class CLLocationManager;
546
+ @class CLLocation;
547
+ @interface MapViewerViewController (SWIFT_EXTENSION(RNAttachmentKit)) <CLLocationManagerDelegate>
548
+ - (void)locationManagerDidChangeAuthorization:(CLLocationManager * _Nonnull)manager;
549
+ - (void)locationManager:(CLLocationManager * _Nonnull)manager didUpdateLocations:(NSArray<CLLocation *> * _Nonnull)locations;
550
+ - (void)locationManager:(CLLocationManager * _Nonnull)manager didFailWithError:(NSError * _Nonnull)error;
551
+ @end
552
+
553
+ @class MKMapView;
554
+ @protocol MKAnnotation;
555
+ @class MKAnnotationView;
556
+ @interface MapViewerViewController (SWIFT_EXTENSION(RNAttachmentKit)) <MKMapViewDelegate>
557
+ - (void)mapViewDidFinishRenderingMap:(MKMapView * _Nonnull)mapView fullyRendered:(BOOL)fullyRendered;
558
+ - (MKAnnotationView * _Nullable)mapView:(MKMapView * _Nonnull)mapView viewForAnnotation:(id <MKAnnotation> _Nonnull)annotation SWIFT_WARN_UNUSED_RESULT;
559
+ - (void)mapView:(MKMapView * _Nonnull)mapView didSelectAnnotationView:(MKAnnotationView * _Nonnull)view;
560
+ @end
561
+
532
562
  /// 사진 모아보기 화면. 피커 갤러리(GalleryViewController)와 같은 바텀시트(60% → 확장, 아래로 끌어 닫기),
533
563
  /// 같은 상단 바·칸 모양·스크롤바/날짜 표시를 씁니다. 칸을 누르면 뷰어가 그 칸에서 줌으로 열리고,
534
564
  /// 뷰어에서 몇 장을 넘기든 닫으면 지금 보던 사진의 칸으로 돌아갑니다.