add:新加入了block回调,详细可以看博客最后面的github地址。
年前的时候,公司说需要在APP里面做微信、新浪和QQ的第三方登录。当时,由于APP已经集成了微信支付、微信、新浪和QQ的分享ShareSDK(精简版)。后来在集成微信、新浪和QQ的第三方登陆地时候,ShareSDK(精简版)集成新浪和QQ的登录是没有问题的。但是当集成到了微信登录的时候就出现了问题,总是在回调的时候,进入取消的状态。后来问了ShareSDK的客服,没有解决这个问题,网上也没有答案。后来经过分析排查后,是微信的APP_KEY的问题。一个APP里面有两个APP_KEY,支付一个分享一个,后注册的一个就会把前面注册的一个给屏蔽掉。后来看了项目中的分享部分,发现当时他们做分享的时候就存在这个问题,由于他们是先做的分享后集成的支付,所以支付是正常的,到了微信分享就有问题,分享后进入“取消”的状态。当时他们做完支付之后就导致了微信分享进入“取消”的状态了问题,之前做的人发现这个问题后的解决办法是在微信分先的回调里面进入“取消”的状态里面发送网络请求加积分(项目有分享之后给用户加积分的功能,但是微信分享的内容还是可以分享出去)。 而我再来做微信登陆地时候,由于之前已经集成了分享(不知道前面的问题,前面的人也没有说),不需要再次注册,所以就踩雷了。后来去微信开放平台看两个APP_KEY都是具有微信登陆和分享的权限的,但又不知道为什么后注册的支付的APP_KEY到了分享和登录的时候就是进入取消状态,前面分享的APP_KEY又没有支付功能。知道问题的原因了,是两个APP_KEY的问题。后来在分享和登录的时候注册一个APP_KEY1,在支付的时候注册另一个APP_KEY2,问题解决了。
当时在做微信登录的时候,由于那个问题,微信尝试使用原生的登录。后来项目没有加第三方登录的功能进来。我也不知道公司到底怎么想的。
今天就看了微信的文档,再次做了微信的原生登录和分享。今天就记录下来分享出来。
由于最近公司把电脑端屏蔽微信和微信相关的接口导致不能访问微信开放平台,只能使用iPad查看了文档了。还好相关的库文件公司APP里面集成的有,可以直接拖过来使用,但是没有APP_KEY,所以呢,去ShareSDK的文档里面找了一个过来使用。
有了上面的操作就可以了。Xcode7还需要设置在Info.plist中增加一点东西,使其兼容http,Xcode7默认是https。
我的做法是做了一个WXLoginShare类,用来做注册、登录、分享及最后登录的回调的。
#import "AppDelegate.h" #import "WXLoginShare.h" @interface AppDelegate () @property (nonatomic,copy)NSString *kWeiXinRefreshToken; @property (nonatomic,strong)WXLoginShare *wXLoginShare; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.wXLoginShare = [WXLoginShare shareInstance]; [self.wXLoginShare WXLoginShareRegisterApp]; // Override point for customization after application launch. return YES; } //- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{ // id idwXLoginShare = self.wXLoginShare; // return [WXApi handleOpenURL:url delegate:idwXLoginShare]; //} // //- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{ // id idwXLoginShare = self.wXLoginShare; // return [WXApi handleOpenURL:url delegate:idwXLoginShare]; //} -(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options { id idwXLoginShare = self.wXLoginShare; return [WXApi handleOpenURL:url delegate:idwXLoginShare]; }
上面我们的WXLoginShare是一个单例。为什么是单例,后面再说。
回调方法里面为什么要做
id idwXLoginShare = self.wXLoginShare;这样的处理呢?因为不做这样的处理会报黄(警告)
因为一个是id类型一个是strong类型,编译不会有问题,但是有警告,做一下转换就好了。
+ (WXLoginShare *)shareInstance { static WXLoginShare *LoginShare = nil; static dispatch_once_t once; dispatch_once(&once, ^{ LoginShare = [[self alloc] init]; }); return LoginShare; } /** * 注册ID */ - (void)WXLoginShareRegisterApp{ [WXApi registerApp:APP_ID_SHARE]; }
从上到下,一次是微信登陆需要显示头像的UIImageView控件和昵称的UILabel控件。中间是:微信登录。下面是微信分享触发按钮。
在WXLoginShare类里面还有一个微信登登录的方法
/** * 微信登录 */ - (void)WXLogin{ SendAuthReq *req = [[SendAuthReq alloc] init]; req.scope = @"snsapi_userinfo"; req.state = @"WXLoginViewController"; [WXApi sendReq:req]; }
和申明了一个协议,两个代理方法:
#import <Foundation/Foundation.h> #import "WXApi.h" @protocol WXLoginShareDelegate <NSObject> @optional /** * 登录成功 * * @param dice 微信后台返回的数据 */ - (void)WXLoginShareLoginSuccess:(NSDictionary *)dice; /** * 登录失败 * * @param dice 微信后台返回的数据 */ - (void)WXLoginShareLoginFail:(NSDictionary *)dice; @end @interface WXLoginShare : NSObject @property (nonatomic,weak)id<WXLoginShareDelegate>delegate; + (WXLoginShare *)shareInstance; /** * 注册ID */ - (void)WXLoginShareRegisterApp; /** * 微信登录 */ - (void)WXLogin;在“微信登录”按钮点击的控制器里面我们只需要关联代理调用方法就可以了。
#import "ViewController.h" #import "WXLoginShare.h" #import "UIImageView+WebCache.h" @interface ViewController ()<WXLoginShareDelegate> /** * 显示头像 */ @property (weak, nonatomic) IBOutlet UIImageView *headImageView; /** * 显示昵称 */ @property (weak, nonatomic) IBOutlet UILabel *nickNameLabel; @property (nonatomic,strong)WXLoginShare *wXLoginShare; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.wXLoginShare = [WXLoginShare shareInstance]; self.wXLoginShare.delegate = self; // Do any additional setup after loading the view, typically from a nib. } /** * 微信登录 * * @param sender sender description */ - (IBAction)wxBtnCLick:(UIButton *)sender { [self.wXLoginShare WXLogin]; }
#pragma mark - WXLoginShareDelegate - (void)WXLoginShareLoginSuccess:(NSDictionary *)dice{ [self.headImageView sd_setImageWithURL:[NSURL URLWithString:dice[@"headimgurl"]] placeholderImage:[UIImage imageNamed:@"2"]]; self.nickNameLabel.text = dice[@"nickname"]; } - (void)WXLoginShareLoginFail:(NSDictionary *)dice{ }但是在处理微信的回调的时候,需要实现微信的代理方法。
#pragma mark - WXApiDelegate -(void) onReq:(BaseReq*)req { } -(void) onResp:(BaseResp*)resp { // 微信登录 if ([resp isKindOfClass:[SendAuthResp class]]) { if (resp.errCode == 0) { SendAuthResp *aresp = (SendAuthResp *)resp; [self getAccessTokenWithCode:aresp.code]; return; } } // 微信分享/收藏 if ([resp isKindOfClass:[SendMessageToWXResp class]]) { SendMessageToWXResp *aresp = (SendMessageToWXResp *)resp; NSLog(@"county = %@",aresp.country); NSLog(@"lang = %@",aresp.lang); } } /** * 获取access_token * * @param code code description */ - (void)getAccessTokenWithCode:(NSString *)code { NSString *urlString =[NSString stringWithFormat:@"https://api.weixin.qq.com/sns/oauth2/access_token?appid=%@&secret=%@&code=%@&grant_type=authorization_code",APP_ID_SHARE,APP_SECRET_SHARE,code]; NSLog(@"urlString = %@",urlString); NSURL *url = [NSURL URLWithString:urlString]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *dataStr = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(), ^{ if (data) { NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; NSLog(@"dict = %@",dict); if ([dict objectForKey:@"errcode"]) { // 授权失败(用户取消/拒绝) if ([self.delegate respondsToSelector:@selector(WXLoginShareLoginFail:)]) { [self.delegate WXLoginShareLoginFail:dict]; } }else{ self.kWeiXinRefreshToken = [dict objectForKey:@"access_token"]; [self getUserInfoWithAccessToken:[dict objectForKey:@"access_token"] andOpenId:[dict objectForKey:@"openid"]]; } } }); }); } /** * 获取用户信息 * * @param accessToken access_token * @param openId openId description */ - (void)getUserInfoWithAccessToken:(NSString *)accessToken andOpenId:(NSString *)openId { NSString *urlString =[NSString stringWithFormat:@"https://api.weixin.qq.com/sns/userinfo?access_token=%@&openid=%@",accessToken,openId]; NSLog(@"urlString = %@",urlString); NSURL *url = [NSURL URLWithString:urlString]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *dataStr = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(), ^{ if (data) { NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; NSLog(@"dict = %@",dict); if ([dict objectForKey:@"errcode"]) { //AccessToken失效 [self getAccessTokenWithRefreshToken:[[NSUserDefaults standardUserDefaults]objectForKey:self.kWeiXinRefreshToken]]; }else{ if ([_delegate respondsToSelector:@selector(WXLoginShareLoginSuccess:)]) { [_delegate WXLoginShareLoginSuccess:dict]; } } } }); }); } /** * 刷新access_token * * @param refreshToken refreshToken description */ - (void)getAccessTokenWithRefreshToken:(NSString *)refreshToken { NSString *urlString =[NSString stringWithFormat:@"https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=%@&grant_type=refresh_token&refresh_token=%@",@"wx29c1153063de230b",refreshToken]; NSLog(@"urlString = %@",urlString); NSURL *url = [NSURL URLWithString:urlString]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSString *dataStr = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSData *data = [dataStr dataUsingEncoding:NSUTF8StringEncoding]; dispatch_async(dispatch_get_main_queue(), ^{ if (data) { NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; if ([dict objectForKey:@"errcode"]) { //授权过期 }else{ //重新使用AccessToken获取信息 } } }); }); }代码就是按照微信的做就可了。我们只需要把最后从微信回去的数据用过代理传出来就可了。在WXLoginShare类的协议方法里面处理从微信得到的数据就可以了。
效果图
头像和昵称都显示出来了。微信原生的登录也就做完了。下面的需要和自己的应用绑定那就另外的问题了。
为什么要在WXLoginShare类里面做单例呢?
在做微信登录的时候,发现在控制器里面alloc一次WXLoginShare这个类,再关联代理,程序不会运行WXLoginShare类的代理方法,但是还是会走微信的代理的方法。这是由于我们在AppDelegate.m里面注册的时候指定的代理是WXLoginShare的对象,所以微信回调在WXLoginShare里面实现是会走的,至于为什么在控制器里面就不走WXLoginShare类的代理,我也不知道原因,但是使用单例后问题解决了,可能是注册是登陆地时候使用的不是同一个WXLoginShare类的对象吧。
微信分享这个没什么好说的,按照微信的文档操作就是的。
/** * 微信文字分享 * * @param scene WXSceneSession:微信聊天 WXSceneTimeline:朋友圈 WXSceneFavorite:收藏 */ - (void)WXSendMessageToWX:(int)scene{ SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.text = @"测试数据"; req.bText = YES; req.scene = scene; [WXApi sendReq:req]; } /** * 微信图片分享 * * @param scene WXSceneSession:微信聊天 WXSceneTimeline:朋友圈 WXSceneFavorite:收藏 */ - (void)WXSendImageToWX:(int)scene{ WXMediaMessage *message = [WXMediaMessage message]; [message setThumbImage:[UIImage imageNamed:@"1"]]; WXImageObject *imageobject = [WXImageObject object]; NSString *filePath = [[NSBundle mainBundle] pathForResource:@"2" ofType:@"png"]; imageobject.imageData = [NSData dataWithContentsOfFile:filePath]; message.mediaObject = imageobject; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = scene; [WXApi sendReq:req]; } /** * 微信音乐分享 * * @param scene WXSceneSession:微信聊天 WXSceneTimeline:朋友圈 WXSceneFavorite:收藏 */ - (void)WXSendMusicToWX:(int)scene{ WXMediaMessage *message = [WXMediaMessage message]; message.title = @"这事一首音乐"; message.description = @"好听吗?"; [message setThumbImage:[UIImage imageNamed:@"1"]]; WXMusicObject *musicobject = [WXMusicObject object]; musicobject.musicUrl = @"http://tsmusic24.tc.qq.com/102396.mp3"; musicobject.musicLowBandUrl = musicobject.musicUrl; musicobject.musicDataUrl = @"http://tsmusic24.tc.qq.com/102396.mp3"; musicobject.musicLowBandDataUrl = musicobject.musicDataUrl; message.mediaObject = musicobject; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = scene; [WXApi sendReq:req]; } /** * 微信视频分享 * * @param scene WXSceneSession:微信聊天 WXSceneTimeline:朋友圈 WXSceneFavorite:收藏 */ - (void)WXSendVideoToWX:(int)scene{ WXMediaMessage *message = [WXMediaMessage message]; message.title = @"这事一段视频"; message.description = @"好看吗?"; [message setThumbImage:[UIImage imageNamed:@"1"]]; WXVideoObject *videoobject = [WXVideoObject object]; videoobject.videoUrl = @"http://www.iqiyi.com/v_19rrok5lvc.html"; videoobject.videoLowBandUrl = videoobject.videoUrl; message.mediaObject = videoobject; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = scene; [WXApi sendReq:req]; } /** * 微信网页分享 * * @param scene WXSceneSession:微信聊天 WXSceneTimeline:朋友圈 WXSceneFavorite:收藏 */ - (void)WXSendWebToWX:(int)scene{ WXMediaMessage *message = [WXMediaMessage message]; message.title = @"这事一个网页"; message.description = @"好看吗?"; [message setThumbImage:[UIImage imageNamed:@"1"]]; WXWebpageObject *videoobject = [WXWebpageObject object]; videoobject.webpageUrl = @"http://www.hao123.com"; message.mediaObject = videoobject; SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = message; req.scene = scene; [WXApi sendReq:req]; }申明分享之后给用户积分,加经验,我们只需要在方法
-(void) onResp:(BaseResp*)resp里面处理就好了。
按钮触发分享:
/** * 文字分享 * * @param sender sender description */ - (IBAction)shareBtnClick1:(UIButton *)sender { [self.wXLoginShare WXSendMessageToWX:WXSceneTimeline]; } /** * 图片分享 * * @param sender sender description */ - (IBAction)shareImageBtnClick:(UIButton *)sender { [self.wXLoginShare WXSendImageToWX:WXSceneSession]; } /** * 音乐分享 * * @param sender sender description */ - (IBAction)shareMusicBtnClick:(UIButton *)sender { [self.wXLoginShare WXSendMusicToWX:WXSceneFavorite]; } /** * 视频分享 * * @param sender sender description */ - (IBAction)shareVideoBtnClick:(UIButton *)sender { [self.wXLoginShare WXSendVideoToWX:WXSceneSession]; } /** * 网页分享 * * @param sender sender description */ - (IBAction)shareWebBtnClick:(UIButton *)sender { [self.wXLoginShare WXSendWebToWX:WXSceneSession]; }总体来说,集成微信原生的登录和分享没有什么难点,仔细地看官方文档,按照文档操作就差不多了。
github代码地址:请点击我