1// Copyright © 2018 650 Industries. All rights reserved. 2 3#import <MessageUI/MessageUI.h> 4#import <ABI47_0_0EXSMS/ABI47_0_0EXSMSModule.h> 5#import <ABI47_0_0ExpoModulesCore/ABI47_0_0EXUtilities.h> 6#if SD_MAC 7#import <CoreServices/CoreServices.h> 8#else 9#import <MobileCoreServices/MobileCoreServices.h> 10#endif 11 12@interface ABI47_0_0EXSMSModule () <MFMessageComposeViewControllerDelegate> 13 14@property (nonatomic, weak) id<ABI47_0_0EXUtilitiesInterface> utils; 15@property (nonatomic, strong) ABI47_0_0EXPromiseResolveBlock resolve; 16@property (nonatomic, strong) ABI47_0_0EXPromiseRejectBlock reject; 17 18@end 19 20@implementation ABI47_0_0EXSMSModule 21 22ABI47_0_0EX_EXPORT_MODULE(ExpoSMS); 23 24- (dispatch_queue_t)methodQueue 25{ 26 // Everything in this module uses `MFMessageComposeViewController` which is a subclass of UIViewController, 27 // so everything should be called from main thread. 28 return dispatch_get_main_queue(); 29} 30 31- (void)setModuleRegistry:(ABI47_0_0EXModuleRegistry *)moduleRegistry 32{ 33 _utils = [moduleRegistry getModuleImplementingProtocol:@protocol(ABI47_0_0EXUtilitiesInterface)]; 34} 35 36ABI47_0_0EX_EXPORT_METHOD_AS(isAvailableAsync, 37 isAvailable:(ABI47_0_0EXPromiseResolveBlock)resolve 38 rejecter:(ABI47_0_0EXPromiseRejectBlock)reject) 39{ 40 resolve(@([MFMessageComposeViewController canSendText])); 41} 42 43ABI47_0_0EX_EXPORT_METHOD_AS(sendSMSAsync, 44 sendSMS:(NSArray<NSString *> *)addresses 45 message:(NSString *)message 46 options:(NSDictionary *)options 47 resolver:(ABI47_0_0EXPromiseResolveBlock)resolve 48 rejecter:(ABI47_0_0EXPromiseRejectBlock)reject) 49{ 50 if (![MFMessageComposeViewController canSendText]) { 51 reject(@"E_SMS_UNAVAILABLE", @"SMS service not available", nil); 52 return; 53 } 54 55 if (_resolve != nil || _reject != nil) { 56 reject(@"E_SMS_SENDING_IN_PROGRESS", @"Different SMS sending in progress. Await the old request and then try again.", nil); 57 return; 58 } 59 60 _resolve = resolve; 61 _reject = reject; 62 63 MFMessageComposeViewController *messageComposeViewController = [[MFMessageComposeViewController alloc] init]; 64 messageComposeViewController.messageComposeDelegate = self; 65 messageComposeViewController.recipients = addresses; 66 messageComposeViewController.body = message; 67 68 if (options) { 69 if (options[@"attachments"]) { 70 NSArray *attachments = (NSArray *) [options objectForKey:@"attachments"]; 71 for (NSDictionary* attachment in attachments) { 72 NSString *mimeType = attachment[@"mimeType"]; 73 CFStringRef mimeTypeRef = (__bridge CFStringRef)mimeType; 74 CFStringRef utiRef = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeTypeRef, NULL); 75 if (utiRef == NULL) { 76 reject(@"E_SMS_ATTACHMENT", [NSString stringWithFormat:@"Failed to find UTI for mimeType: %@", mimeType], nil); 77 _resolve = nil; 78 _reject = nil; 79 return; 80 } 81 NSString *typeIdentifier = (__bridge_transfer NSString *)utiRef; 82 NSString *uri = attachment[@"uri"]; 83 NSString *filename = attachment[@"filename"]; 84 NSError *error; 85 NSData *attachmentData = [NSData dataWithContentsOfURL:[NSURL URLWithString:uri] options:(NSDataReadingOptions)0 error:&error]; 86 bool attached = [messageComposeViewController addAttachmentData:attachmentData typeIdentifier:typeIdentifier filename:filename]; 87 if (!attached) { 88 reject(@"E_SMS_ATTACHMENT", [NSString stringWithFormat:@"Failed to attach file: %@", uri], nil); 89 _resolve = nil; 90 _reject = nil; 91 return; 92 } 93 } 94 } 95 } 96 97 [self.utils.currentViewController presentViewController:messageComposeViewController animated:YES completion:nil]; 98} 99 100- (void)messageComposeViewController:(MFMessageComposeViewController *)controller 101 didFinishWithResult:(MessageComposeResult)result 102{ 103 NSDictionary *resolveData; 104 NSString *rejectMessage; 105 switch (result) { 106 case MessageComposeResultCancelled: 107 resolveData = @{@"result": @"cancelled"}; 108 break; 109 case MessageComposeResultSent: 110 resolveData = @{@"result": @"sent"}; 111 break; 112 case MessageComposeResultFailed: 113 rejectMessage = @"User's attempt to save or send an SMS was unsuccessful. This can occur when the device loses connection to Wifi or Cellular."; 114 break; 115 default: 116 rejectMessage = @"SMS message sending failed with unknown error"; 117 break; 118 } 119 ABI47_0_0EX_WEAKIFY(self); 120 [controller dismissViewControllerAnimated:YES completion:^{ 121 ABI47_0_0EX_ENSURE_STRONGIFY(self); 122 if (rejectMessage) { 123 self->_reject(@"E_SMS_SENDING_FAILED", rejectMessage, nil); 124 } else { 125 self->_resolve(resolveData); 126 } 127 self->_reject = nil; 128 self->_resolve = nil; 129 }]; 130} 131 132@end 133