// Pods for WXPay
// 1. pod 'WechatOpenSDK'
// 2.用到一个 XMLDictionary 工具类
// info.plist 打开方式 Source Code
// 设置 CFBundleURLTypes 如下注意 APPid写自己的
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>weixin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>微信APPid</string>
</array>
</dict>
</array>
#import <Foundation/Foundation.h>
#import "WXApi.h"
/** 微信appid */
#define WXAPPID @""
/** 微信支付商户号 */
#define MCH_ID @"" // 微信支付商户号 partnerId
/** 交易结果通知网站此处用于测试,随意填写,正式使用时填写正确网站 */
#define NOTIFY_URL @"http://wxpay.weixin.qq.com/pub_v2/pay/notify.v2.php"
/** 商户密钥 partner */
#define kEY @""
NS_ASSUME_NONNULL_BEGIN
//0 = 好友列表 1 = 朋友圈 2 = 收藏
typedef NS_ENUM(NSUInteger, WechtShareType) {
WechtShareTypeFriends = 0,
WechtShareTypePengYouQuan,
WechtShareTypeFavorites
};
@interface WeChatManager : NSObject
<
WXApiDelegate
>
+(instancetype)shareManager;
/**
* 支付
* https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
*/
-(void)weiXinPayName:(NSString *)name money:(CGFloat)money ;
/** 分享 */
-(void)weChatShare:(NSString *)tittle
content:(NSString *)content
url:(NSString *)url
image:(UIImage *)image
scenetype:(WechtShareType)scenetype;
/**
*登录
* https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419317851&token=&lang=zh_CN
*/
-(void)weChatLogin;
@end
NS_ASSUME_NONNULL_END
#import "WeChatManager.h"
#import <CommonCrypto/CommonDigest.h>
#import "XMLDictionary.h"
/** 统一下单地址 */
#define HTTP @"https://api.mch.weixin.qq.com/pay/unifiedorder"
@interface WeChatManager ()
/**
* 预支付订单号
*/
@property (nonatomic,strong)NSString *out_trade_no;
/**
* 随机数 时间戳
*/
@property (nonatomic,strong)NSString *nonceStr;
/**
* 预支付订单返回值
*/
@property (nonatomic,strong)NSDictionary *backDic;
@end
@implementation WeChatManager
+ (instancetype)shareManager
{
static WeChatManager * manager =nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (manager == nil)
{
manager = [[self alloc]init];
}
});
return manager;
}
#pragma mark ————————— 充值 —————————————
- (void)weiXinPayName:(NSString *)name money:(CGFloat)money
{
[WXApi registerApp:WXAPPID];//注册appid
_nonceStr = [self md5:[self generateTradeNO]] ; // 随机数MD5
_out_trade_no = [self genTimeStamp]; // 商户订单号(时间戳)
NSString *appid, *mch_id, *nonce_str, *sign, *body, *out_trade_no, *total_fee, *spbill_create_ip, *notify_url, *trade_type, *partner;
//应用APPID
appid = WXAPPID;
//微信支付商户号
mch_id = MCH_ID;
///产生随机字符串
nonce_str = _nonceStr;
// 商品名称
body = name;
//商户订单号
out_trade_no = _out_trade_no;
// 交易价格1表示0.01元,10表示0.1元
NSInteger JG = [[NSString stringWithFormat:@"%f", money * 100] integerValue];
// NSLog(@"money = %zd",JG);
total_fee = [NSString stringWithFormat:@"%zd", JG];
//获取本机IP地址,请再wifi环境下测试,否则获取的ip地址为error,正确格式应该是8.8.8.8
spbill_create_ip = @"192.168.23.138" ;
//交易结果通知网站此处用于测试,随意填写,正式使用时填写正确网站
notify_url = NOTIFY_URL;
// 移动端类型
trade_type = @"APP";
//商户密钥
partner = kEY;
sign = [self getSignForMD5ppid:appid
mch_id:mch_id
nonce_str:nonce_str
partner_id:partner
body:body
out_trade_no:out_trade_no
total_fee:total_fee
spbill_create_ip:spbill_create_ip
notify_url:notify_url
trade_type:trade_type];
NSLog(@"sign = %@",sign);
NSString *xmlData = [self getXmlFromAppid:appid
mch_id:mch_id
nonce_str:nonce_str
sign:sign
body:body
out_trade_no:out_trade_no
total_fee:total_fee
spbill_create_ip:spbill_create_ip
notify_url:notify_url
trade_type:trade_type];
NSLog(@"xmlData \n %@",xmlData);
[self http:HTTP data:xmlData];
}
#pragma mark ————————— 设置参数并转化成xml格式 —————————————
-(NSString *)getXmlFromAppid:(NSString *)appid
mch_id:(NSString *)mch_id
nonce_str:(NSString *)nonce_str
sign:(NSString *)sign
body:(NSString *)body
out_trade_no:(NSString *)out_trade_no
total_fee:(NSString *)total_fee
spbill_create_ip:(NSString *)spbill_create_ip
notify_url:(NSString *)notify_url
trade_type:(NSString *)trade_type
{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setValue:appid forKey:@"appid"];//公众账号ID
[dic setValue:mch_id forKey:@"mch_id"];//商户号
[dic setValue:nonce_str forKey:@"nonce_str"];//随机字符串
[dic setValue:sign forKey:@"sign"];//签名
[dic setValue:body forKey:@"body"];//商品描述
[dic setValue:out_trade_no forKey:@"out_trade_no"];//订单号
[dic setValue:total_fee forKey:@"total_fee"];//金额
[dic setValue:spbill_create_ip forKey:@"spbill_create_ip"];//终端IP
[dic setValue:notify_url forKey:@"notify_url"];//通知地址
[dic setValue:trade_type forKey:@"trade_type"];//交易类型
return [dic XMLString];
}
#pragma mark ————————— 组合sign签名必要参数 —————————————
-(NSString *)getSignForMD5ppid:(NSString *)appid_key
mch_id:(NSString *)mch_id_key
nonce_str:(NSString *)noce_str_key
partner_id:(NSString *)partner_id
body:(NSString *)body_key
out_trade_no:(NSString *)out_trade_no_key
total_fee:(NSString *)total_fee_key
spbill_create_ip:(NSString *)spbill_create_ip_key
notify_url:(NSString *)notify_url_key
trade_type:(NSString *)trade_type_key
{
NSMutableDictionary *dic = [NSMutableDictionary dictionary];
[dic setValue:appid_key forKey:@"appid"];
[dic setValue:mch_id_key forKey:@"mch_id"];
[dic setValue:noce_str_key forKey:@"nonce_str"];
[dic setValue:body_key forKey:@"body"];
[dic setValue:out_trade_no_key forKey:@"out_trade_no"];
[dic setValue:total_fee_key forKey:@"total_fee"];
[dic setValue:spbill_create_ip_key forKey:@"spbill_create_ip"];
[dic setValue:notify_url_key forKey:@"notify_url"];
[dic setValue:trade_type_key forKey:@"trade_type"];
return [self createMd5Sign:dic];
}
#pragma mark ————————— 创建Md5格式的 sign 签名 —————————————
- (NSString*) createMd5Sign:(NSMutableDictionary*)dict
{
NSMutableString *contentString =[NSMutableString string];
NSArray *keys = [dict allKeys];
//按字母顺序排序
NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//拼接字符串
for (NSString *categoryId in sortedArray)
{
if (![[dict objectForKey:categoryId] isEqualToString:@""]&&
![[dict objectForKey:categoryId] isEqualToString:@"sign"]&&
![[dict objectForKey:categoryId] isEqualToString:@"key"] )
{
[contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]];
}
}
//添加商户密钥key字段
[contentString appendFormat:@"key=%@", kEY];
//得到MD5 sign签名
NSString *md5Sign =[self md5:contentString];
return md5Sign;
}
#pragma mark ————————— 预支付订单号请求 —————————————
- (void)http:(NSString *)url data:(NSString *)send
{
__weak __typeof(self) weakself= self;
// 1 创建URL
NSURL *urls = [NSURL URLWithString:url];
// 2 创建请求对象
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:urls];
[request setHTTPMethod:@"POST"];
// 3 设置body
NSData *bodyData = [send dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:bodyData];
// 4 创建会话
NSURLSession *session = [NSURLSession sharedSession];
// 5 创建数据请求任务
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
NSString * newStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"预支付订单号请求结果XML=%@",newStr);
NSDictionary *xmlDoc = [NSDictionary dictionaryWithXMLString:newStr];
NSLog(@"预支付订单号请求结果%@",xmlDoc);
[weakself WeiXinPay:xmlDoc];
}];
// 4.3、 启动任务
[task resume];
}
#pragma mark ————————— 调用微信支付 —————————————
- (void)WeiXinPay:(NSDictionary *)xmlDoc {
//判断返回的许可
if ([[xmlDoc objectForKey:@"result_code"] isEqualToString:@"SUCCESS"] &&
[[xmlDoc objectForKey:@"return_code"] isEqualToString:@"SUCCESS"])
{
//发起微信支付,设置参数
PayReq *request = [[PayReq alloc] init];
request.partnerId = [xmlDoc objectForKey:@"mch_id"];
request.prepayId = [xmlDoc objectForKey:@"prepay_id"];
request.package = @"Sign=WXPay";
request.nonceStr = [xmlDoc objectForKey:@"nonce_str"];
UInt32 timestamp = (UInt32)time(0);
request.timeStamp = timestamp;
request.sign = [self createMD5SingForPay:WXAPPID
partnerid:request.partnerId
prepayid:request.prepayId
package:request.package
noncestr:request.nonceStr
timestamp:request.timeStamp];
[self saveOrder:xmlDoc];
// 调用微信
[WXApi sendReq:request];
NSLog(@"\n appid=%@ \n partid=%@ \n prepayid=%@ \n noncestr=%@ \n timestamp=%ld \n package=%@ \n sign=%@",[xmlDoc objectForKey:@"appid"],request.partnerId,request.prepayId,request.nonceStr,(long)request.timeStamp,request.package,request.sign );
}
else
{
NSLog(@"参数不正确,请检查参数");
}
}
#pragma mark ————————— 微信分享 —————————————
-(void)weChatShare:(NSString *)tittle
content:(NSString *)content
url:(NSString *)url
image:(UIImage *)image
scenetype:(WechtShareType)scenetype
{
[WXApi registerApp:WXAPPID];//注册appid
// 无图分享
//创建发送对象实例
SendMessageToWXReq *sendReq = [[SendMessageToWXReq alloc] init];
sendReq.bText = NO;//不使用文本信息
sendReq.scene = scenetype;//0 = 好友列表 1 = 朋友圈 2 = 收藏
//创建分享内容对象
WXMediaMessage *urlMessage = [WXMediaMessage message];
if (tittle.length > 0)
{
urlMessage.title = tittle;//分享标题
}
if (content.length > 0)
{
urlMessage.description = content;//分享描述
}
if (url)
{
// [urlMessage setThumbImage: [UIImage imageNamed:@"ic_launcher"]];
[urlMessage setThumbImage:[self imageByScalingAndCroppingForSize:CGSizeMake(30, 30) withSourceImage:image]];
//创建多媒体对象
WXWebpageObject *webObj = [WXWebpageObject object];
webObj.webpageUrl = url;//分享链接
//完成发送对象实例
urlMessage.mediaObject = webObj;
}
else
{
if (image)
{
//创建分享内容对象
[urlMessage setThumbImage:image]; //分享图片,使用SDK的setThumbImage方法可压缩图片大小
//完成发送对象实例
urlMessage.mediaObject = urlMessage;
}
}
sendReq.message = urlMessage;
//发送分享信息
[WXApi sendReq:sendReq];
}
#pragma mark ————————— 微信登录 —————————————
-(void)weChatLogin
{
[WXApi registerApp:WXAPPID];//注册appid
SendAuthReq* req =[[SendAuthReq alloc ] init];
req.scope = @"snsapi_userinfo";// snsapi_userinfo // snsapi_userinfo,snsapi_base
req.state = @"0744" ;
if ([WXApi sendReq:req])
{
NSLog(@"YES");
}
else
{
NSLog(@"NO");
}
}
#pragma mark -
#pragma mark WXApiDelegate
#pragma mark ————————— 收到微信的回应 分享 支付 第三方登录后 —————————————
/*
* @brief 发送一个sendReq后,收到微信的回应
* 收到一个来自微信的处理结果。调用一次sendReq后会收到onResp。
* 可能收到的处理结果有SendMessageToWXResp、SendAuthResp等。
*/
- (void)onResp:(BaseResp*)resp
{
NSLog(@"收到微信的回应\n %d",resp.errCode);
PayResp *response = (PayResp*)resp;
// 支付结果回调
if ([resp isKindOfClass:[PayResp class]])
{
switch(response.errCode){
case WXSuccess:
{
NSLog(@"支付成功");
}
break;
default:
{
NSLog(@"支付失败,retcode=%d",resp.errCode);
}
break;
}
}
// 第三方登陆
if ([resp isKindOfClass:[SendAuthResp class]])
{
SendAuthResp *aresp = (SendAuthResp *)resp;
switch (aresp.errCode) {
case WXSuccess:
{
// 微信授权登陆 获取令牌 令牌获取成功后直接获取用户信息
NSString *code = aresp.code ;
NSString *state = aresp.state;
NSLog(@"授权登陆获取到用户信息 %@ %@",code,state);
}
break;
case WXErrCodeUserCancel:
{
NSLog(@"用户取消微信授权登陆");
}
break;
case WXErrCodeAuthDeny:
{
NSLog(@"用户拒绝微信授权登陆");
}
break;
default:
{
NSLog(@"消微信授权登陆失败");
}
break;
}
}
// 微信分享 SendMessageToWXResp
if ([resp isKindOfClass:[SendMessageToWXResp class]]) {
SendMessageToWXResp *saresp = (SendMessageToWXResp *)resp;
NSLog(@"分享完回调 = %d",saresp.errCode);
switch (saresp.errCode) {
case WXSuccess:
{
NSLog(@"分享成功");
}
break;
case WXErrCodeUserCancel:
{
NSLog(@"取消分享");
}
break;
default:
{
NSLog(@"取消失败");
}
break;
}
}
}
#pragma mark ————————— 收到一个来自微信的请求 —————————————
/*
*! @brief 收到一个来自微信的请求,第三方应用程序处理完后调用sendResp向微信发送结果
* 收到一个来自微信的请求,异步处理完成后必须调用sendResp发送处理结果给微信。
* 可能收到的请求有GetMessageFromWXReq、ShowMessageFromWXReq等。
* req 具体请求内容,是自动释放的
*/
- (void)onReq:(BaseReq*)req
{
NSLog(@"收到一个来自微信的请求 = %@",req.openID);
}
#pragma mark -
#pragma mark ————————— 保存订单信息 —————————————
-(void)saveOrder:(NSDictionary *)xmlDoc
{
NSUserDefaults * us = [NSUserDefaults standardUserDefaults];
[us setObject:xmlDoc forKey:[NSString stringWithFormat:@"用户名+%@",[NSDate date]]];
[us synchronize];
}
#pragma mark ————————— 创建发起支付时的sige签名 —————————————
- (NSString *)createMD5SingForPay:(NSString *)appid_key
partnerid:(NSString *)partnerid_key
prepayid:(NSString *)prepayid_key
package:(NSString *)package_key
noncestr:(NSString *)noncestr_key
timestamp:(UInt32)timestamp_key
{
NSMutableDictionary *signParams = [NSMutableDictionary dictionary];
[signParams setObject:appid_key forKey:@"appid"];
[signParams setObject:noncestr_key forKey:@"noncestr"];
[signParams setObject:package_key forKey:@"package"];
[signParams setObject:partnerid_key forKey:@"partnerid"];
[signParams setObject:prepayid_key forKey:@"prepayid"];
[signParams setObject:[NSString stringWithFormat:@"%u",timestamp_key] forKey:@"timestamp"];
NSMutableString *contentString =[NSMutableString string];
NSArray *keys = [signParams allKeys];
//按字母顺序排序
NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1 compare:obj2 options:NSNumericSearch];
}];
//拼接字符串
for (NSString *categoryId in sortedArray)
{
if (![[signParams objectForKey:categoryId] isEqualToString:@""]&&
![[signParams objectForKey:categoryId] isEqualToString:@"sign"]&&
![[signParams objectForKey:categoryId] isEqualToString:@"key"])
{
[contentString appendFormat:@"%@=%@&", categoryId, [signParams objectForKey:categoryId]];
}
}
//添加商户密钥key字段
[contentString appendFormat:@"key=%@", kEY];
NSString *result = [self md5:contentString];
return result;
}
#pragma mark ————————— 获取时间戳 —————————————
- (NSString *)genTimeStamp
{
NSString* timeString = [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970]];
NSLog(@"timeString = %@",timeString);
return timeString;
}
#pragma mark ————————— 将订单号使用md5加密 —————————————
-(NSString *) md5:(NSString *)str
{
const char *cStr = [str UTF8String];
unsigned char result[16]= "0123456789abcdef";
CC_MD5(cStr, (CC_LONG)strlen(cStr), result); // This is the md5 call
return [NSString stringWithFormat:
@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1], result[2], result[3],
result[4], result[5], result[6], result[7],
result[8], result[9], result[10], result[11],
result[12], result[13], result[14], result[15]
];
}
#pragma mark ————————— 产生随机订单号 —————————————
- (NSString *)generateTradeNO {
static int kNumber = 5;
NSString *sourceStr = @"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
NSMutableString *resultStr = [[NSMutableString alloc] init];
srand(time(0)); // 此行代码有警告:
for (int i = 0; i < kNumber; i++)
{
unsigned index = rand() % [sourceStr length];
NSString *oneStr = [sourceStr substringWithRange:NSMakeRange(index, 1)];
[resultStr appendString:oneStr];
}
NSString *str = [NSString stringWithFormat:@"%@%@",[self genTimeStamp],resultStr];
return str;
}
/**
* 图片压缩到指定大小
* @param targetSize 目标图片的大小
* @param sourceImage 源图片
* @return 目标图片
*/
- (UIImage*)imageByScalingAndCroppingForSize:(CGSize)targetSize withSourceImage:(UIImage *)sourceImage
{
UIImage *newImage = nil;
CGSize imageSize = sourceImage.size;
CGFloat width = imageSize.width;
CGFloat height = imageSize.height;
CGFloat targetWidth = targetSize.width;
CGFloat targetHeight = targetSize.height;
CGFloat scaleFactor = 0.0;
CGFloat scaledWidth = targetWidth;
CGFloat scaledHeight = targetHeight;
CGPoint thumbnailPoint = CGPointMake(0.0,0.0);
if (CGSizeEqualToSize(imageSize, targetSize) == NO)
{
CGFloat widthFactor = targetWidth / width;
CGFloat heightFactor = targetHeight / height;
if (widthFactor > heightFactor)
scaleFactor = widthFactor; // scale to fit height
else
scaleFactor = heightFactor; // scale to fit width
scaledWidth= width * scaleFactor;
scaledHeight = height * scaleFactor;
// center the image
if (widthFactor > heightFactor)
{
thumbnailPoint.y = (targetHeight - scaledHeight) * 0.5;
}
else if (widthFactor < heightFactor)
{
thumbnailPoint.x = (targetWidth - scaledWidth) * 0.5;
}
}
UIGraphicsBeginImageContext(targetSize); // this will crop
CGRect thumbnailRect = CGRectZero;
thumbnailRect.origin = thumbnailPoint;
thumbnailRect.size.width= scaledWidth;
thumbnailRect.size.height = scaledHeight;
[sourceImage drawInRect:thumbnailRect];
newImage = UIGraphicsGetImageFromCurrentImageContext();
if(newImage == nil)
NSLog(@"could not scale image");
//pop the context to get back to the default
UIGraphicsEndImageContext();
return newImage;
}
/*
#warning 微信官方参数测试
- (NSString *)jumpToBizPay {
//============================================================
// V3&V4支付流程实现
// 注意:参数配置请查看服务器端Demo
// 更新时间:2015年11月20日
//============================================================
NSString *urlString = @"http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=ios";
//解析服务端返回json数据
NSError *error;
//加载一个NSURL对象
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
//将请求的url数据放到NSData对象中
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
if ( response != nil) {
NSMutableDictionary *dict = NULL;
//IOS5自带解析类NSJSONSerialization从response中解析出数据放到字典中
dict = [NSJSONSerialization JSONObjectWithData:response options:NSJSONReadingMutableLeaves error:&error];
NSLog(@"url:%@",urlString);
if(dict != nil){
NSMutableString *retcode = [dict objectForKey:@"retcode"];
if (retcode.intValue == 0){
NSMutableString *stamp = [dict objectForKey:@"timestamp"];
//调起微信支付
PayReq* req = [[PayReq alloc] init];
req.partnerId = [dict objectForKey:@"partnerid"];
req.prepayId = [dict objectForKey:@"prepayid"];
req.nonceStr = [dict objectForKey:@"noncestr"];
req.timeStamp = stamp.intValue;
req.package = [dict objectForKey:@"package"];
req.sign = [dict objectForKey:@"sign"];
[WXApi sendReq:req];
//日志输出
NSLog(@"\nappid=%@\npartid=%@\nprepayid=%@\nnoncestr=%@\ntimestamp=%ld\npackage=%@\nsign=%@",[dict objectForKey:@"appid"],req.partnerId,req.prepayId,req.nonceStr,(long)req.timeStamp,req.package,req.sign );
return @"";
}else{
return [dict objectForKey:@"retmsg"];
}
}else{
return @"服务器返回错误,未获取到json对象";
}
}else{
return @"服务器返回错误";
}
}
*/
@end
#import <Foundation/Foundation.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis"
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, XMLDictionaryAttributesMode)
{
XMLDictionaryAttributesModePrefixed = 0, //default
XMLDictionaryAttributesModeDictionary,
XMLDictionaryAttributesModeUnprefixed,
XMLDictionaryAttributesModeDiscard
};
typedef NS_ENUM(NSInteger, XMLDictionaryNodeNameMode)
{
XMLDictionaryNodeNameModeRootOnly = 0, //default
XMLDictionaryNodeNameModeAlways,
XMLDictionaryNodeNameModeNever
};
static NSString *const XMLDictionaryAttributesKey = @"__attributes";
static NSString *const XMLDictionaryCommentsKey = @"__comments";
static NSString *const XMLDictionaryTextKey = @"__text";
static NSString *const XMLDictionaryNodeNameKey = @"__name";
static NSString *const XMLDictionaryAttributePrefix = @"_";
@interface XMLDictionaryParser : NSObject <NSCopying>
+ (XMLDictionaryParser *)sharedInstance;
@property (nonatomic, assign) BOOL collapseTextNodes; // defaults to YES
@property (nonatomic, assign) BOOL stripEmptyNodes; // defaults to YES
@property (nonatomic, assign) BOOL trimWhiteSpace; // defaults to YES
@property (nonatomic, assign) BOOL alwaysUseArrays; // defaults to NO
@property (nonatomic, assign) BOOL preserveComments; // defaults to NO
@property (nonatomic, assign) BOOL wrapRootNode; // defaults to NO
@property (nonatomic, assign) XMLDictionaryAttributesMode attributesMode;
@property (nonatomic, assign) XMLDictionaryNodeNameMode nodeNameMode;
- (nullable NSDictionary<NSString *, id> *)dictionaryWithParser:(NSXMLParser *)parser;
- (nullable NSDictionary<NSString *, id> *)dictionaryWithData:(NSData *)data;
- (nullable NSDictionary<NSString *, id> *)dictionaryWithString:(NSString *)string;
- (nullable NSDictionary<NSString *, id> *)dictionaryWithFile:(NSString *)path;
@end
@interface NSDictionary (XMLDictionary)
+ (nullable NSDictionary<NSString *, id> *)dictionaryWithXMLParser:(NSXMLParser *)parser;
+ (nullable NSDictionary<NSString *, id> *)dictionaryWithXMLData:(NSData *)data;
+ (nullable NSDictionary<NSString *, id> *)dictionaryWithXMLString:(NSString *)string;
+ (nullable NSDictionary<NSString *, id> *)dictionaryWithXMLFile:(NSString *)path;
@property (nonatomic, readonly, copy, nullable) NSDictionary<NSString *, NSString *> *attributes;
@property (nonatomic, readonly, copy, nullable) NSDictionary<NSString *, id> *childNodes;
@property (nonatomic, readonly, copy, nullable) NSArray<NSString *> *comments;
@property (nonatomic, readonly, copy, nullable) NSString *nodeName;
@property (nonatomic, readonly, copy, nullable) NSString *innerText;
@property (nonatomic, readonly, copy) NSString *innerXML;
@property (nonatomic, readonly, copy) NSString *XMLString;
- (nullable NSArray *)arrayValueForKeyPath:(NSString *)keyPath;
- (nullable NSString *)stringValueForKeyPath:(NSString *)keyPath;
- (nullable NSDictionary<NSString *, id> *)dictionaryValueForKeyPath:(NSString *)keyPath;
@end
@interface NSString (XMLDictionary)
@property (nonatomic, readonly, copy) NSString *XMLEncodedString;
@end
NS_ASSUME_NONNULL_END
#pragma GCC diagnostic pop
#import "XMLDictionary.h"
#pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis"
#pragma GCC diagnostic ignored "-Wpartial-availability"
#pragma GCC diagnostic ignored "-Wdirect-ivar-access"
#pragma GCC diagnostic ignored "-Wformat-non-iso"
#pragma GCC diagnostic ignored "-Wgnu"
#import <Availability.h>
#if !__has_feature(objc_arc)
#error This class requires automatic reference counting
#endif
@interface XMLDictionaryParser () <NSXMLParserDelegate>
@property (nonatomic, strong) NSMutableDictionary<NSString *, id> *root;
@property (nonatomic, strong) NSMutableArray *stack;
@property (nonatomic, strong) NSMutableString *text;
@end
@implementation XMLDictionaryParser
+ (XMLDictionaryParser *)sharedInstance
{
static dispatch_once_t once;
static XMLDictionaryParser *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[XMLDictionaryParser alloc] init];
});
return sharedInstance;
}
- (instancetype)init
{
if ((self = [super init]))
{
_collapseTextNodes = YES;
_stripEmptyNodes = YES;
_trimWhiteSpace = YES;
_alwaysUseArrays = NO;
_preserveComments = NO;
_wrapRootNode = NO;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
XMLDictionaryParser *copy = [[[self class] allocWithZone:zone] init];
copy.collapseTextNodes = _collapseTextNodes;
copy.stripEmptyNodes = _stripEmptyNodes;
copy.trimWhiteSpace = _trimWhiteSpace;
copy.alwaysUseArrays = _alwaysUseArrays;
copy.preserveComments = _preserveComments;
copy.attributesMode = _attributesMode;
copy.nodeNameMode = _nodeNameMode;
copy.wrapRootNode = _wrapRootNode;
return copy;
}
- (NSDictionary<NSString *, id> *)dictionaryWithParser:(NSXMLParser *)parser
{
parser.delegate = self;
[parser parse];
id result = _root;
_root = nil;
_stack = nil;
_text = nil;
return result;
}
- (NSDictionary<NSString *, id> *)dictionaryWithData:(NSData *)data
{
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
return [self dictionaryWithParser:parser];
}
- (NSDictionary<NSString *, id> *)dictionaryWithString:(NSString *)string
{
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [self dictionaryWithData:data];
}
- (NSDictionary<NSString *, id> *)dictionaryWithFile:(NSString *)path
{
NSData *data = [NSData dataWithContentsOfFile:path];
return [self dictionaryWithData:data];
}
+ (NSString *)XMLStringForNode:(id)node withNodeName:(NSString *)nodeName
{
if ([node isKindOfClass:[NSArray class]])
{
NSMutableArray<NSString *> *nodes = [NSMutableArray arrayWithCapacity:[node count]];
for (id individualNode in node)
{
[nodes addObject:[self XMLStringForNode:individualNode withNodeName:nodeName]];
}
return [nodes componentsJoinedByString:@"\n"];
}
else if ([node isKindOfClass:[NSDictionary class]])
{
NSDictionary<NSString *, NSString *> *attributes = [(NSDictionary *)node attributes];
NSMutableString *attributeString = [NSMutableString string];
[attributes enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, __unused BOOL *stop) {
[attributeString appendFormat:@" %@=\"%@\"", key.description.XMLEncodedString, value.description.XMLEncodedString];
}];
NSString *innerXML = [node innerXML];
if (innerXML.length)
{
return [NSString stringWithFormat:@"<%1$@%2$@>%3$@</%1$@>", nodeName, attributeString, innerXML];
}
else
{
return [NSString stringWithFormat:@"<%@%@/>", nodeName, attributeString];
}
}
else
{
return [NSString stringWithFormat:@"<%1$@>%2$@</%1$@>", nodeName, [node description].XMLEncodedString];
}
}
- (void)endText
{
if (_trimWhiteSpace)
{
_text = [[_text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] mutableCopy];
}
if (_text.length)
{
NSMutableDictionary *top = _stack.lastObject;
id existing = top[XMLDictionaryTextKey];
if ([existing isKindOfClass:[NSArray class]])
{
[existing addObject:_text];
}
else if (existing)
{
top[XMLDictionaryTextKey] = [@[existing, _text] mutableCopy];
}
else
{
top[XMLDictionaryTextKey] = _text;
}
}
_text = nil;
}
- (void)addText:(NSString *)text
{
if (!_text)
{
_text = [NSMutableString stringWithString:text];
}
else
{
[_text appendString:text];
}
}
- (void)parser:(__unused NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName attributes:(NSDictionary *)attributeDict
{
[self endText];
NSMutableDictionary<NSString *, id> *node = [NSMutableDictionary dictionary];
switch (_nodeNameMode)
{
case XMLDictionaryNodeNameModeRootOnly:
{
if (!_root)
{
node[XMLDictionaryNodeNameKey] = elementName;
}
break;
}
case XMLDictionaryNodeNameModeAlways:
{
node[XMLDictionaryNodeNameKey] = elementName;
break;
}
case XMLDictionaryNodeNameModeNever:
{
break;
}
}
if (attributeDict.count)
{
switch (_attributesMode)
{
case XMLDictionaryAttributesModePrefixed:
{
for (NSString *key in attributeDict)
{
node[[XMLDictionaryAttributePrefix stringByAppendingString:key]] = attributeDict[key];
}
break;
}
case XMLDictionaryAttributesModeDictionary:
{
node[XMLDictionaryAttributesKey] = attributeDict;
break;
}
case XMLDictionaryAttributesModeUnprefixed:
{
[node addEntriesFromDictionary:attributeDict];
break;
}
case XMLDictionaryAttributesModeDiscard:
{
break;
}
}
}
if (!_root)
{
_root = node;
_stack = [NSMutableArray arrayWithObject:node];
if (_wrapRootNode)
{
_root = [NSMutableDictionary dictionaryWithObject:_root forKey:elementName];
[_stack insertObject:_root atIndex:0];
}
}
else
{
NSMutableDictionary<NSString *, id> *top = _stack.lastObject;
id existing = top[elementName];
if ([existing isKindOfClass:[NSArray class]])
{
[(NSMutableArray *)existing addObject:node];
}
else if (existing)
{
top[elementName] = [@[existing, node] mutableCopy];
}
else if (_alwaysUseArrays)
{
top[elementName] = [NSMutableArray arrayWithObject:node];
}
else
{
top[elementName] = node;
}
[_stack addObject:node];
}
}
- (NSString *)nameForNode:(NSDictionary<NSString *, id> *)node inDictionary:(NSDictionary<NSString *, id> *)dict
{
if (node.nodeName)
{
return node.nodeName;
}
else
{
for (NSString *name in dict)
{
id object = dict[name];
if (object == node)
{
return name;
}
else if ([object isKindOfClass:[NSArray class]] && [(NSArray *)object containsObject:node])
{
return name;
}
}
}
return nil;
}
- (void)parser:(__unused NSXMLParser *)parser didEndElement:(__unused NSString *)elementName namespaceURI:(__unused NSString *)namespaceURI qualifiedName:(__unused NSString *)qName
{
[self endText];
NSMutableDictionary<NSString *, id> *top = _stack.lastObject;
[_stack removeLastObject];
if (!top.attributes && !top.childNodes && !top.comments)
{
NSMutableDictionary<NSString *, id> *newTop = _stack.lastObject;
NSString *nodeName = [self nameForNode:top inDictionary:newTop];
if (nodeName)
{
id parentNode = newTop[nodeName];
NSString *innerText = top.innerText;
if (innerText && _collapseTextNodes)
{
if ([parentNode isKindOfClass:[NSArray class]])
{
parentNode[[parentNode count] - 1] = innerText;
}
else
{
newTop[nodeName] = innerText;
}
}
else if (!innerText)
{
if (_stripEmptyNodes)
{
if ([parentNode isKindOfClass:[NSArray class]])
{
[(NSMutableArray *)parentNode removeLastObject];
}
else
{
[newTop removeObjectForKey:nodeName];
}
}
else if (!_collapseTextNodes)
{
top[XMLDictionaryTextKey] = @"";
}
}
}
}
}
- (void)parser:(__unused NSXMLParser *)parser foundCharacters:(NSString *)string
{
[self addText:string];
}
- (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock
{
[self addText:[[NSString alloc] initWithData:CDATABlock encoding:NSUTF8StringEncoding]];
}
- (void)parser:(__unused NSXMLParser *)parser foundComment:(NSString *)comment
{
if (_preserveComments)
{
NSMutableDictionary<NSString *, id> *top = _stack.lastObject;
NSMutableArray<NSString *> *comments = top[XMLDictionaryCommentsKey];
if (!comments)
{
comments = [@[comment] mutableCopy];
top[XMLDictionaryCommentsKey] = comments;
}
else
{
[comments addObject:comment];
}
}
}
@end
@implementation NSDictionary(XMLDictionary)
+ (NSDictionary<NSString *, id> *)dictionaryWithXMLParser:(NSXMLParser *)parser
{
return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithParser:parser];
}
+ (NSDictionary<NSString *, id> *)dictionaryWithXMLData:(NSData *)data
{
return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithData:data];
}
+ (NSDictionary<NSString *, id> *)dictionaryWithXMLString:(NSString *)string
{
return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithString:string];
}
+ (NSDictionary<NSString *, id> *)dictionaryWithXMLFile:(NSString *)path
{
return [[[XMLDictionaryParser sharedInstance] copy] dictionaryWithFile:path];
}
- (nullable NSDictionary<NSString *, NSString *> *)attributes
{
NSDictionary<NSString *, NSString *> *attributes = self[XMLDictionaryAttributesKey];
if (attributes)
{
return attributes.count? attributes: nil;
}
else
{
NSMutableDictionary<NSString *, id> *filteredDict = [NSMutableDictionary dictionaryWithDictionary:self];
[filteredDict removeObjectsForKeys:@[XMLDictionaryCommentsKey, XMLDictionaryTextKey, XMLDictionaryNodeNameKey]];
for (NSString *key in filteredDict.allKeys)
{
[filteredDict removeObjectForKey:key];
if ([key hasPrefix:XMLDictionaryAttributePrefix])
{
filteredDict[[key substringFromIndex:XMLDictionaryAttributePrefix.length]] = self[key];
}
}
return filteredDict.count? filteredDict: nil;
}
return nil;
}
- (nullable NSDictionary *)childNodes
{
NSMutableDictionary *filteredDict = [self mutableCopy];
[filteredDict removeObjectsForKeys:@[XMLDictionaryAttributesKey, XMLDictionaryCommentsKey, XMLDictionaryTextKey, XMLDictionaryNodeNameKey]];
for (NSString *key in filteredDict.allKeys)
{
if ([key hasPrefix:XMLDictionaryAttributePrefix])
{
[filteredDict removeObjectForKey:key];
}
}
return filteredDict.count? filteredDict: nil;
}
- (nullable NSArray *)comments
{
return self[XMLDictionaryCommentsKey];
}
- (nullable NSString *)nodeName
{
return self[XMLDictionaryNodeNameKey];
}
- (id)innerText
{
id text = self[XMLDictionaryTextKey];
if ([text isKindOfClass:[NSArray class]])
{
return [text componentsJoinedByString:@"\n"];
}
else
{
return text;
}
}
- (NSString *)innerXML
{
NSMutableArray *nodes = [NSMutableArray array];
for (NSString *comment in [self comments])
{
[nodes addObject:[NSString stringWithFormat:@"<!--%@-->", [comment XMLEncodedString]]];
}
NSDictionary *childNodes = [self childNodes];
for (NSString *key in childNodes)
{
[nodes addObject:[XMLDictionaryParser XMLStringForNode:childNodes[key] withNodeName:key]];
}
NSString *text = [self innerText];
if (text)
{
[nodes addObject:[text XMLEncodedString]];
}
return [nodes componentsJoinedByString:@"\n"];
}
- (NSString *)XMLString
{
if (self.count == 1 && ![self nodeName])
{
//ignore outermost dictionary
return [self innerXML];
}
else
{
return [XMLDictionaryParser XMLStringForNode:self withNodeName:[self nodeName] ?: @"root"];
}
}
- (nullable NSArray *)arrayValueForKeyPath:(NSString *)keyPath
{
id value = [self valueForKeyPath:keyPath];
if (value && ![value isKindOfClass:[NSArray class]])
{
return @[value];
}
return value;
}
- (nullable NSString *)stringValueForKeyPath:(NSString *)keyPath
{
id value = [self valueForKeyPath:keyPath];
if ([value isKindOfClass:[NSArray class]])
{
value = ((NSArray *)value).firstObject;
}
if ([value isKindOfClass:[NSDictionary class]])
{
return [(NSDictionary *)value innerText];
}
return value;
}
- (nullable NSDictionary<NSString *, id> *)dictionaryValueForKeyPath:(NSString *)keyPath
{
id value = [self valueForKeyPath:keyPath];
if ([value isKindOfClass:[NSArray class]])
{
value = [value count]? value[0]: nil;
}
if ([value isKindOfClass:[NSString class]])
{
return @{XMLDictionaryTextKey: value};
}
return value;
}
@end
@implementation NSString (XMLDictionary)
- (NSString *)XMLEncodedString
{
return [[[[[self stringByReplacingOccurrencesOfString:@"&" withString:@"&"]
stringByReplacingOccurrencesOfString:@"<" withString:@"<"]
stringByReplacingOccurrencesOfString:@">" withString:@">"]
stringByReplacingOccurrencesOfString:@"\"" withString:@"""]
stringByReplacingOccurrencesOfString:@"\'" withString:@"'"];
}
@end
//调用
#import "AppDelegate.h"
#import "WeChatManager.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
return YES;
}
#pragma mark ————————— 9.0以后使用 —————————————
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
return [WXApi handleOpenURL:url delegate:[WeChatManager shareManager]];
}
@end
#import "ViewController.h"
#import "WeChatManager.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// Pods for WXPay
// 1. pod 'WechatOpenSDK'
// 2.用到一个 XMLDictionary 工具类拖入项目
WeChatManager *wx = [WeChatManager shareManager];
// [wx weiXinPayName:@"商品名字" money:0.01f];
// [wx weChatShare:@"标题" content:@"详细内容" url:@"http://wwww.baidu.com" image:nil scenetype:(WechtShareTypeFriends)];
[wx weChatLogin];
}
@end