// Created by little-snow-fox on 2019-10-9. #import "WechatLib.h" #import "WXApiObject.h" #import #import #import #import @implementation WechatLib // Define error messages #define NOT_REGISTERED (@"registerApp required.") #define INVOKE_FAILED (@"WeChat API invoke returns false.") @synthesize bridge = _bridge; RCT_EXPORT_MODULE() - (instancetype)init { self = [super init]; if (self) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:@"RCTOpenURLNotification" object:nil]; } return self; } - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (BOOL)handleOpenURL:(NSNotification *)aNotification { NSString * aURLString = [aNotification userInfo][@"url"]; NSURL * aURL = [NSURL URLWithString:aURLString]; if ([WXApi handleOpenURL:aURL delegate:self]) { return YES; } else { return NO; } } - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } + (BOOL)requiresMainQueueSetup { return YES; } // 获取网络图片的公共方法 - (UIImage *) getImageFromURL:(NSString *)fileURL { UIImage * result; NSData * data = [NSData dataWithContentsOfURL:[NSURL URLWithString:fileURL]]; result = [UIImage imageWithData:data]; return result; } // 压缩图片 - (NSData *)compressImage:(UIImage *)image toByte:(NSUInteger)maxLength { // Compress by quality CGFloat compression = 1; NSData *data = UIImageJPEGRepresentation(image, compression); if (data.length < maxLength) return data; CGFloat max = 1; CGFloat min = 0; for (int i = 0; i < 6; ++i) { compression = (max + min) / 2; data = UIImageJPEGRepresentation(image, compression); if (data.length < maxLength * 0.9) { min = compression; } else if (data.length > maxLength) { max = compression; } else { break; } } UIImage *resultImage = [UIImage imageWithData:data]; if (data.length < maxLength) return data; // Compress by size NSUInteger lastDataLength = 0; while (data.length > maxLength && data.length != lastDataLength) { lastDataLength = data.length; CGFloat ratio = (CGFloat)maxLength / data.length; CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)), (NSUInteger)(resultImage.size.height * sqrtf(ratio))); // Use NSUInteger to prevent white blank UIGraphicsBeginImageContext(size); [resultImage drawInRect:CGRectMake(0, 0, size.width, size.height)]; resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); data = UIImageJPEGRepresentation(resultImage, compression); } if (data.length > maxLength) { return [self compressImage:resultImage toByte:maxLength]; } return data; } RCT_EXPORT_METHOD(registerApp:(NSString *)appid :(NSString *)universalLink :(RCTResponseSenderBlock)callback) { self.appId = appid; callback(@[[WXApi registerApp:appid universalLink:universalLink] ? [NSNull null] : INVOKE_FAILED]); } // RCT_EXPORT_METHOD(registerAppWithDescription:(NSString *)appid // :(NSString *)appdesc // :(RCTResponseSenderBlock)callback) // { // callback(@[[WXApi registerApp:appid withDescription:appdesc] ? [NSNull null] : INVOKE_FAILED]); // } RCT_EXPORT_METHOD(isWXAppInstalled:(RCTResponseSenderBlock)callback) { callback(@[[NSNull null], @([WXApi isWXAppInstalled])]); } RCT_EXPORT_METHOD(isWXAppSupportApi:(RCTResponseSenderBlock)callback) { callback(@[[NSNull null], @([WXApi isWXAppSupportApi])]); } RCT_EXPORT_METHOD(getWXAppInstallUrl:(RCTResponseSenderBlock)callback) { callback(@[[NSNull null], [WXApi getWXAppInstallUrl]]); } RCT_EXPORT_METHOD(getApiVersion:(RCTResponseSenderBlock)callback) { callback(@[[NSNull null], [WXApi getApiVersion]]); } RCT_EXPORT_METHOD(openWXApp:(RCTResponseSenderBlock)callback) { callback(@[([WXApi openWXApp] ? [NSNull null] : INVOKE_FAILED)]); } RCT_EXPORT_METHOD(sendRequest:(NSString *)openid :(RCTResponseSenderBlock)callback) { BaseReq* req = [[BaseReq alloc] init]; req.openID = openid; // callback(@[[WXApi sendReq:req] ? [NSNull null] : INVOKE_FAILED]); void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } RCT_EXPORT_METHOD(sendAuthRequest:(NSString *)scope :(NSString *)state :(RCTResponseSenderBlock)callback) { SendAuthReq* req = [[SendAuthReq alloc] init]; req.scope = scope; req.state = state; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController; [WXApi sendAuthReq:req viewController:rootViewController delegate:self completion:completion]; } RCT_EXPORT_METHOD(sendSuccessResponse:(RCTResponseSenderBlock)callback) { BaseResp* resp = [[BaseResp alloc] init]; resp.errCode = WXSuccess; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendResp:resp completion:completion]; // callback(@[[WXApi sendResp:resp] ? [NSNull null] : INVOKE_FAILED]); } RCT_EXPORT_METHOD(sendErrorCommonResponse:(NSString *)message :(RCTResponseSenderBlock)callback) { BaseResp* resp = [[BaseResp alloc] init]; resp.errCode = WXErrCodeCommon; resp.errStr = message; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendResp:resp completion:completion]; // callback(@[[WXApi sendResp:resp] ? [NSNull null] : INVOKE_FAILED]); } RCT_EXPORT_METHOD(sendErrorUserCancelResponse:(NSString *)message :(RCTResponseSenderBlock)callback) { BaseResp* resp = [[BaseResp alloc] init]; resp.errCode = WXErrCodeUserCancel; resp.errStr = message; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendResp:resp completion:completion]; // callback(@[[WXApi sendResp:resp] ? [NSNull null] : INVOKE_FAILED]); } // 分享文本 RCT_EXPORT_METHOD(shareText:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = YES; req.text = data[@"text"]; req.scene = [data[@"scene"] intValue]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 选择发票 RCT_EXPORT_METHOD(chooseInvoice:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { WXChooseInvoiceReq *req = [[WXChooseInvoiceReq alloc] init]; req.appID = self.appId; req.timeStamp = [data[@"timeStamp"] intValue]; req.nonceStr = data[@"nonceStr"]; req.cardSign = data[@"cardSign"]; req.signType = data[@"signType"]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 分享文件 RCT_EXPORT_METHOD(shareFile:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { NSString *url = data[@"url"]; WXFileObject *file = [[WXFileObject alloc] init]; file.fileExtension = data[@"ext"]; NSData *fileData = [NSData dataWithContentsOfURL:[NSURL URLWithString: url]]; file.fileData = fileData; WXMediaMessage *message = [WXMediaMessage message]; message.title = data[@"title"]; message.mediaObject = file; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = [data[@"scene"] intValue]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 分享图片 RCT_EXPORT_METHOD(shareImage:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { NSString *imageUrl = data[@"imageUrl"]; if (imageUrl == NULL || [imageUrl isEqual:@""]) { callback([NSArray arrayWithObject:@"shareImage: The value of ImageUrl cannot be empty."]); return; } NSRange range = [imageUrl rangeOfString:@"."]; if ( range.length == 0) { callback([NSArray arrayWithObject:@"shareImage: ImageUrl value, Could not find file suffix."]); return; } // 根据路径下载图片 UIImage *image = [self getImageFromURL:imageUrl]; // 从 UIImage 获取图片数据 NSData *imageData = UIImageJPEGRepresentation(image, 1); // 用图片数据构建 WXImageObject 对象 WXImageObject *imageObject = [WXImageObject object]; imageObject.imageData = imageData; WXMediaMessage *message = [WXMediaMessage message]; // 利用原图压缩出缩略图,确保缩略图大小不大于32KB message.thumbData = [self compressImage: image toByte:32678]; message.mediaObject = imageObject; message.title = data[@"title"]; message.description = data[@"description"]; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = [data[@"scene"] intValue]; // [WXApi sendReq:req]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 分享本地图片 RCT_EXPORT_METHOD(shareLocalImage:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { NSString *imageUrl = data[@"imageUrl"]; if (imageUrl == NULL || [imageUrl isEqual:@""]) { callback([NSArray arrayWithObject:@"shareLocalImage: The value of ImageUrl cannot be empty."]); return; } NSRange range = [imageUrl rangeOfString:@"."]; if ( range.length == 0) { callback([NSArray arrayWithObject:@"shareLocalImage: ImageUrl value, Could not find file suffix."]); return; } // 根据路径下载图片 UIImage *image = [UIImage imageWithContentsOfFile:imageUrl]; // 从 UIImage 获取图片数据 NSData *imageData = UIImageJPEGRepresentation(image, 1); // 用图片数据构建 WXImageObject 对象 WXImageObject *imageObject = [WXImageObject object]; imageObject.imageData = imageData; WXMediaMessage *message = [WXMediaMessage message]; // 利用原图压缩出缩略图,确保缩略图大小不大于32KB message.thumbData = [self compressImage: image toByte:32678]; message.mediaObject = imageObject; message.title = data[@"title"]; message.description = data[@"description"]; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = [data[@"scene"] intValue]; // [WXApi sendReq:req]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 分享音乐 RCT_EXPORT_METHOD(shareMusic:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { WXMusicObject *musicObject = [WXMusicObject object]; musicObject.musicUrl = data[@"musicUrl"]; musicObject.musicLowBandUrl = data[@"musicLowBandUrl"]; musicObject.musicDataUrl = data[@"musicDataUrl"]; musicObject.musicLowBandDataUrl = data[@"musicLowBandDataUrl"]; WXMediaMessage *message = [WXMediaMessage message]; message.title = data[@"title"]; message.description = data[@"description"]; NSString *thumbImageUrl = data[@"thumbImageUrl"]; if (thumbImageUrl != NULL && ![thumbImageUrl isEqual:@""]) { // 根据路径下载图片 UIImage *image = [self getImageFromURL:thumbImageUrl]; message.thumbData = [self compressImage: image toByte:32678]; } message.mediaObject = musicObject; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = [data[@"scene"] intValue]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 分享视频 RCT_EXPORT_METHOD(shareVideo:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { WXVideoObject *videoObject = [WXVideoObject object]; videoObject.videoUrl = data[@"videoUrl"]; videoObject.videoLowBandUrl = data[@"videoLowBandUrl"]; WXMediaMessage *message = [WXMediaMessage message]; message.title = data[@"title"]; message.description = data[@"description"]; NSString *thumbImageUrl = data[@"thumbImageUrl"]; if (thumbImageUrl != NULL && ![thumbImageUrl isEqual:@""]) { UIImage *image = [self getImageFromURL:thumbImageUrl]; message.thumbData = [self compressImage: image toByte:32678]; } message.mediaObject = videoObject; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = [data[@"scene"] intValue]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 分享网页 RCT_EXPORT_METHOD(shareWebpage:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { WXWebpageObject *webpageObject = [WXWebpageObject object]; webpageObject.webpageUrl = data[@"webpageUrl"]; WXMediaMessage *message = [WXMediaMessage message]; message.title = data[@"title"]; message.description = data[@"description"]; NSString *thumbImageUrl = data[@"thumbImageUrl"]; if (thumbImageUrl != NULL && ![thumbImageUrl isEqual:@""]) { UIImage *image = [self getImageFromURL:thumbImageUrl]; message.thumbData = [self compressImage: image toByte:32678]; } message.mediaObject = webpageObject; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = [data[@"scene"] intValue]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 分享小程序 RCT_EXPORT_METHOD(shareMiniProgram:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { WXMiniProgramObject *object = [WXMiniProgramObject object]; object.webpageUrl = data[@"webpageUrl"]; object.userName = data[@"userName"]; object.path = data[@"path"]; NSString *hdImageUrl = data[@"hdImageUrl"]; if (hdImageUrl != NULL && ![hdImageUrl isEqual:@""]) { UIImage *image = [self getImageFromURL:hdImageUrl]; // 压缩图片到小于128KB object.hdImageData = [self compressImage: image toByte:131072]; } object.withShareTicket = data[@"withShareTicket"]; int miniProgramType = [data[@"miniProgramType"] integerValue]; object.miniProgramType = [self integerToWXMiniProgramType:miniProgramType]; WXMediaMessage *message = [WXMediaMessage message]; message.title = data[@"title"]; message.description = data[@"description"]; //兼容旧版本节点的图片,小于32KB,新版本优先 //使用WXMiniProgramObject的hdImageData属性 NSString *thumbImageUrl = data[@"thumbImageUrl"]; if (thumbImageUrl != NULL && ![thumbImageUrl isEqual:@""]) { UIImage *image = [self getImageFromURL:thumbImageUrl]; message.thumbData = [self compressImage: image toByte:32678]; } message.mediaObject = object; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = [data[@"scene"] integerValue]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } // 一次性订阅消息 RCT_EXPORT_METHOD(subscribeMessage:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { WXSubscribeMsgReq *req = [[WXSubscribeMsgReq alloc] init]; req.scene = [data[@"scene"] integerValue]; req.templateId = data[@"templateId"]; req.reserved = data[@"reserved"]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; } RCT_EXPORT_METHOD(launchMiniProgram:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { WXLaunchMiniProgramReq *launchMiniProgramReq = [WXLaunchMiniProgramReq object]; // 拉起的小程序的username launchMiniProgramReq.userName = data[@"userName"]; // 拉起小程序页面的可带参路径,不填默认拉起小程序首页 launchMiniProgramReq.path = data[@"path"]; // 拉起小程序的类型 int miniProgramType = [data[@"miniProgramType"] integerValue]; launchMiniProgramReq.miniProgramType = [self integerToWXMiniProgramType:miniProgramType]; // launchMiniProgramReq.miniProgramType = [data[@"miniProgramType"] integerValue]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:launchMiniProgramReq completion:completion]; // BOOL success = [WXApi sendReq:launchMiniProgramReq]; // callback(@[success ? [NSNull null] : INVOKE_FAILED]); } RCT_EXPORT_METHOD(pay:(NSDictionary *)data :(RCTResponseSenderBlock)callback) { PayReq* req = [PayReq new]; req.partnerId = data[@"partnerId"]; req.prepayId = data[@"prepayId"]; req.nonceStr = data[@"nonceStr"]; req.timeStamp = [data[@"timeStamp"] unsignedIntValue]; req.package = data[@"package"]; req.sign = data[@"sign"]; void ( ^ completion )( BOOL ); completion = ^( BOOL success ) { callback(@[success ? [NSNull null] : INVOKE_FAILED]); return; }; [WXApi sendReq:req completion:completion]; // BOOL success = [WXApi sendReq:req]; // callback(@[success ? [NSNull null] : INVOKE_FAILED]); } // 跳转微信客服 RCT_EXPORT_METHOD(openCustomerServiceChat:(NSString *)kfUrl :(RCTResponseSenderBlock)callback) { WXOpenCustomerServiceReq *req = [[WXOpenCustomerServiceReq alloc] init]; req.corpid = self.appId; //企业ID req.url = kfUrl; //客服URL [WXApi sendReq:req completion:nil]; } #pragma mark - wx callback -(void) onReq:(BaseReq*)req { if ([req isKindOfClass:[LaunchFromWXReq class]]) { LaunchFromWXReq *launchReq = req; NSString *appParameter = launchReq.message.messageExt; NSMutableDictionary *body = @{@"errCode":@0}.mutableCopy; body[@"type"] = @"LaunchFromWX.Req"; body[@"lang"] = launchReq.lang; body[@"country"] = launchReq.country; body[@"extMsg"] = appParameter; [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXEventNameWeChatReq body:body]; } } -(void) onResp:(BaseResp*)resp { if([resp isKindOfClass:[SendMessageToWXResp class]]) { SendMessageToWXResp *r = (SendMessageToWXResp *)resp; NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy; body[@"errStr"] = r.errStr; body[@"lang"] = r.lang; body[@"country"] =r.country; body[@"type"] = @"SendMessageToWX.Resp"; [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXEventName body:body]; } else if ([resp isKindOfClass:[SendAuthResp class]]) { SendAuthResp *r = (SendAuthResp *)resp; NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy; body[@"errStr"] = r.errStr; body[@"state"] = r.state; body[@"lang"] = r.lang; body[@"country"] =r.country; body[@"type"] = @"SendAuth.Resp"; if (resp.errCode == WXSuccess) { if (self.appId && r) { // ios第一次获取不到appid会卡死,加个判断OK [body addEntriesFromDictionary:@{@"appid":self.appId, @"code":r.code}]; [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXEventName body:body]; } } else { [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXEventName body:body]; } } else if ([resp isKindOfClass:[PayResp class]]) { PayResp *r = (PayResp *)resp; NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy; body[@"errStr"] = r.errStr; body[@"type"] = @(r.type); body[@"returnKey"] =r.returnKey; body[@"type"] = @"PayReq.Resp"; [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXEventName body:body]; } else if ([resp isKindOfClass:[WXLaunchMiniProgramResp class]]){ WXLaunchMiniProgramResp *r = (WXLaunchMiniProgramResp *)resp; NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy; body[@"errStr"] = r.errStr; body[@"extMsg"] = r.extMsg; body[@"type"] = @"WXLaunchMiniProgramReq.Resp"; [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXEventName body:body]; } else if ([resp isKindOfClass:[WXChooseInvoiceResp class]]){ WXChooseInvoiceResp *r = (WXChooseInvoiceResp *)resp; NSMutableDictionary *body = @{@"errCode":@(r.errCode)}.mutableCopy; body[@"errStr"] = r.errStr; NSMutableArray *arr = [[NSMutableArray alloc] init]; for (WXCardItem* cardItem in r.cardAry) { NSMutableDictionary *item = @{@"cardId":cardItem.cardId,@"encryptCode":cardItem.encryptCode,@"appId":cardItem.appID}.mutableCopy; [arr addObject:item]; } body[@"cards"] = arr; body[@"type"] = @"WXChooseInvoiceResp.Resp"; [self.bridge.eventDispatcher sendDeviceEventWithName:RCTWXEventName body:body]; } } -(WXMiniProgramType) integerToWXMiniProgramType:(int)value { WXMiniProgramType type = WXMiniProgramTypeRelease; switch (value) { case 0: type = WXMiniProgramTypeRelease; break; case 1: type = WXMiniProgramTypeTest; break; case 2: type = WXMiniProgramTypePreview; break; } return type; } @end