axmol/plugin/plugins/weibo/proj.ios/SinaWeibo/SinaWeibo.m

553 lines
19 KiB
Objective-C
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// SinaWeibo.m
// sinaweibo_ios_sdk
//
// Created by Wade Cheng on 4/19/12.
// Copyright (c) 2012 SINA. All rights reserved.
//
#import "SinaWeibo.h"
#import "SinaWeiboRequest.h"
#import "SinaWeiboAuthorizeView.h"
#import "SinaWeiboConstants.h"
@interface SinaWeibo ()
@property (nonatomic, copy) NSString *appKey;
@property (nonatomic, copy) NSString *appSecret;
@property (nonatomic, copy) NSString *appRedirectURI;
@end
@implementation SinaWeibo
@synthesize userID;
@synthesize accessToken;
@synthesize expirationDate;
@synthesize refreshToken;
@synthesize ssoCallbackScheme;
@synthesize delegate;
@synthesize appKey;
@synthesize appSecret;
@synthesize appRedirectURI;
#pragma mark - Memory management
/**
* @description 初始化构造函数返回采用默认sso回调地址构造的SinaWeibo对象
* @param _appKey: 分配给第三方应用的appkey
* @param _appSecrect: 分配给第三方应用的appsecrect
* @param _appRedirectURI: 微博开放平台中授权设置的应用回调页
* @return SinaWeibo对象
*/
- (id)initWithAppKey:(NSString *)_appKey appSecret:(NSString *)_appSecrect
appRedirectURI:(NSString *)_appRedirectURI
andDelegate:(id<SinaWeiboDelegate>)_delegate
{
return [self initWithAppKey:_appKey appSecret:_appSecrect appRedirectURI:_appRedirectURI ssoCallbackScheme:nil andDelegate:_delegate];
}
/**
* @description 初始化构造函数返回采用默认sso回调地址构造的SinaWeibo对象
* @param _appKey: 分配给第三方应用的appkey
* @param _appSecrect: 分配给第三方应用的appsecrect
* @param _ssoCallbackScheme: sso回调地址此值应与URL Types中定义的保持一致
* 若为nil,则初始化为默认格式 sinaweibosso.your_app_key;
* @param _appRedirectURI: 微博开放平台中授权设置的应用回调页
* @return SinaWeibo对象
*/
- (id)initWithAppKey:(NSString *)_appKey appSecret:(NSString *)_appSecrect
appRedirectURI:(NSString *)_appRedirectURI
ssoCallbackScheme:(NSString *)_ssoCallbackScheme
andDelegate:(id<SinaWeiboDelegate>)_delegate
{
if ((self = [super init]))
{
self.appKey = _appKey;
self.appSecret = _appSecrect;
self.appRedirectURI = _appRedirectURI;
self.delegate = _delegate;
if (!_ssoCallbackScheme)
{
_ssoCallbackScheme = [NSString stringWithFormat:@"sinaweibosso.%@://", self.appKey];
}
self.ssoCallbackScheme = _ssoCallbackScheme;
requests = [[NSMutableSet alloc] init];
}
return self;
}
- (void)dealloc
{
delegate = nil;
for (SinaWeiboRequest* _request in requests)
{
_request.sinaweibo = nil;
}
[request disconnect];
[request release], request = nil;
[userID release], userID = nil;
[accessToken release], accessToken = nil;
[expirationDate release], expirationDate = nil;
[appKey release], appKey = nil;
[appSecret release], appSecret = nil;
[appRedirectURI release], appRedirectURI = nil;
[ssoCallbackScheme release], ssoCallbackScheme = nil;
[super dealloc];
}
/**
* @description 清空认证信息
*/
- (void)removeAuthData
{
self.accessToken = nil;
self.userID = nil;
self.expirationDate = nil;
NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray* sinaweiboCookies = [cookies cookiesForURL:
[NSURL URLWithString:@"https://open.weibo.cn"]];
for (NSHTTPCookie* cookie in sinaweiboCookies)
{
[cookies deleteCookie:cookie];
}
}
#pragma mark - Private methods
- (void)requestAccessTokenWithAuthorizationCode:(NSString *)code
{
NSDictionary *params = [NSDictionary dictionaryWithObjectsAndKeys:
self.appKey, @"client_id",
self.appSecret, @"client_secret",
@"authorization_code", @"grant_type",
self.appRedirectURI, @"redirect_uri",
code, @"code", nil];
[request disconnect];
[request release], request = nil;
request = [[SinaWeiboRequest requestWithURL:kSinaWeiboWebAccessTokenURL
httpMethod:@"POST"
params:params
delegate:self] retain];
[request connect];
}
- (void)requestDidFinish:(SinaWeiboRequest *)_request
{
[requests removeObject:_request];
_request.sinaweibo = nil;
}
- (void)requestDidFailWithInvalidToken:(NSError *)error
{
if ([delegate respondsToSelector:@selector(sinaweibo:accessTokenInvalidOrExpired:)])
{
[delegate sinaweibo:self accessTokenInvalidOrExpired:error];
}
}
- (void)notifyTokenExpired:(id<SinaWeiboRequestDelegate>)requestDelegate
{
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
@"Token expired", NSLocalizedDescriptionKey, nil];
NSError *error = [NSError errorWithDomain:kSinaWeiboSDKErrorDomain
code:21315
userInfo:userInfo];
if ([delegate respondsToSelector:@selector(sinaweibo:accessTokenInvalidOrExpired:)])
{
[delegate sinaweibo:self accessTokenInvalidOrExpired:error];
}
if ([requestDelegate respondsToSelector:@selector(request:didFailWithError:)])
{
[requestDelegate request:nil didFailWithError:error];
}
}
- (void)logInDidCancel
{
if ([delegate respondsToSelector:@selector(sinaweiboLogInDidCancel:)])
{
[delegate sinaweiboLogInDidCancel:self];
}
}
- (void)logInDidFinishWithAuthInfo:(NSDictionary *)authInfo
{
NSString *access_token = [authInfo objectForKey:@"access_token"];
NSString *uid = [authInfo objectForKey:@"uid"];
NSString *remind_in = [authInfo objectForKey:@"remind_in"];
NSString *refresh_token = [authInfo objectForKey:@"refresh_token"];
if (access_token && uid)
{
if (remind_in != nil)
{
int expVal = [remind_in intValue];
if (expVal == 0)
{
self.expirationDate = [NSDate distantFuture];
}
else
{
self.expirationDate = [NSDate dateWithTimeIntervalSinceNow:expVal];
}
}
self.accessToken = access_token;
self.userID = uid;
self.refreshToken = refresh_token;
if ([delegate respondsToSelector:@selector(sinaweiboDidLogIn:)])
{
[delegate sinaweiboDidLogIn:self];
}
}
}
- (void)logInDidFailWithErrorInfo:(NSDictionary *)errorInfo
{
NSString *error_code = [errorInfo objectForKey:@"error_code"];
if ([error_code isEqualToString:@"21330"])
{
[self logInDidCancel];
}
else
{
if ([delegate respondsToSelector:@selector(sinaweibo:logInDidFailWithError:)])
{
NSString *error_description = [errorInfo objectForKey:@"error_description"];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
errorInfo, @"error",
error_description, NSLocalizedDescriptionKey, nil];
NSError *error = [NSError errorWithDomain:kSinaWeiboSDKErrorDomain
code:[error_code intValue]
userInfo:userInfo];
[delegate sinaweibo:self logInDidFailWithError:error];
}
}
}
#pragma mark - Validation
/**
* @description 判断是否登录
* @return YES为已登录NO为未登录
*/
- (BOOL)isLoggedIn
{
return userID && accessToken && expirationDate;
}
/**
* @description 判断登录是否过期
* @return YES为已过期NO为未为期
*/
- (BOOL)isAuthorizeExpired
{
NSDate *now = [NSDate date];
return ([now compare:expirationDate] == NSOrderedDescending);
}
/**
* @description 判断登录是否有效,当已登录并且登录未过期时为有效状态
* @return YES为有效NO为无效
*/
- (BOOL)isAuthValid
{
return ([self isLoggedIn] && ![self isAuthorizeExpired]);
}
#pragma mark - LogIn / LogOut
/**
* @description 登录入口当初始化SinaWeibo对象完成后直接调用此方法完成登录
*/
- (void)logIn
{
if ([self isAuthValid])
{
if ([delegate respondsToSelector:@selector(sinaweiboDidLogIn:)])
{
[delegate sinaweiboDidLogIn:self];
}
}
else
{
[self removeAuthData];
ssoLoggingIn = NO;
// open sina weibo app
UIDevice *device = [UIDevice currentDevice];
if ([device respondsToSelector:@selector(isMultitaskingSupported)] &&
[device isMultitaskingSupported])
{
NSDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
self.appKey, @"client_id",
self.appRedirectURI, @"redirect_uri",
self.ssoCallbackScheme, @"callback_uri", nil];
// 先用iPad微博打开
NSString *appAuthBaseURL = kSinaWeiboAppAuthURL_iPad;
if (SinaWeiboIsDeviceIPad())
{
NSString *appAuthURL = [SinaWeiboRequest serializeURL:appAuthBaseURL
params:params httpMethod:@"GET"];
ssoLoggingIn = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:appAuthURL]];
}
// 在用iPhone微博打开
if (!ssoLoggingIn)
{
appAuthBaseURL = kSinaWeiboAppAuthURL_iPhone;
NSString *appAuthURL = [SinaWeiboRequest serializeURL:appAuthBaseURL
params:params httpMethod:@"GET"];
ssoLoggingIn = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:appAuthURL]];
}
}
if (!ssoLoggingIn)
{
// open authorize view
NSDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
self.appKey, @"client_id",
@"code", @"response_type",
self.appRedirectURI, @"redirect_uri",
@"mobile", @"display", nil];
SinaWeiboAuthorizeView *authorizeView = \
[[SinaWeiboAuthorizeView alloc] initWithAuthParams:params
delegate:self];
[authorizeView show];
[authorizeView release];
}
}
}
/**
* @description 退出方法,需要退出时直接调用此方法
*/
- (void)logOut
{
[self removeAuthData];
if ([delegate respondsToSelector:@selector(sinaweiboDidLogOut:)])
{
[delegate sinaweiboDidLogOut:self];
}
}
#pragma mark - Send request with token
/**
* @description 微博API的请求接口方法中自动完成token信息的拼接
* @param url: 请求的接口
* @param params: 请求的参数,如发微博所带的文字内容等
* @param httpMethod: http类型GET或POST
* @param _delegate: 处理请求结果的回调的对象SinaweiboRequestDelegate类
* @return 完成实际请求操作的SinaWeiboRequest对象
*/
- (SinaWeiboRequest *)requestWithURL:(NSString *)url
params:(NSMutableDictionary *)params
httpMethod:(NSString *)httpMethod
delegate:(id<SinaWeiboRequestDelegate>)_delegate
{
if (params == nil)
{
params = [NSMutableDictionary dictionary];
}
if ([self isAuthValid])
{
[params setValue:self.accessToken forKey:@"access_token"];
NSString *fullURL = [kSinaWeiboSDKAPIDomain stringByAppendingString:url];
SinaWeiboRequest *_request = [SinaWeiboRequest requestWithURL:fullURL
httpMethod:httpMethod
params:params
delegate:_delegate];
_request.sinaweibo = self;
[requests addObject:_request];
[_request connect];
return _request;
}
else
{
//notify token expired in next runloop
[self performSelectorOnMainThread:@selector(notifyTokenExpired:)
withObject:_delegate
waitUntilDone:NO];
return nil;
}
}
#pragma mark - SinaWeiboAuthorizeView Delegate
- (void)authorizeView:(SinaWeiboAuthorizeView *)authView didRecieveAuthorizationCode:(NSString *)code
{
[self requestAccessTokenWithAuthorizationCode:code];
}
- (void)authorizeView:(SinaWeiboAuthorizeView *)authView didFailWithErrorInfo:(NSDictionary *)errorInfo
{
[self logInDidFailWithErrorInfo:errorInfo];
}
- (void)authorizeViewDidCancel:(SinaWeiboAuthorizeView *)authView
{
[self logInDidCancel];
}
#pragma mark - SinaWeiboRequest Delegate
- (void)request:(SinaWeiboRequest *)_request didFailWithError:(NSError *)error
{
if (_request == request)
{
if ([delegate respondsToSelector:@selector(sinaweibo:logInDidFailWithError:)])
{
[delegate sinaweibo:self logInDidFailWithError:error];
}
[request release], request = nil;
}
}
- (void)request:(SinaWeiboRequest *)_request didFinishLoadingWithResult:(id)result
{
if (_request == request)
{
NSLog(@"access token result = %@", result);
[self logInDidFinishWithAuthInfo:result];
[request release], request = nil;
}
}
#pragma mark - Application life cycle
/**
* @description 当应用从后台唤起时,应调用此方法,需要完成退出当前登录状态的功能
*/
- (void)applicationDidBecomeActive
{
if (ssoLoggingIn)
{
// user open the app manually
// clean sso login state
ssoLoggingIn = NO;
if ([delegate respondsToSelector:@selector(sinaweiboLogInDidCancel:)])
{
[delegate sinaweiboLogInDidCancel:self];
}
}
}
/**
* @description sso回调方法官方客户端完成sso授权后回调唤起应用应用中应调用此方法完成sso登录
* @param url: 官方客户端回调给应用时传回的参数,包含认证信息等
* @return YES
*/
- (BOOL)handleOpenURL:(NSURL *)url
{
NSString *urlString = [url absoluteString];
if ([urlString hasPrefix:self.ssoCallbackScheme])
{
if (!ssoLoggingIn)
{
// sso callback after user have manually opened the app
// ignore the request
}
else
{
ssoLoggingIn = NO;
if ([SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"sso_error_user_cancelled"])
{
if ([delegate respondsToSelector:@selector(sinaweiboLogInDidCancel:)])
{
[delegate sinaweiboLogInDidCancel:self];
}
}
else if ([SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"sso_error_invalid_params"])
{
if ([delegate respondsToSelector:@selector(sinaweibo:logInDidFailWithError:)])
{
NSString *error_description = @"Invalid sso params";
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
error_description, NSLocalizedDescriptionKey, nil];
NSError *error = [NSError errorWithDomain:kSinaWeiboSDKErrorDomain
code:kSinaWeiboSDKErrorCodeSSOParamsError
userInfo:userInfo];
[delegate sinaweibo:self logInDidFailWithError:error];
}
}
else if ([SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"error_code"])
{
NSString *error_code = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"error_code"];
NSString *error = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"error"];
NSString *error_uri = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"error_uri"];
NSString *error_description = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"error_description"];
NSDictionary *errorInfo = [NSDictionary dictionaryWithObjectsAndKeys:
error, @"error",
error_uri, @"error_uri",
error_code, @"error_code",
error_description, @"error_description", nil];
[self logInDidFailWithErrorInfo:errorInfo];
}
else
{
NSString *access_token = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"access_token"];
NSString *expires_in = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"expires_in"];
NSString *remind_in = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"remind_in"];
NSString *uid = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"uid"];
NSString *refresh_token = [SinaWeiboRequest getParamValueFromUrl:urlString paramName:@"refresh_token"];
NSMutableDictionary *authInfo = [NSMutableDictionary dictionary];
if (access_token) [authInfo setObject:access_token forKey:@"access_token"];
if (expires_in) [authInfo setObject:expires_in forKey:@"expires_in"];
if (remind_in) [authInfo setObject:remind_in forKey:@"remind_in"];
if (refresh_token) [authInfo setObject:refresh_token forKey:@"refresh_token"];
if (uid) [authInfo setObject:uid forKey:@"uid"];
[self logInDidFinishWithAuthInfo:authInfo];
}
}
}
return YES;
}
@end
BOOL SinaWeiboIsDeviceIPad()
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 30200
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
return YES;
}
#endif
return NO;
}