@lodev09/react-native-exify 0.2.2 → 0.2.4

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.
package/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # React Native Exify
2
2
 
3
+ [![CI](https://github.com/lodev09/react-native-exify/actions/workflows/ci.yml/badge.svg)](https://github.com/lodev09/react-native-exify/actions/workflows/ci.yml)
4
+ ![GitHub Release](https://img.shields.io/github/v/release/lodev09/react-native-exify)
5
+ ![NPM Downloads](https://img.shields.io/npm/dw/%40lodev09%2Freact-native-exify)
6
+
3
7
  A simple library to read and write image Exif metadata in React Native. Inspired from [this thread](https://github.com/mrousavy/react-native-vision-camera/issues/780).
4
8
 
5
9
  ## Features
@@ -42,11 +46,11 @@ const result = await writeAsync(uri, newTags)
42
46
  console.log(result.tags)
43
47
  ```
44
48
 
45
- See [example](example) for more detailed usage.
46
-
47
- ℹ️ Note that on IOS, writing exif into an Asset file will duplicate the image. IOS does not allow writing exif into an Asset file directly.
49
+ > **Note**:
50
+ > On IOS, writing exif into an Asset file will duplicate the image. IOS does not allow writing exif into an Asset file directly.
51
+ > If you're getting the photo from a [camera](https://github.com/mrousavy/react-native-vision-camera/), write it into the output file first before saving to the Asset library!
48
52
 
49
- If you're getting the photo from a [camera](https://github.com/mrousavy/react-native-vision-camera/), write it into the output file first before saving to the Asset library!
53
+ See [example](example) for more detailed usage.
50
54
 
51
55
  ## Contributing
52
56
  Contributions are welcome!
@@ -44,7 +44,7 @@ def supportsNamespace() {
44
44
 
45
45
  android {
46
46
  if (supportsNamespace()) {
47
- namespace "com.exify"
47
+ namespace "com.lodev09.exify"
48
48
 
49
49
  sourceSets {
50
50
  main {
@@ -1,3 +1,3 @@
1
1
  <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- package="com.exify">
2
+ package="com.lodev09.exify">
3
3
  </manifest>
@@ -1,9 +1,9 @@
1
- package com.exify
1
+ package com.lodev09.exify
2
2
 
3
3
  import android.content.Context
4
4
  import android.net.Uri
5
5
  import androidx.exifinterface.media.ExifInterface
6
- import com.exify.ExifyUtils.formatTags
6
+ import com.lodev09.exify.ExifyUtils.formatTags
7
7
  import com.facebook.react.bridge.Arguments
8
8
  import com.facebook.react.bridge.Promise
9
9
  import com.facebook.react.bridge.ReactApplicationContext
@@ -1,4 +1,4 @@
1
- package com.exify
1
+ package com.lodev09.exify
2
2
 
3
3
  import com.facebook.react.ReactPackage
4
4
  import com.facebook.react.bridge.NativeModule
@@ -1,4 +1,4 @@
1
- package com.exify
1
+ package com.lodev09.exify
2
2
  import androidx.exifinterface.media.ExifInterface
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- package com.exify
1
+ package com.lodev09.exify
2
2
 
3
3
  import androidx.exifinterface.media.ExifInterface
4
4
  import com.facebook.react.bridge.Arguments
package/ios/Exify.swift CHANGED
@@ -11,19 +11,18 @@ import PhotosUI
11
11
 
12
12
  @objc(Exify)
13
13
  class Exify: NSObject {
14
-
15
- func readExif(uri: String, resolve: @escaping RCTPromiseResolveBlock) -> Void {
14
+ func readExif(uri: String, resolve: @escaping RCTPromiseResolveBlock) {
16
15
  guard let url = URL(string: uri) else {
17
16
  resolve(nil)
18
17
  return
19
18
  }
20
-
19
+
21
20
  readExifTags(from: url) { tags in
22
21
  resolve(tags)
23
22
  }
24
23
  }
25
24
 
26
- func readExif(assetId: String, resolve: @escaping RCTPromiseResolveBlock) -> Void {
25
+ func readExif(assetId: String, resolve: @escaping RCTPromiseResolveBlock) {
27
26
  guard let asset = getAssetBy(id: assetId) else {
28
27
  resolve(nil)
29
28
  return
@@ -32,62 +31,61 @@ class Exify: NSObject {
32
31
  let imageOptions = PHContentEditingInputRequestOptions()
33
32
  imageOptions.isNetworkAccessAllowed = true
34
33
 
35
- asset.requestContentEditingInput(with: imageOptions) { contentInput, info in
34
+ asset.requestContentEditingInput(with: imageOptions) { contentInput, _ in
36
35
  guard let url = contentInput?.fullSizeImageURL else {
37
36
  resolve(nil)
38
37
  return
39
38
  }
40
-
39
+
41
40
  readExifTags(from: url) { tags in
42
41
  resolve(tags)
43
42
  }
44
43
  }
45
44
  }
46
-
47
- func writeExif(assetId: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
45
+
46
+ func writeExif(assetId: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
48
47
  guard let asset = getAssetBy(id: assetId) else {
49
48
  reject("Error", "Cannot retrieve asset.", nil)
50
49
  return
51
50
  }
52
-
51
+
53
52
  let imageOptions = PHContentEditingInputRequestOptions()
54
53
  imageOptions.isNetworkAccessAllowed = true
55
-
54
+
56
55
  asset.requestContentEditingInput(with: imageOptions) { contentInput, _ in
57
56
  guard let contentInput, let url = contentInput.fullSizeImageURL else {
58
57
  reject("Error", "Unable to read metadata from asset", nil)
59
58
  return
60
59
  }
61
-
60
+
62
61
  updateMetadata(url: url, with: tags) { metadata, data in
63
62
  guard let metadata, let data else {
64
63
  reject("Error", "Could not update metadata", nil)
65
64
  return
66
65
  }
67
-
66
+
68
67
  do {
69
- try PHPhotoLibrary.shared().performChangesAndWait{
68
+ try PHPhotoLibrary.shared().performChangesAndWait {
70
69
  let request = PHAssetCreationRequest.forAsset()
71
70
  request.addResource(with: .photo, data: data, options: nil)
72
71
  request.creationDate = Date()
73
-
72
+
74
73
  let newAssetId = request.placeholderForCreatedAsset!.localIdentifier
75
74
  resolve([
76
75
  "uri": "ph://\(newAssetId)",
77
76
  "assetId": newAssetId,
78
77
  "tags": getExifTags(from: metadata),
79
78
  ])
80
-
81
79
  }
82
- } catch let error {
80
+ } catch {
83
81
  reject("Error", "Could not save to image file", nil)
84
82
  print(error)
85
83
  }
86
84
  }
87
85
  }
88
86
  }
89
-
90
- func writeExif(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
87
+
88
+ func writeExif(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
91
89
  guard let url = URL(string: uri) else {
92
90
  reject("Error", "Invalid URL", nil)
93
91
  return
@@ -98,7 +96,7 @@ class Exify: NSObject {
98
96
  reject("Error", "Could not update metadata", nil)
99
97
  return
100
98
  }
101
-
99
+
102
100
  do {
103
101
  // Write to the current file
104
102
  try data.write(to: url, options: .atomic)
@@ -107,8 +105,7 @@ class Exify: NSObject {
107
105
  "uri": uri,
108
106
  "tags": getExifTags(from: metadata),
109
107
  ])
110
-
111
- } catch let error {
108
+ } catch {
112
109
  reject("Error", "Could not save to image file", nil)
113
110
  print(error)
114
111
  }
@@ -116,7 +113,7 @@ class Exify: NSObject {
116
113
  }
117
114
 
118
115
  @objc(readAsync:withResolver:withRejecter:)
119
- func readAsync(uri: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
116
+ func readAsync(uri: String, resolve: @escaping RCTPromiseResolveBlock, reject _: @escaping RCTPromiseRejectBlock) {
120
117
  if uri.starts(with: "ph://") {
121
118
  let assetId = String(uri[uri.index(uri.startIndex, offsetBy: 5)...])
122
119
  readExif(assetId: assetId, resolve: resolve)
@@ -126,7 +123,7 @@ class Exify: NSObject {
126
123
  }
127
124
 
128
125
  @objc(writeAsync:withExif:withResolver:withRejecter:)
129
- func writeAsync(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
126
+ func writeAsync(uri: String, tags: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
130
127
  if uri.starts(with: "ph://") {
131
128
  let assetId = String(uri[uri.index(uri.startIndex, offsetBy: 5)...])
132
129
  writeExif(assetId: assetId, tags: tags, resolve: resolve, reject: reject)
@@ -27,7 +27,7 @@ func getAssetsBy(assetIds: [String]) -> PHFetchResult<PHAsset> {
27
27
  return PHAsset.fetchAssets(withLocalIdentifiers: assetIds, options: options)
28
28
  }
29
29
 
30
- func addTagEntries(from dictionary: CFString, metadata: NSDictionary, to tags: NSMutableDictionary) -> Void {
30
+ func addTagEntries(from dictionary: CFString, metadata: NSDictionary, to tags: NSMutableDictionary) {
31
31
  if let entries = metadata[dictionary] as? [String: Any] {
32
32
  tags.addEntries(from: entries)
33
33
  }
@@ -35,54 +35,56 @@ func addTagEntries(from dictionary: CFString, metadata: NSDictionary, to tags: N
35
35
 
36
36
  func getExifTags(from metadata: NSDictionary) -> [String: Any] {
37
37
  let tags: NSMutableDictionary = [:]
38
-
38
+
39
39
  // Add root non-dictionary properties
40
40
  for (key, value) in metadata {
41
41
  if value as? [String: Any] == nil {
42
42
  tags[key] = value
43
43
  }
44
44
  }
45
-
45
+
46
46
  // Append all {Exif} properties
47
47
  addTagEntries(from: kCGImagePropertyExifDictionary, metadata: metadata, to: tags)
48
-
48
+
49
49
  // Prefix {GPS} dictionary with "GPS"
50
50
  if let gps = metadata[kCGImagePropertyGPSDictionary as String] as? [String: Any] {
51
51
  for (key, value) in gps {
52
52
  tags["GPS" + key] = value
53
53
  }
54
54
  }
55
-
55
+
56
56
  // Include tags from formats
57
57
  addTagEntries(from: kCGImagePropertyTIFFDictionary, metadata: metadata, to: tags)
58
58
  addTagEntries(from: kCGImagePropertyPNGDictionary, metadata: metadata, to: tags)
59
59
  addTagEntries(from: kCGImagePropertyHEICSDictionary, metadata: metadata, to: tags)
60
-
61
- return tags as! [String: Any]
60
+
61
+ return tags as? [String: Any] ?? [:]
62
62
  }
63
63
 
64
- func readExifTags(from url: URL?, completionHandler: ([String: Any]?) -> Void) -> Void {
64
+ func readExifTags(from url: URL?, completionHandler: ([String: Any]?) -> Void) {
65
65
  guard let url, let sourceImage = CGImageSourceCreateWithURL(url as CFURL, nil),
66
66
  let metadataDict = CGImageSourceCopyPropertiesAtIndex(sourceImage, 0, nil) else {
67
+ completionHandler(nil)
67
68
  return
68
69
  }
69
-
70
+
70
71
  let tags = getExifTags(from: metadataDict)
71
72
  completionHandler(tags)
72
73
  }
73
74
 
74
- func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDictionary?, Data?) -> Void) -> Void {
75
+ func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDictionary?, Data?) -> Void) {
75
76
  guard let cgImageSource = CGImageSourceCreateWithURL(url as CFURL, nil) else {
77
+ completionHanlder(nil, nil)
76
78
  return
77
79
  }
78
-
80
+
79
81
  let metadataDict = CGImageSourceCopyPropertiesAtIndex(cgImageSource, 0, nil) ?? [:] as CFDictionary
80
82
  let metadata = NSMutableDictionary(dictionary: metadataDict)
81
83
 
82
84
  // Append additional Exif data
83
85
  let exifDict = metadata[kCGImagePropertyExifDictionary as String] as? NSMutableDictionary
84
- exifDict!.addEntries(from: tags)
85
-
86
+ exifDict?.addEntries(from: tags)
87
+
86
88
  // Handle GPS Tags
87
89
  var gpsDict = [String: Any]()
88
90
 
@@ -100,15 +102,14 @@ func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDi
100
102
  gpsDict[kCGImagePropertyGPSAltitude as String] = abs(altitude)
101
103
  gpsDict[kCGImagePropertyGPSAltitudeRef as String] = altitude >= 0 ? 0 : 1
102
104
  }
103
-
105
+
104
106
  if let gpsDate = tags["GPSDateStamp"] as? String {
105
107
  gpsDict[kCGImagePropertyGPSDateStamp as String] = gpsDate
106
108
  }
107
-
109
+
108
110
  if let gpsTime = tags["GPSTimeStamp"] as? String {
109
111
  gpsDict[kCGImagePropertyGPSTimeStamp as String] = gpsTime
110
112
  }
111
-
112
113
 
113
114
  if metadata[kCGImagePropertyGPSDictionary as String] == nil {
114
115
  metadata[kCGImagePropertyGPSDictionary as String] = gpsDict
@@ -117,19 +118,20 @@ func updateMetadata(url: URL, with tags: [String: Any], completionHanlder: (NSDi
117
118
  metadataGpsDict.addEntries(from: gpsDict)
118
119
  }
119
120
  }
120
-
121
+
121
122
  metadata.setObject(NSNumber(value: 1), forKey: kCGImageDestinationLossyCompressionQuality as NSString)
122
-
123
+
123
124
  let destinationData = NSMutableData()
124
125
 
125
- guard let sourceType = CGImageSourceGetType(cgImageSource),
126
- let destination = CGImageDestinationCreateWithData(destinationData, sourceType, 1, nil) else {
126
+ guard let uiImage = UIImage(contentsOfFile: url.path),
127
+ let sourceType = CGImageSourceGetType(cgImageSource),
128
+ let destination = CGImageDestinationCreateWithData(destinationData, sourceType, 1, nil) else {
129
+ completionHanlder(nil, nil)
127
130
  return
128
131
  }
129
132
 
130
- CGImageDestinationAddImageFromSource(destination, cgImageSource, 0, metadata)
131
- if CGImageDestinationFinalize(destination) {
132
- completionHanlder(metadata, destinationData as Data)
133
- }
134
- }
133
+ CGImageDestinationAddImage(destination, uiImage.cgImage!, metadata)
134
+ CGImageDestinationFinalize(destination)
135
135
 
136
+ completionHanlder(metadata, destinationData as Data)
137
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lodev09/react-native-exify",
3
- "version": "0.2.2",
4
- "description": "Read and write exif data into an image.",
3
+ "version": "0.2.4",
4
+ "description": "Read and write exif data into an image 🏷️",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
7
7
  "types": "lib/typescript/src/index.d.ts",
@@ -31,7 +31,7 @@
31
31
  "typecheck": "tsc --noEmit",
32
32
  "lint": "eslint --fix \"**/*.{js,ts,tsx}\"",
33
33
  "format": "prettier --write \"**/*.{js,ts,tsx}\"",
34
- "tidy": "yarn typecheck && yarn lint && yarn format",
34
+ "tidy": "yarn typecheck && yarn lint && yarn format && scripts/swiftlint.sh && scripts/swiftformat.sh",
35
35
  "clean": "del-cli android/build lib && yarn workspace exify-example clean",
36
36
  "prepare": "bob build",
37
37
  "release": "release-it"
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <Workspace
3
- version = "1.0">
4
- <FileRef
5
- location = "self:">
6
- </FileRef>
7
- </Workspace>
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>IDEDidComputeMac32BitWarning</key>
6
- <true/>
7
- </dict>
8
- </plist>
@@ -1,14 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <key>SchemeUserState</key>
6
- <dict>
7
- <key>Exify.xcscheme_^#shared#^_</key>
8
- <dict>
9
- <key>orderHint</key>
10
- <integer>0</integer>
11
- </dict>
12
- </dict>
13
- </dict>
14
- </plist>