Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Attach Image to Email in React Native (using rn-image-picker & rn-mail)

11/04 UPDATE:

I've created a repo with an app that demonstrates just this problem below. Anyone who wants to install and play with it can find it here... https://github.com/geirman/react-native-photo-emailer


I'm using the following packages together in an attempt to choose an image from the gallery (or take one using the camera), then attach that photo to an email.

  • react-native-image-picker
  • react-native-mail

The following code works great on Android, but fails to attach the image on iOS.

import ImagePicker from 'react-native-image-picker';
handleAndroid = (subject, to) => {
  var pickerOptions = {
    title: 'Select Avatar',
    storageOptions: {
      skipBackup: true,
      path: 'images'
    }
  };

  ImagePicker.showImagePicker(pickerOptions, (response) => {
    const { uri, type, path } = response;
    this.sendMail(subject, to, path, type);
  });
}

And the sendMail method looks like this (minus error handling to simplify)

import Mailer from 'react-native-mail';
sendMail = (subject, to, uri, type) => {
  Mailer.mail({
    subject,
    recipients: [to],
    body: 'Optional Comment: ',
    attachment: {
      path: uri,
      type: type,
      name: subject
    },
    isHTML: true
  }
}    

Note that attachment.path must be the full file path to the attachment. For Android, that turns out to be the response.path, but iOS only returns response.uri.

For iOS's sendMail method, I remove attachment.name and attachment.type since they aren't returned, leaving just attachment.path which is set to the response.uri.

response.path (on Android) looks like this (this works)...

/storage/emulated/0/DCIM/Camera/IMG_20171102_11304344.jpg 

response.uri (on iOS) looks like this (response.path does not return anything on iOS)...

file://var/mobile/Containers/Data/Application/983938D-5304-463C-BD05-D033E55F5BEB/Documents/images/224CA6DD-5299-48C3-A7CF-0B645004535F.jpg

But according to React native - send photo per mail, I want something that looks more like this (on iOS)...

/Users/anton/Library/Developer/CoreSimulator/Devices/9A15F203-9A58-41C5-A4FC-EA25FAAE92BD/data/Containers/Data/Application/79FF93F9-BA89-4F4C-8809-277BEECD447D/Documents/EFFF0ECE-4063-4FE5-984E-E76506788350.jpg

So my question is, how do I get the full path to the image on iOS from the above the response.uri value. In other words, how do I get something that looks more like what RNMail needs (e.g. /Users/anton...) from what ImagePicker returns (e.g. file://var/mobile...)?

Update:

I forgot to mention that I'm running these tests on actual devices. An Android Moto Z and iPhone 5s. This is necessary because emulators are useless when testing camera and email functionality.

Here's a dump of my xcode logs related to one session: initialize app > choose photo from gallery > email opens (nothing attached) > cancel > delete draft (throws an 'email failed to send' error, even if I send it I get this error though the email does actually send)

2017-11-02 20:12:45.310 [info][tid:main][RCTCxxBridge.mm:187] Initializing <RCTCxxBridge: 0x1c01b5fc0> (parent: <RCTBridge: 0x1c00babe0>, executor: (null))
2017-11-02 20:12:45.314031-0700 RNMail[434:92814] Initializing <RCTCxxBridge: 0x1c01b5fc0> (parent: <RCTBridge: 0x1c00babe0>, executor: (null))
2017-11-02 20:12:45.421 [warn][tid:main][RCTBridge.m:121] Class RCTCxxModule was not exported. Did you forget to use RCT_EXPORT_MODULE()?
2017-11-02 20:12:45.421568-0700 RNMail[434:92814] Class RCTCxxModule was not exported. Did you forget to use RCT_EXPORT_MODULE()?
2017-11-02 20:12:45.492 [warn][tid:main][RCTModuleData.mm:69] Module RNMail requires main queue setup since it overrides `init` but doesn't implement `requiresMainQueueSetup. In a future release React Native will default to initializing all native modules on a background thread unless explicitly opted-out of.
2017-11-02 20:12:45.492054-0700 RNMail[434:92814] Module RNMail requires main queue setup since it overrides `init` but doesn't implement `requiresMainQueueSetup. In a future release React Native will default to initializing all native modules on a background thread unless explicitly opted-out of.
2017-11-02 20:12:45.504 [info][tid:main][RCTRootView.m:301] Running application RNMail ({
    initialProps =     {
    };
    rootTag = 1;
})
2017-11-02 20:12:45.503900-0700 RNMail[434:92814] Running application RNMail ({
    initialProps =     {
    };
    rootTag = 1;
})
2017-11-02 20:12:45.537105-0700 RNMail[434:92814] refreshPreferences: HangTracerEnabled: 0
2017-11-02 20:12:45.537237-0700 RNMail[434:92814] refreshPreferences: HangTracerDuration: 500
2017-11-02 20:12:45.537355-0700 RNMail[434:92814] refreshPreferences: ActivationLoggingEnabled: 0 ActivationLoggingTaskedOffByDA:0
=================================================================
Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 434, TID: 92857, Thread name: com.facebook.react.JavaScript, Queue name: com.apple.root.default-qos.overcommit, QoS: 21
Backtrace:
4   RNMail                              0x000000010026d2bc RCTCurrentAppBackgroundState + 220
5   RNMail                              0x000000010026c7f8 -[RCTAppState constantsToExport] + 68
6   RNMail                              0x0000000100275af4 -[RCTModuleData gatherConstants] + 724
7   RNMail                              0x0000000100275ea8 -[RCTModuleData exportedConstants] + 36
8   RNMail                              0x00000001002d312c _ZN8facebook5react15RCTNativeModule12getConstantsEv + 216
9   RNMail                              0x000000010035764c _ZN8facebook5react14ModuleRegistry9getConfigERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE + 5864
10  RNMail                              0x000000010038d8e0 _ZN8facebook5react16JSCNativeModules12createModuleERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEPK15OpaqueJSContext + 492
11  RNMail                              0x000000010038c5a0 _ZN8facebook5react16JSCNativeModules9getModuleEPK15OpaqueJSContextP14OpaqueJSString + 640
12  RNMail                              0x00000001003708c0 _ZN8facebook5react11JSCExecutor15getNativeModuleEP13OpaqueJSValueP14OpaqueJSString + 268
13  RNMail                              0x000000010037313c _ZZN8facebook5react12_GLOBAL__N_119exceptionWrapMethodIXadL_ZNS0_11JSCExecutor15getNativeModuleEP13OpaqueJSValueP14OpaqueJSStringEEEEPFPKS4_PK15OpaqueJSContextS5_S7_PS9_EvEN11funcWrapper4callESC_S5_S7_SD_ + 356
14  JavaScriptCore                      0x000000018df6cdc4 <redacted> + 336
15  JavaScriptCore                      0x000000018d950394 <redacted> + 3952
16  JavaScriptCore                      0x000000018e052a94 <redacted> + 10612
17  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
18  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
19  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
20  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
21  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
22  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
23  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
24  JavaScriptCore                      0x000000018e04ff50 <redacted> + 272
25  JavaScriptCore                      0x000000018df35b94 <redacted> + 136
26  JavaScriptCore                      0x000000018df0cb28 <redacted> + 11244
27  JavaScriptCore                      0x000000018dc011ac _ZN3JSC8evaluateEPNS_9ExecStateERKNS_10SourceCodeENS_7JSValueERN3WTF8NakedPtrINS_9ExceptionEEE + 316
28  JavaScriptCore                      0x000000018df6a558 JSEvaluateScript + 448
29  RNMail                              0x00000001002fcc58 _ZN8facebook5react14evaluateScriptEPK15OpaqueJSContextP14OpaqueJSStringS5_ + 100
30  RNMail                              0x000000010036d494 _ZN8facebook5react11JSCExecutor21loadApplicationScriptENSt3__110unique_ptrIKNS0_11JSBigStringENS2_14default_deleteIS5_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE + 2528
31  RNMail                              0x0000000100384d14 _ZZN8facebook5react16NativeToJsBridge15loadApplicationENSt3__110unique_ptrINS0_17JSModulesUnbundleENS2_14default_deleteIS4_EEEENS3_IKNS0_11JSBigStringENS5_IS9_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEN3$_0clEPNS0_10JSExecutorE + 1372
32  RNMail                              0x00000001003847ac _ZNSt3__128__invoke_void_return_wrapperIvE6__callIJRZN8facebook5react16NativeToJsBridge15loadApplicationENS_10unique_ptrINS4_17JSModulesUnbundleENS_14default_deleteIS7_EEEENS6_IKNS4_11JSBigStringENS8_ISC_EEEENS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEE3$_0PNS4_10JSExecutorEEEEvDpOT_ + 84
33  RNMail                              0x0000000100383da0 _ZNSt3__110__function6__funcIZN8fa2017-11-02 20:12:46.253004-0700 RNMail[434:92857] [reports] Main Thread Checker: UI API called on a background thread: -[UIApplication applicationState]
PID: 434, TID: 92857, Thread name: com.facebook.react.JavaScript, Queue name: com.apple.root.default-qos.overcommit, QoS: 21
Backtrace:
4   RNMail                              0x000000010026d2bc RCTCurrentAppBackgroundState + 220
5   RNMail                              0x000000010026c7f8 -[RCTAppState constantsToExport] + 68
6   RNMail                              0x0000000100275af4 -[RCTModuleData gatherConstants] + 724
7   RNMail                              0x0000000100275ea8 -[RCTModuleData exportedConstants] + 36
8   RNMail                              0x00000001002d312c _ZN8facebook5react15RCTNativeModule12getConstantsEv + 216
9   RNMail                              0x000000010035764c _ZN8facebook5react14ModuleRegistry9getConfigERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE + 5864
10  RNMail                              0x000000010038d8e0 _ZN8facebook5react16JSCNativeModules12createModuleERKNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEPK15OpaqueJSContext + 492
11  RNMail                              0x000000010038c5a0 _ZN8facebook5react16JSCNativeModules9getModuleEPK15OpaqueJSContextP14OpaqueJSString + 640
12  RNMail                              0x00000001003708c0 _ZN8facebook5react11JSCExecutor15getNativeModuleEP13OpaqueJSValueP14OpaqueJSString + 268
13  RNMail                              0x000000010037313c _ZZN8facebook5react12_GLOBAL__N_119exceptionWrapMethodIXadL_ZNS0_11JSCExecutor15getNativeModuleEP13OpaqueJSValueP14OpaqueJSStringEEEEPFPKS4_PK15OpaqueJSContextS5_S7_PS9_EvEN11funcWrapper4callESC_S5_S7_SD_ + 356
14  JavaScriptCore                      0x000000018df6cdc4 <redacted> + 336
15  JavaScriptCore                      0x000000018d950394 <redacted> + 3952
16  JavaScriptCore                      0x000000018e052a94 <redacted> + 10612
17  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
18  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
19  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
20  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
21  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
22  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
23  JavaScriptCore                      0x000000018e056a94 <redacted> + 26996
24  JavaScriptCore                      0x000000018e04ff50 <redacted> + 272
25  JavaScriptCore                      0x000000018df35b94 <redacted> + 136
26  JavaScriptCore                      0x000000018df0cb28 <redacted> + 11244
27  JavaScriptCore                      0x000000018dc011ac _ZN3JSC8evaluateEPNS_9ExecStateERKNS_10SourceCodeENS_7JSValueERN3WTF8NakedPtrINS_9ExceptionEEE + 316
28  JavaScriptCore                      0x000000018df6a558 JSEvaluateScript + 448
29  RNMail                              0x00000001002fcc58 _ZN8facebook5react14evaluateScriptEPK15OpaqueJSContextP14OpaqueJSStringS5_ + 100
30  RNMail                              0x000000010036d494 _ZN8facebook5react11JSCExecutor21loadApplicationScriptENSt3__110unique_ptrIKNS0_11JSBigStringENS2_14default_deleteIS5_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEE + 2528
31  RNMail                              0x0000000100384d14 _ZZN8facebook5react16NativeToJsBridge15loadApplicationENSt3__110unique_ptrINS0_17JSModulesUnbundleENS2_14default_deleteIS4_EEEENS3_IKNS0_11JSBigStringENS5_IS9_EEEENS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEN3$_0clEPNS0_10JSExecutorE + 1372
32  RNMail                              0x00000001003847ac _ZNSt3__128__invoke_void_return_wrapperIvE6__callIJRZN8facebook5react16NativeToJsBridge15loadApplicationENS_10unique_ptrINS4_17JSModulesUnbundleENS_14default_deleteIS7_EEEENS6_IKNS4_11JSBigStringENS8_ISC_EEEENS_12basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEEE3$_0PNS4_10JSExecutorEEEEvDpOT_ + 84
33  RNMail                              0x0000000100383da0 _ZNSt3__110__function6__funcIZN8fa
2017-11-02 20:12:46.621 [info][tid:com.facebook.react.JavaScript] Running application "RNMail" with appParams: {"rootTag":1,"initialProps":{}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF
2017-11-02 20:12:46.621391-0700 RNMail[434:92857] Running application "RNMail" with appParams: {"rootTag":1,"initialProps":{}}. __DEV__ === true, development-level warning are ON, performance optimizations are OFF
2017-11-02 20:12:47.742027-0700 RNMail[434:92844] [] nw_connection_get_connected_socket 4 Connection has no connected handler
2017-11-02 20:12:47.742187-0700 RNMail[434:92844] TCP Conn 0x1c4170200 Failed : error 0:61 [61]
2017-11-02 20:12:50.900283-0700 RNMail[434:92848] [] nw_connection_get_connected_socket 5 Connection has no connected handler
2017-11-02 20:12:50.900432-0700 RNMail[434:92848] TCP Conn 0x1c0171400 Failed : error 0:61 [61]
2017-11-02 20:12:53.627866-0700 RNMail[434:92814] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles
2017-11-02 20:12:53.628903-0700 RNMail[434:92814] [MC] Reading from public effective user settings.
2017-11-02 20:12:54.060535-0700 RNMail[434:92843] [] nw_connection_get_connected_socket 6 Connection has no connected handler
2017-11-02 20:12:54.060707-0700 RNMail[434:92843] TCP Conn 0x1c416e400 Failed : error 0:61 [61]
2017-11-02 20:12:57.279004-0700 RNMail[434:92847] [] nw_connection_get_connected_socket 7 Connection has no connected handler
2017-11-02 20:12:57.279148-0700 RNMail[434:92847] TCP Conn 0x1c0172b40 Failed : error 0:61 [61]
2017-11-02 20:13:00.409536-0700 RNMail[434:92848] [discovery] errors encountered while discovering extensions: Error Domain=PlugInKit Code=13 "query cancelled" UserInfo={NSLocalizedDescription=query cancelled}
2017-11-02 20:13:00.429314-0700 RNMail[434:93038] [] nw_connection_get_connected_socket 8 Connection has no connected handler
2017-11-02 20:13:00.429401-0700 RNMail[434:93038] TCP Conn 0x1c0171c40 Failed : error 0:61 [61]
2017-11-02 20:13:00.827 [info][tid:com.facebook.react.JavaScript] 'Response = ', { fileSize: 508257,
  data: REMOVED_BASE64_STRING
  timestamp: '2017-10-25T02:51:35Z',
  uri: 'file:///var/mobile/Containers/Data/Application/144D1C48-C6D5-475A-891A-F75360CD64E2/Documents/images/0EF09235-6E04-4799-86F5-3E996ED4E439.jpg',
  origURL: 'assets-library://asset/asset.JPG?id=EF345E7A-2DF1-49CA-875D-4208976A635B&ext=JPG',
  isVertical: false,
  height: 768,
  width: 1024,
  fileName: 'IMG_0003.JPG' }
2017-11-02 20:13:00.888025-0700 RNMail[434:92857] 'Response = ', { fileSize: 508257,
  data: REMOVED_BASE64_STRING
2017-11-02 20:13:00.974189-0700 RNMail[434:92814] [MC] Filtering mail sheet accounts for bundle ID: org.reactjs.native.example.RNMail, source account management: 1
2017-11-02 20:13:00.985481-0700 RNMail[434:92814] [MC] Filtering mail sheet accounts for bundle ID: org.reactjs.native.example.RNMail, source account management: 1
2017-11-02 20:13:01.834117-0700 RNMail[434:92814] [MC] Lazy loading NSBundle MobileCoreServices.framework
2017-11-02 20:13:01.843828-0700 RNMail[434:92814] [MC] Loaded MobileCoreServices.framework
2017-11-02 20:13:04.218796-0700 RNMail[434:93034] [] nw_connection_get_connected_socket 9 Connection has no connected handler
2017-11-02 20:13:04.218943-0700 RNMail[434:93034] TCP Conn 0x1c01747c0 Failed : error 0:61 [61]
2017-11-02 20:13:07.341962-0700 RNMail[434:93036] [] nw_connection_get_connected_socket 10 Connection has no connected handler
2017-11-02 20:13:07.342107-0700 RNMail[434:93036] TCP Conn 0x1c0172540 Failed : error 0:61 [61]
2017-11-02 20:13:08.605 [info][tid:com.facebook.react.JavaScript] null
2017-11-02 20:13:08.606476-0700 RNMail[434:92857] null
2017-11-02 20:13:10.469212-0700 RNMail[434:93034] [] nw_connection_get_connected_socket 11 Connection has no connected handler
2017-11-02 20:13:10.469352-0700 RNMail[434:93034] TCP Conn 0x1c4174280 Failed : error 0:61 [61]
2017-11-02 20:13:11.144 [info][tid:com.facebook.react.JavaScript] OK: Email Error Response
2017-11-02 20:13:11.144088-0700 RNMail[434:92857] OK: Email Error Response
2017-11-02 20:13:13.599247-0700 RNMail[434:93036] [] nw_connection_get_connected_socket 12 Connection has no connected handler
2017-11-02 20:13:13.599391-0700 RNMail[434:93036] TCP Conn 0x1c0175480 Failed : error 0:61 [61]

Update 11/7/2017

At @Artal's suggestion, I started digging into the Xcode debugger. I found that the attachmentPath is assigned to fileData as an NSData type, then it's later used to actually attach the photo to the email. However, while it looks as though fileData is being assigned properly, as soon as I step one line further into the code, then fileData = nill somehow. Here's a few screenshots that step through the code.

enter image description here

like image 690
Chris Geirman Avatar asked Nov 02 '17 23:11

Chris Geirman


1 Answers

@Artal set me on the right path, but this is the code that ultimately solved the problem...

Replace Line 82 of RNMail.m

NSData *fileData = [NSData dataWithContentsOfFile:attachmentPath];

With this code...

// Get the URL string, which is *not* a path (e.g. because it's file:// based)
NSString *attachmentURLString = [RCTConvert NSString:options[@"attachment"][@"path"]];
// Create a URL from the string
NSURL *attachmentURL = [[NSURLComponents componentsWithString:attachmentURLString] URL];

// Get the resource path and read the file using NSData
NSError *error = nil;
NSData *fileData = [NSData dataWithContentsOfURL:attachmentURL options:0 error:&error];

if(fileData == nil) {
    // handle error
}

More information surrounding how this solution was arrived at, please see NSData Assignment Vanishes (becomes nil) Directly After Assigned

like image 56
Chris Geirman Avatar answered Sep 28 '22 06:09

Chris Geirman