├── .gitignore ├── .travis.yml ├── Classes ├── AVOSCloudSNS.h ├── AVOSCloudSNS.m ├── AVOSCloudSNSUtils.h ├── AVOSCloudSNSUtils.m ├── AVOSCloudSNS_.h ├── AVSNSHttpClient.h ├── AVSNSHttpClient.m ├── AVSNSLoginViewController.h ├── AVSNSLoginViewController.m ├── AVSNSWebViewController.h ├── AVSNSWebViewController.m ├── AVUser+SNS.h ├── AVUser+SNS.m ├── AVUser_SNSInternal.h ├── AVWebViewController.h ├── AVWebViewController.m ├── NSURL+AVAdditions.h ├── NSURL+AVAdditions.m └── Vendor │ └── AFNetworking │ ├── LCSHTTPRequestOperation.h │ ├── LCSHTTPRequestOperation.m │ ├── LCSHTTPRequestOperationManager.h │ ├── LCSHTTPRequestOperationManager.m │ ├── LCSHTTPSessionManager.h │ ├── LCSHTTPSessionManager.m │ ├── LCSNetworkReachabilityManager.h │ ├── LCSNetworkReachabilityManager.m │ ├── LCSNetworking.h │ ├── LCSSecurityPolicy.h │ ├── LCSSecurityPolicy.m │ ├── LCSURLConnectionOperation.h │ ├── LCSURLConnectionOperation.m │ ├── LCSURLRequestSerialization.h │ ├── LCSURLRequestSerialization.m │ ├── LCSURLResponseSerialization.h │ ├── LCSURLResponseSerialization.m │ ├── LCSURLSessionManager.h │ └── LCSURLSessionManager.m ├── LeanCloudSocial.podspec ├── LeanCloudSocial.xcworkspace └── contents.xcworkspacedata ├── LeanCloudSocial ├── LeanCloudSocial.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── LeanCloudSocial.xcscheme │ │ ├── LeanCloudSocialDynamic.xcscheme │ │ └── LeanCloudSocialTests.xcscheme ├── LeanCloudSocial │ └── Info.plist ├── LeanCloudSocialDynamic │ └── Info.plist └── LeanCloudSocialTests │ ├── AVOSCloudSNSTest.m │ ├── AVSNSTestCase.h │ ├── AVSNSTestCase.m │ ├── AVUserSNSTest.m │ └── Info.plist ├── LeanCloudSocialDemo ├── LeanCloudSocialDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── LeanCloudSocialDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-60@2x-1.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-72.png │ │ │ ├── Icon-72@2x.png │ │ │ ├── Icon-76.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── Icon-Small-1.png │ │ │ ├── Icon-Small-40.png │ │ │ ├── Icon-Small-40@2x-1.png │ │ │ ├── Icon-Small-40@2x.png │ │ │ ├── Icon-Small-40@3x.png │ │ │ ├── Icon-Small-50.png │ │ │ ├── Icon-Small-50@2x.png │ │ │ ├── Icon-Small.png │ │ │ ├── Icon-Small@2x-1.png │ │ │ ├── Icon-Small@2x.png │ │ │ ├── Icon-Small@3x.png │ │ │ ├── Icon.png │ │ │ └── Icon@2x.png │ ├── Info.plist │ ├── ResultViewController.h │ ├── ResultViewController.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── LeanCloudSocialDemoTests │ ├── Info.plist │ └── LeanCloudSocialDemoTests.m ├── LeanCloudSocialDynamic.podspec ├── LeanCloudSocialDynamic ├── LeanCloudSocialDynamic.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── LeanCloudSocialDynamic │ └── Info.plist ├── Podfile ├── README.md └── build-framework.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | 20 | ## Other 21 | *.xccheckout 22 | *.moved-aside 23 | *.xcuserstate 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | # CocoaPods 30 | Pods/ 31 | Podfile.lock 32 | 33 | *.gcda 34 | *.gcno 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | xcode_workspace: LeanCloudSocial.xcworkspace 3 | xcode_scheme: LeanCloudSocial 4 | -------------------------------------------------------------------------------- /Classes/AVOSCloudSNS.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVOSCloudSNS.h 3 | // AVOSCloudSocial 4 | // 5 | // Created by Feng Junwen on 5/22/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for AVOSCloudSocial. 12 | FOUNDATION_EXPORT double AVOSCloudSocialVersionNumber; 13 | 14 | //! Project version string for AVOSCloudSocial. 15 | FOUNDATION_EXPORT const unsigned char AVOSCloudSocialVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | #import "AVUser+SNS.h" 20 | 21 | /** 22 | * AVOSCloudSNS目前支持的平台类型 23 | */ 24 | typedef NS_ENUM(int, AVOSCloudSNSType){ 25 | /// 新浪微博 26 | AVOSCloudSNSSinaWeibo =1, 27 | 28 | /// QQ 29 | AVOSCloudSNSQQ =2, 30 | 31 | /// 微信 32 | AVOSCloudSNSWeiXin =3, 33 | }; 34 | 35 | /** 36 | * AVOSCloudSNS错误码 37 | */ 38 | 39 | typedef NS_ENUM(int, AVOSCloudSNSErrorCode){ 40 | /// 用户取消 41 | AVOSCloudSNSErrorUserCancel =1, 42 | 43 | /// 登录失败 44 | AVOSCloudSNSErrorLoginFail =2, 45 | 46 | /// 无绑定用户 47 | AVOSCloudSNSErrorNeedLogin =3, 48 | 49 | /// Token过期 50 | AVOSCloudSNSErrorTokenExpired=4, 51 | 52 | /// 操作不支持。当微信没有安装时,调用 -[loginWithCallback:platform] 会返回该错误,暂不支持微信网页登录 53 | AVOSCloudSNSErrorCodeNotSupported = 5, 54 | 55 | /// 无效的第三方数据 56 | AVOSCloudSNSErrorCodeAuthDataError = 6, 57 | }; 58 | 59 | /** 60 | * AVOSCloudSNS错误域 61 | */ 62 | extern NSString * const AVOSCloudSNSErrorDomain; 63 | 64 | /** 65 | * AVOSCloudSNS请求回调 66 | * 67 | * @param object 请求成功返回的内容 68 | * @param error 请求失败返回的内容 69 | */ 70 | typedef void (^AVSNSResultBlock)(id object, NSError *error); 71 | typedef void (^AVSNSProgressBlock)(float percent); 72 | 73 | 74 | /** 75 | * AVOSCloudSNS 是一个轻量级的社交平台助手 76 | * 几行代码就可以实现登录和发布内容 77 | */ 78 | @interface AVOSCloudSNS : NSObject 79 | 80 | /** 81 | * 设置平台所需要的参数 82 | * 83 | * @warning 如果不进行设置, 则用`AVOSCloud`进行登录认证. 84 | * 85 | * @param type 平台类型 86 | * @param appkey 该平台分配的AppKey 87 | * @param appsec 该平台分配的AppSecret 88 | * @param redirect_uri 该平台上设置的回调地址 (QQ可以nil, 因为QQ没有这个设置选项. 新浪微博必填!) 89 | */ 90 | +(void)setupPlatform:(AVOSCloudSNSType)type 91 | withAppKey:(NSString*)appkey andAppSecret:(NSString*)appsec andRedirectURI:(NSString*)redirect_uri; 92 | 93 | /** 94 | * 相应的 App 是否有安装,如果有安装的话,说明可以用 SSO 跳转登录。没有安装的话,QQ 和微博将跳转至网页登录,微信暂时不支持网页登录,请隐藏微信按钮。 95 | * @param type 支持 QQ、WeiXin、Weibo 96 | */ 97 | + (BOOL)isAppInstalledForType:(AVOSCloudSNSType)type; 98 | 99 | /** 100 | * 用社交平台登录, 并获取手动显示登录界面 101 | * @warning 需要在回调后手动关闭此UIViewController. 102 | * @param callback 登录结果回调 103 | * 104 | * @return 用于显示登录界面的UIViewController,如果可以SSO登录 则返回nil. 105 | */ 106 | +(UIViewController*)loginManuallyWithCallback:(AVSNSResultBlock)callback; 107 | 108 | 109 | /** 110 | * 用指定的社交平台登录, 并获取手动显示登录界面 111 | * @warning 需要在回调后手动关闭此UIViewController. 112 | * @param type 指定平台类型 113 | * @param callback 登录结果回调 114 | * 115 | * @return 用于显示登录界面的UIViewController,如果可以SSO登录 则返回nil. 116 | */ 117 | +(UIViewController*)loginManuallyWithCallback:(AVSNSResultBlock)callback toPlatform:(AVOSCloudSNSType)type; 118 | 119 | /** 120 | * 用社会化平台登录,并自动弹出登录界面 121 | * 122 | * @discussion 如果配置了SSO的相关数据会自动尝试用SSO来打开官方应用登录,如果不成功则用传统的方式在本App界面中完成登录过程 123 | * @warning 如果主窗口有`rootViewController`,则会自动通过presentModalViewController打开. **注意:** presentModalViewController与正在进行的其它系统动画同时出现时会在Log里出现`Warning: ... while a presentation or dismiss is in progress!`类似的警告,但是不会影响使用 124 | * @param callback 登录结果回调 125 | * 126 | */ 127 | +(void)loginWithCallback:(AVSNSResultBlock)callback; 128 | 129 | /** 130 | * 用指定的社交平台登录,并自动弹出登录界面 131 | * 132 | * @warning 如果主窗口有`rootViewController`,则会自动通过presentModalViewController打开. **注意:** presentModalViewController与正在进行的其它系统动画同时出现时会在Log里出现`Warning: ... while a presentation or dismiss is in progress!`类似的警告,但是不会影响使用 133 | * @param type 指定平台类型 134 | * @param callback 登录结果回调 135 | * 136 | */ 137 | +(void)loginWithCallback:(AVSNSResultBlock)callback toPlatform:(AVOSCloudSNSType)type; 138 | 139 | /** 140 | * 通过后台生成的登录url显示登录界面 141 | * @warning 需要在回调后手动关闭此UIViewController. 142 | * @param url 后台生成的登录url 143 | * @param callback 登录结果回调 144 | * 145 | * @return 用于显示登录界面的UIViewController. 146 | */ 147 | +(UIViewController *)loginManuallyWithURL:(NSURL *)url callback:(AVSNSResultBlock)callback; 148 | 149 | /** 150 | * 通过后台生成的登录url显示登录界面 151 | * 152 | * @warning 如果主窗口有`rootViewController`,则会自动通过presentModalViewController打开. **注意:** presentModalViewController与正在进行的其它系统动画同时出现时会在Log里出现`Warning: ... while a presentation or dismiss is in progress!`类似的警告,但是不会影响使用 153 | * @param url 后台生成的登录url 154 | * @param callback 登录结果回调 155 | * 156 | */ 157 | +(void)loginWithURL:(NSURL *)url callback:(AVSNSResultBlock)callback; 158 | 159 | /** 160 | * 分享文字到指定社交平台 161 | * @warning 目前只支持新浪微博 162 | * 163 | * @param text 文字内容 164 | * @param linkUrl 链接地址(可选) 165 | * @param type 指定平台类型 166 | * @param callback 结果回调 167 | * @param progressBlock 进度回调(可选) 168 | * 169 | */ 170 | //+(void)shareText:(NSString*)text andLink:(NSString*)linkUrl toPlatform:(AVOSCloudSNSType)type withCallback:(AVSNSResultBlock)callback andProgress:(AVSNSProgressBlock)progressBlock; 171 | 172 | 173 | /** 174 | * 分享文字和图片到指定社交平台 175 | * @warning 目前只支持新浪微博 176 | * 177 | * @param text 文字内容 178 | * @param linkUrl 链接地址(可选) 179 | * @param image 图片 (将会被JPEG压缩0.8) 180 | * @param type 指定平台类型 181 | * @param callback 结果回调 182 | * @param progressBlock 进度回调(可选) 183 | * 184 | */ 185 | //+(void)shareText:(NSString*)text andLink:(NSString*)linkUrl andImage:(UIImage*)image toPlatform:(AVOSCloudSNSType)type withCallback:(AVSNSResultBlock)callback andProgress:(AVSNSProgressBlock)progressBlock; 186 | 187 | 188 | /** 189 | * 注销指定的社交平台绑定的账号 190 | * 191 | * @param type 指定平台类型 192 | * 193 | */ 194 | +(void)logout:(AVOSCloudSNSType)type; 195 | 196 | /** 197 | * 捕获SSO登录后返回数据 198 | * 199 | * @param url 回调本app的URL 200 | */ 201 | +(BOOL)handleOpenURL:(NSURL *)url; 202 | 203 | 204 | /** 205 | * 获取指定的社交平台已经缓存的用户信息 206 | * 207 | * @param type 指定平台类型 208 | * @return 包含用户信息的字典, 如果返回nil则没有绑定的用户. 包括常用字段, 用户ID:`id`, 用户名:`username`, 平台类型:`type`, 头像:`avatar`, 过期时间:`expires_at`, token:`access_token`, 用户原始信息:`raw-user` 209 | */ 210 | 211 | +(NSDictionary*)userInfo:(AVOSCloudSNSType)type; 212 | 213 | /** 214 | * 判断指定的社交平台已经缓存的用户信息是否过期 215 | * 216 | * @param type 指定平台类型 217 | * @return 是否过期 218 | */ 219 | +(BOOL)doesUserExpireOfPlatform:(AVOSCloudSNSType)type; 220 | 221 | 222 | /** 223 | * 刷新用户授权时间 224 | * @discussion 如果当前用户授权没有过期,则无需用户操作, 登录过程一闪而过. 如果授权过期,则需要用户重新授权 225 | * 226 | * @param type 指定平台类型 227 | * @param callback 登录结果回调 228 | */ 229 | +(void)refreshToken:(AVOSCloudSNSType)type withCallback:(AVSNSResultBlock)callback; 230 | 231 | #pragma mark - Deprecated 232 | 233 | + (UIViewController*)loginManualyWithCallback:(AVSNSResultBlock)callback AVDeprecated("Use loginManuallyWithCallback: instead"); 234 | + (UIViewController *)loginManualyWithURL:(NSURL *)url callback:(AVSNSResultBlock)callback AVDeprecated("Use loginManuallyWithURL:callback instead."); 235 | + (UIViewController*)loginManualyWithCallback:(AVSNSResultBlock)callback toPlatform:(AVOSCloudSNSType)type AVDeprecated("Use loginManuallyWithCallback:toPlatform: instead."); 236 | 237 | @end 238 | 239 | -------------------------------------------------------------------------------- /Classes/AVOSCloudSNSUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVOSCloudSNSUtils.h 3 | // AVOSCloudSNS 4 | // 5 | // Created by Travis on 13-10-21. 6 | // Copyright (c) 2013年 AVOS. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AVOSCloudSNS.h" 11 | #import 12 | 13 | #ifdef DEBUG 14 | # define SLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__) 15 | #else 16 | # define SLog(fmt, ...) 17 | #endif 18 | 19 | #define NameStringOfParam(param) [NSString stringWithFormat:@"%s", #param] 20 | 21 | @interface AVOSCloudSNSUtils : NSObject 22 | + (NSString *)serializeURL:(NSString *)baseURL params:(NSDictionary *)params; 23 | + (NSDictionary *)unserializeURL:(NSString *)url; 24 | + (NSDictionary *)unserializeJSONP:(NSString *)jsonp; 25 | 26 | +(NSDate*)expireDateWithOffset:(NSInteger)offset; 27 | 28 | #pragma mark - 29 | 30 | +(NSString *)stringFromDate:(NSDate *)date; 31 | 32 | + (NSError *)errorWithText:(NSString *)format, ... NS_FORMAT_FUNCTION(1, 2); 33 | 34 | #pragma mark - Block 35 | 36 | + (void)callBooleanResultBlock:(AVBooleanResultBlock)block 37 | error:(NSError *)error; 38 | 39 | + (void)callIntegerResultBlock:(AVIntegerResultBlock)block 40 | number:(NSInteger)number 41 | error:(NSError *)error; 42 | 43 | + (void)callArrayResultBlock:(AVArrayResultBlock)block 44 | array:(NSArray *)array 45 | error:(NSError *)error; 46 | 47 | + (void)callObjectResultBlock:(AVObjectResultBlock)block 48 | object:(AVObject *)object 49 | error:(NSError *)error; 50 | 51 | + (void)callUserResultBlock:(AVUserResultBlock)block 52 | user:(AVUser *)user 53 | error:(NSError *)error; 54 | 55 | + (void)callIdResultBlock:(AVIdResultBlock)block 56 | object:(id)object 57 | error:(NSError *)error; 58 | 59 | + (void)callProgressBlock:(AVProgressBlock)block 60 | percent:(NSInteger)percentDone; 61 | 62 | 63 | + (void)callImageResultBlock:(AVImageResultBlock)block 64 | image:(UIImage *)image 65 | error:(NSError *)error; 66 | 67 | + (void)callFileResultBlock:(AVFileResultBlock)block 68 | AVFile:(AVFile *)file 69 | error:(NSError *)error; 70 | 71 | +(void)callSetResultBlock:(AVSetResultBlock)block 72 | set:(NSSet *)set 73 | error:(NSError *)error; 74 | +(void)callCloudQueryResultBlock:(AVCloudQueryCallback)block 75 | result:(AVCloudQueryResult *)result 76 | error:error; 77 | 78 | + (NSString*)calMD5:(NSString*)input; 79 | 80 | 81 | @end 82 | -------------------------------------------------------------------------------- /Classes/AVOSCloudSNSUtils.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVOSCloudSNSUtils.m 3 | // AVOSCloudSNS 4 | // 5 | // Created by Travis on 13-10-21. 6 | // Copyright (c) 2013年 AVOS. All rights reserved. 7 | // 8 | 9 | #define dateFormat @"yyyy'-'MM'-'dd'T'HH':'mm':'ss.SSS'Z'" 10 | 11 | #import "AVOSCloudSNSUtils.h" 12 | #import 13 | 14 | @implementation AVOSCloudSNSUtils 15 | + (NSString *)serializeURL:(NSString *)baseURL params:(NSDictionary *)params{ 16 | NSURL* parsedURL = [NSURL URLWithString:baseURL]; 17 | 18 | NSString* queryPrefix=nil; 19 | 20 | if ([baseURL hasSuffix:@"?"]) { 21 | queryPrefix=@""; 22 | }else{ 23 | queryPrefix=parsedURL.query?@"&":@"?"; 24 | } 25 | 26 | 27 | NSMutableArray* pairs = [NSMutableArray array]; 28 | for (NSString* key in [params keyEnumerator]) 29 | { 30 | CFStringRef escaped_value= CFURLCreateStringByAddingPercentEscapes( 31 | NULL, /* allocator */ 32 | (CFStringRef)[params objectForKey:key], 33 | NULL, /* charactersToLeaveUnescaped */ 34 | (CFStringRef)@"!*'();:@&=+$,/?%#[]", 35 | kCFStringEncodingUTF8); 36 | 37 | [pairs addObject:[NSString stringWithFormat:@"%@=%@", key, (__bridge NSString*)escaped_value]]; 38 | CFRelease(escaped_value); 39 | 40 | } 41 | NSString* query = [pairs componentsJoinedByString:@"&"]; 42 | 43 | return [NSString stringWithFormat:@"%@%@%@", baseURL, queryPrefix, query]; 44 | } 45 | 46 | + (NSDictionary *)unserializeURL:(NSString *)url 47 | { 48 | NSArray *cpmts= [url componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"?#&"]]; 49 | 50 | if (cpmts.count<2) { 51 | return nil; 52 | } 53 | 54 | NSMutableDictionary *dict=[NSMutableDictionary dictionary]; 55 | for (int i=1; i 10 | #import 11 | 12 | @interface AVSNSHttpClient : NSObject 13 | 14 | +(AVSNSHttpClient *)sharedInstance; 15 | 16 | -(void)postObject:(NSString *)path 17 | withParameters:(NSDictionary *)parameters 18 | block:(AVIdResultBlock)block; 19 | 20 | -(void)getObject:(NSString *)path 21 | withParameters:(NSDictionary *)parameters 22 | block:(AVIdResultBlock)block; 23 | 24 | -(void)putObject:(NSString *)path 25 | withParameters:(NSDictionary *)parameters 26 | block:(AVIdResultBlock)block; 27 | 28 | -(void)deleteObject:(NSString *)path 29 | withParameters:(NSDictionary *)parameters 30 | block:(AVIdResultBlock)block; 31 | 32 | @end 33 | -------------------------------------------------------------------------------- /Classes/AVSNSHttpClient.m: -------------------------------------------------------------------------------- 1 | // 2 | // LCHttpClient.m 3 | // SocialNetwork 4 | // 5 | // Created by Feng Junwen on 5/15/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import "AVSNSHttpClient.h" 10 | #import "AVOSCloudSNSUtils.h" 11 | #import "LCRouter.h" 12 | 13 | @interface AVSNSHttpClient () 14 | 15 | @property (nonatomic, strong) NSURL *baseURL; 16 | @property (nonatomic, strong) NSOperationQueue *operationQueue; 17 | 18 | @end 19 | 20 | @implementation AVSNSHttpClient 21 | 22 | + (AVSNSHttpClient*)sharedInstance { 23 | static dispatch_once_t once; 24 | static AVSNSHttpClient * sharedInstance; 25 | dispatch_once(&once, ^{ 26 | sharedInstance = [[self alloc] init]; 27 | sharedInstance.baseURL = [NSURL URLWithString:@"https://api.leancloud.cn/1.1/"]; 28 | sharedInstance.operationQueue = [[NSOperationQueue alloc] init]; 29 | }); 30 | return sharedInstance; 31 | } 32 | 33 | - (NSString *)queryStringFromParameters:(NSDictionary *)parameters { 34 | NSMutableString *queries = [[NSMutableString alloc] init]; 35 | NSArray *keys = [parameters allKeys]; 36 | for (int i = 0; i < keys.count; i++) { 37 | if (i != 0) { 38 | [queries appendString:@"&"]; 39 | } 40 | NSString *value = [parameters valueForKey:keys[i]]; 41 | [queries appendFormat:@"%@=%@", keys[i], value]; 42 | } 43 | return [queries stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 44 | } 45 | 46 | - (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters { 47 | 48 | NSURL *url = [NSURL URLWithString:path]; 49 | 50 | if (!url.scheme.length) { 51 | NSString *URLString = [[LCRouter sharedInstance] URLStringForPath:path]; 52 | url = [NSURL URLWithString:URLString]; 53 | } 54 | 55 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url]; 56 | [request setValue:[AVOSCloud getApplicationId] forHTTPHeaderField:@"X-AVOSCloud-Application-Id"]; 57 | 58 | NSString *timestamp=[NSString stringWithFormat:@"%.0f",1000*[[NSDate date] timeIntervalSince1970]]; 59 | NSString *sign=[AVOSCloudSNSUtils calMD5:[NSString stringWithFormat:@"%@%@",timestamp,[AVOSCloud getClientKey]]]; 60 | NSString *headerValue=[NSString stringWithFormat:@"%@,%@",sign,timestamp]; 61 | [request setValue:headerValue forHTTPHeaderField:@"X-AVOSCloud-Request-Sign"]; 62 | 63 | // [request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; 64 | [request setTimeoutInterval:kAVDefaultNetworkTimeoutInterval]; 65 | [request setHTTPMethod:method]; 66 | if ([method isEqualToString:@"GET"] || [method isEqualToString:@"DELETE"]) { 67 | url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:@"?%@", [self queryStringFromParameters:parameters]]]; 68 | [request setURL:url]; 69 | } else { 70 | [request setValue:[NSString stringWithFormat:@"application/json"] forHTTPHeaderField:@"Content-Type"]; 71 | NSError *error; 72 | [request setHTTPBody:[NSJSONSerialization dataWithJSONObject:parameters options:0 error:&error]]; 73 | if (error) { 74 | SLog(@"%@ error : %@", [self class], error); 75 | } 76 | } 77 | return request; 78 | } 79 | 80 | - (void)goRequest:(NSURLRequest *)request block:(AVIdResultBlock)block { 81 | SLog(@"request url : %@", request.URL); 82 | SLog(@"request headers : %@", [request allHTTPHeaderFields]); 83 | SLog(@"request body %@", [[NSString alloc] initWithData:[request HTTPBody] encoding:NSUTF8StringEncoding]); 84 | [NSURLConnection sendAsynchronousRequest:request queue:self.operationQueue completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { 85 | if (connectionError) { 86 | block(nil, connectionError); 87 | } else { 88 | if (response && data.length > 0) { 89 | NSError *error; 90 | NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error]; 91 | if (error) { 92 | NSString *responseString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 93 | SLog(@"reponse : %@", responseString); 94 | block(nil, [AVOSCloudSNSUtils errorWithText:@"HTTP request failed, reponse string: %@", responseString]); 95 | } else { 96 | SLog(@"reponse : %@", dictionary); 97 | block(dictionary, nil); 98 | } 99 | } 100 | } 101 | }]; 102 | } 103 | 104 | - (NSDictionary *)whereDictionaryFromConditions:(NSDictionary *)conditions { 105 | NSDictionary *where = [NSDictionary dictionary]; 106 | if (conditions.count>0) { 107 | NSData *data = [NSJSONSerialization dataWithJSONObject:conditions options:0 error:nil]; 108 | NSString *conditionString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; 109 | where = @{@"where":conditionString}; 110 | } 111 | return where; 112 | } 113 | 114 | -(void)getObject:(NSString *)path 115 | withParameters:(NSDictionary *)parameters 116 | block:(AVIdResultBlock)block { 117 | NSMutableURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:[self whereDictionaryFromConditions:parameters]]; 118 | [self goRequest:request block:block]; 119 | } 120 | 121 | -(void)postObject:(NSString *)path 122 | withParameters:(NSDictionary *)parameters 123 | block:(AVIdResultBlock)block 124 | { 125 | NSMutableURLRequest *request= [self requestWithMethod:@"POST" path:path parameters:parameters]; 126 | [self goRequest:request block:block]; 127 | } 128 | 129 | -(void)putObject:(NSString *)path 130 | withParameters:(NSDictionary *)parameters 131 | block:(AVIdResultBlock)block { 132 | NSMutableURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters]; 133 | [self goRequest:request block:block]; 134 | } 135 | 136 | -(void)deleteObject:(NSString *)path 137 | withParameters:(NSDictionary *)parameters 138 | block:(AVIdResultBlock)block { 139 | NSMutableURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters]; 140 | [self goRequest:request block:block]; 141 | } 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /Classes/AVSNSLoginViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVSNSLoginViewController.h 3 | // AVOSCloudSNS 4 | // 5 | // Created by Travis on 13-10-21. 6 | // Copyright (c) 2013年 AVOS. All rights reserved. 7 | // 8 | 9 | #import "AVWebViewController.h" 10 | 11 | @interface AVSNSLoginViewController :AVWebViewController 12 | @property(nonatomic) AVOSCloudSNSType type; 13 | 14 | -(void)loginToPlatform:(AVOSCloudSNSType)type; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /Classes/AVSNSLoginViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVSNSLoginViewController.m 3 | // AVOSCloudSNS 4 | // 5 | // Created by Travis on 13-10-21. 6 | // Copyright (c) 2013年 AVOS. All rights reserved. 7 | // 8 | 9 | #import "AVOSCloudSNS.h" 10 | #import "AVSNSLoginViewController.h" 11 | #import "AVOSCloudSNSUtils.h" 12 | #import "AVOSCloudSNS_.h" 13 | 14 | static NSString * const AVOS_SNS_BASE_URL=@"cn.avoscloud.com"; 15 | static NSString * const AVOS_SNS_BASE_URL2=@"leancloud.cn"; 16 | static NSString * const AVOS_SNS_API_VERSION=@"1"; 17 | 18 | @interface AVSNSLoginViewController() 19 | @property(nonatomic,copy) NSString *redirect_uri; 20 | @property(nonatomic,copy) NSString *appkey; 21 | @property(nonatomic,copy) NSString *appsec; 22 | 23 | @property(nonatomic) BOOL hasCode; 24 | @end 25 | @implementation AVSNSLoginViewController 26 | 27 | - (void)dealloc 28 | { 29 | //NSLog(@"AVSNSLoginViewController dealloc"); 30 | } 31 | 32 | -(void)close:(UIBarButtonItem*)item{ 33 | [self close]; 34 | [AVOSCloudSNS onCancel:self.type]; 35 | } 36 | 37 | -(void)setType:(AVOSCloudSNSType)type{ 38 | _type=type; 39 | if (type==0) { 40 | return; 41 | } 42 | NSDictionary *config=[[AVOSCloudSNS ssoConfigs] objectForKey:@(self.type)]; 43 | NSString *appkey=config[@"appkey"]; 44 | 45 | if (appkey) { 46 | //用设置的key尝试直接登陆 47 | self.appkey=appkey; 48 | self.appsec=config[@"appsec"]; 49 | self.redirect_uri=config[@"redirect_uri"]; 50 | } 51 | 52 | } 53 | 54 | -(void)viewDidLoad{ 55 | [super viewDidLoad]; 56 | self.title=@"帐号绑定"; 57 | 58 | self.navigationItem.leftBarButtonItem=[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(close:)]; 59 | } 60 | -(void)loginToPlatform:(AVOSCloudSNSType)type{ 61 | if (type==0) { 62 | //打开选择登录界面 63 | 64 | NSString *url=[NSString stringWithFormat:@"https://%@/%@",AVOS_SNS_BASE_URL2,@"sns.html"]; 65 | 66 | [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]]; 67 | return; 68 | } 69 | 70 | if (self.appkey) { 71 | [self startWebAuth]; 72 | } else { 73 | NSString *sub=nil; 74 | 75 | switch (type) { 76 | case AVOSCloudSNSSinaWeibo: 77 | sub= [NSString stringWithFormat:@"%@/oauth2/goto/weibo?mobile_sns=true", AVOS_SNS_API_VERSION]; 78 | break; 79 | case AVOSCloudSNSQQ: 80 | sub= [NSString stringWithFormat:@"%@/oauth2/goto/qq?mobile_sns=true", AVOS_SNS_API_VERSION]; 81 | break; 82 | 83 | default: 84 | break; 85 | } 86 | 87 | NSString *url=[NSString stringWithFormat:@"https://%@/%@",AVOS_SNS_BASE_URL,sub]; 88 | [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]]; 89 | } 90 | } 91 | 92 | 93 | -(void)startWebAuth{ 94 | 95 | switch (self.type) { 96 | case AVOSCloudSNSSinaWeibo: 97 | { 98 | NSDictionary *param=@{ 99 | @"client_id":self.appkey, 100 | @"redirect_uri":self.redirect_uri, 101 | @"display":@"mobile", 102 | }; 103 | 104 | NSURLRequest *req = [[LCSHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:@"https://open.weibo.cn/oauth2/authorize" parameters:param error:nil]; 105 | 106 | [self.webView loadRequest:req]; 107 | } 108 | break; 109 | 110 | 111 | case AVOSCloudSNSQQ: 112 | { 113 | NSDictionary *params=@{ 114 | @"client_id":self.appkey, 115 | @"redirect_uri":self.redirect_uri, 116 | @"display":@"mobile", 117 | @"scope":@"get_simple_userinfo,list_album,upload_pic,do_like", 118 | @"response_type":@"token", 119 | @"which":@"Login", 120 | @"ucheck":@1, 121 | @"fall_to_wv":@1 122 | }; 123 | NSURLRequest *req = [[LCSHTTPRequestSerializer serializer] requestWithMethod:@"GET" URLString:@"http://openmobile.qq.com/oauth2.0/m_show" parameters:params error:nil]; 124 | 125 | [self.webView loadRequest:req]; 126 | } 127 | break; 128 | case AVOSCloudSNSWeiXin: 129 | break; 130 | } 131 | } 132 | 133 | -(void)getAccessToken:(NSString*)code{ 134 | 135 | 136 | NSString *url=nil; 137 | NSDictionary *param=nil; 138 | 139 | switch (self.type) { 140 | case AVOSCloudSNSSinaWeibo: 141 | { 142 | param=@{ 143 | @"client_id":self.appkey, 144 | @"client_secret":self.appsec, 145 | @"redirect_uri":self.redirect_uri, 146 | @"grant_type":@"authorization_code", 147 | @"code":code 148 | }; 149 | 150 | url=@"https://api.weibo.com/oauth2/access_token"; 151 | } 152 | break; 153 | 154 | case AVOSCloudSNSQQ: 155 | { 156 | param=@{ 157 | @"client_id":self.appkey, 158 | @"client_secret":self.appsec, 159 | @"redirect_uri":self.redirect_uri, 160 | @"grant_type":@"authorization_code", 161 | @"code":code 162 | }; 163 | 164 | url=@"https://graph.qq.com/oauth2.0/token"; 165 | } 166 | default: 167 | return; 168 | } 169 | 170 | [[AVOSCloudSNS requestManager] POST:url parameters:param success:^(LCSHTTPRequestOperation *operation, id responseObject) { 171 | NSDictionary *info=[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil]; 172 | 173 | NSString *token=info[@"access_token"]; 174 | 175 | if (token) { 176 | NSString *uid=info[@"uid"]; 177 | if (uid==nil) { 178 | uid=info[@"openid"]; 179 | } 180 | //FIXME: check other uid param if need 181 | [AVOSCloudSNS onSuccess:self.type withToken:token andExpires:info[@"expires_in"] andUid:uid]; 182 | }else{ 183 | NSError *error=nil; 184 | //TODO: return unknow error 185 | [AVOSCloudSNS onFail:self.type withError:error]; 186 | } 187 | } failure:^(LCSHTTPRequestOperation *operation, NSError *error) { 188 | 189 | }]; 190 | } 191 | 192 | -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ 193 | NSString *url=[request.URL absoluteString]; 194 | NSLog(@"Open: %@", url); 195 | NSString *avos_callback_prefix=[NSString stringWithFormat:@"%@/%@/%@",AVOS_SNS_BASE_URL, AVOS_SNS_API_VERSION, @"oauth2/"]; 196 | NSString *avos_callback_prefix2=[NSString stringWithFormat:@"%@/%@/%@",AVOS_SNS_BASE_URL2, AVOS_SNS_API_VERSION, @"oauth2/"]; 197 | 198 | if ([url rangeOfString:avos_callback_prefix].length>0||[url rangeOfString:avos_callback_prefix2].length>0) { 199 | //avos 认证, 加载页面JSON 200 | 201 | NSRegularExpression *reg=[NSRegularExpression regularExpressionWithPattern:@"[&|\?]code=" options:NSRegularExpressionCaseInsensitive error:nil]; 202 | 203 | BOOL hasCode= [reg numberOfMatchesInString:url options:0 range:NSMakeRange(0, [url length])]; 204 | 205 | if(hasCode){ 206 | //avos返回用户信息 207 | [[AVOSCloudSNS requestManager] GET:url parameters:nil success:^(LCSHTTPRequestOperation *operation, id responseObject) { 208 | NSDictionary *info=[NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingAllowFragments error:nil]; 209 | info=info[@"user"]; 210 | if (info) { 211 | [AVOSCloudSNS onSuccess:self.type withParams:info]; 212 | }else{ 213 | NSLog(@"avos server format error"); 214 | NSError *err=[NSError errorWithDomain:AVOSCloudSNSErrorDomain code:9999 userInfo:info]; 215 | [AVOSCloudSNS onFail:self.type withError:err]; 216 | } 217 | } failure:^(LCSHTTPRequestOperation *operation, NSError *error) { 218 | [AVOSCloudSNS onFail:self.type withError:error]; 219 | }]; 220 | return NO; 221 | }else if ([url rangeOfString:@"access_denied"].length>0) { 222 | //用户取消 223 | [AVOSCloudSNS onFail:self.type withError:[NSError errorWithDomain:AVOSCloudSNSErrorDomain code:AVOSCloudSNSErrorUserCancel userInfo:nil]]; 224 | return NO; 225 | }else if(self.type==0){ 226 | //获取用户选择的登录平台 227 | if ([url rangeOfString:@"goto/weibo"].length) { 228 | self.type=AVOSCloudSNSSinaWeibo; 229 | }else if ([url rangeOfString:@"goto/qq"].length) { 230 | self.type=AVOSCloudSNSQQ; 231 | }else if ([url rangeOfString:@"goto/"].length) { 232 | NSAssert(NO, @"SDK not support this platform!"); 233 | } 234 | 235 | if (self.appkey) { 236 | //检查是否SSO登录 237 | if ([AVOSCloudSNS SSO:self.type]) 238 | return NO; 239 | 240 | //web认证 241 | [self performSelector:@selector(startWebAuth) withObject:nil afterDelay:0.1]; 242 | 243 | return NO; 244 | } 245 | } 246 | 247 | }else if(self.redirect_uri && [url hasPrefix:self.redirect_uri]){ 248 | NSDictionary *param= [AVOSCloudSNSUtils unserializeURL:url]; 249 | NSString *code=param[@"code"]; 250 | NSString *token=param[@"access_token"]; 251 | if (code) { 252 | [self getAccessToken:code]; 253 | return NO; 254 | }else if (token && self.type==AVOSCloudSNSQQ){ 255 | [[AVOSCloudSNS requestManager] GET:[NSString stringWithFormat:@"https://graph.qq.com/oauth2.0/me?access_token=%@",token] parameters:nil success:^(LCSHTTPRequestOperation *operation, id responseObject) { 256 | NSString *string=[[NSString alloc] initWithBytes:[responseObject bytes] length:[responseObject length] encoding:NSUTF8StringEncoding]; 257 | NSDictionary *ret= [AVOSCloudSNSUtils unserializeJSONP:string]; 258 | NSString *openid=ret[@"openid"]; 259 | [AVOSCloudSNS onSuccess:self.type withToken:token andExpires:param[@"expires_in"] andUid:openid]; 260 | } failure:^(LCSHTTPRequestOperation *operation, NSError *error) { 261 | [AVOSCloudSNS onFail:AVOSCloudSNSQQ withError:error]; 262 | }]; 263 | } 264 | }else{ 265 | // NSLog(@"Open :%@",url); 266 | } 267 | 268 | return YES; 269 | } 270 | 271 | -(void)showWait{ 272 | UIActivityIndicatorView *view=[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; 273 | [view startAnimating]; 274 | self.navigationItem.rightBarButtonItem=[[UIBarButtonItem alloc] initWithCustomView:view]; 275 | } 276 | 277 | -(void)hideWait{ 278 | self.navigationItem.rightBarButtonItem=nil; 279 | } 280 | 281 | -(void)webViewDidFinishLoad:(UIWebView *)webView{ 282 | [self hideWait]; 283 | } 284 | 285 | -(void)webViewDidStartLoad:(UIWebView *)webView{ 286 | [self showWait]; 287 | } 288 | 289 | -(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error{ 290 | 291 | if ((error.code==101 || error.code == 102) && [error.domain isEqualToString:@"WebKitErrorDomain"]) { 292 | //ignore 293 | }else{ 294 | [self hideWait]; 295 | [AVOSCloudSNS onFail:self.type withError:error]; 296 | } 297 | 298 | } 299 | 300 | @end 301 | -------------------------------------------------------------------------------- /Classes/AVSNSWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVSNSWebViewController.h 3 | // AVOS 4 | // 5 | // Created by Qihe Bian on 11/28/14. 6 | // 7 | // 8 | 9 | #import "AVWebViewController.h" 10 | #import "AVOSCloudSNS.h" 11 | @interface AVSNSWebViewController : AVWebViewController 12 | @property(nonatomic, copy)AVSNSResultBlock callback; 13 | @end 14 | -------------------------------------------------------------------------------- /Classes/AVSNSWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVSNSWebViewController.m 3 | // AVOS 4 | // 5 | // Created by Qihe Bian on 11/28/14. 6 | // 7 | // 8 | 9 | #import "AVSNSWebViewController.h" 10 | #import "NSURL+AVAdditions.h" 11 | 12 | extern NSString * const AVOSCloudSNSErrorDomain; 13 | @interface AVSNSWebViewController () 14 | 15 | @end 16 | 17 | @implementation AVSNSWebViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | self.title = @"账号绑定"; 22 | self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(close)]; 23 | } 24 | 25 | - (NSDictionary *)extractParamsFromUrl:(NSURL *)url { 26 | NSMutableDictionary *params = [[url av_queryDictionary] mutableCopy]; 27 | return params; 28 | } 29 | 30 | -(void)dispatchActionWithURL:(NSURL *)url { 31 | NSDictionary *params = [self extractParamsFromUrl:url]; 32 | NSString *param = [params objectForKey:@"param"]; 33 | NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[param dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:NULL]; 34 | [self close]; 35 | if (self.callback) { 36 | NSString *errorString = [dict objectForKey:@"error"]; 37 | if (errorString) { 38 | self.callback(nil, [NSError errorWithDomain:AVOSCloudSNSErrorDomain code:5 userInfo:@{@"error":errorString}]); 39 | } else { 40 | self.callback(dict, nil); 41 | } 42 | } 43 | } 44 | 45 | @end 46 | -------------------------------------------------------------------------------- /Classes/AVUser+SNS.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVUser+SNS.h 3 | // VZ 4 | // 5 | // Created by Travis on 13-10-28. 6 | // Copyright (c) 2013 AVOS. All rights reserved. 7 | // 8 | 9 | #ifndef VZ_AVUser_SNS_h 10 | #define VZ_AVUser_SNS_h 11 | 12 | #import 13 | 14 | #if !defined(AVDeprecated) 15 | # define AVDeprecated(explain) __attribute__((deprecated(explain))) 16 | #endif 17 | 18 | extern NSString *const AVOSCloudSNSPlatformWeiBo; 19 | extern NSString *const AVOSCloudSNSPlatformQQ; 20 | extern NSString *const AVOSCloudSNSPlatformWeiXin; 21 | 22 | @interface AVUser(SNS) 23 | 24 | /** 25 | * 用SNS数据登录 26 | * 27 | * @discussion 如果登录成功, 会将currentUser设置为登录后返回的AVUser或其子类 28 | * @param authData SNS平台登录返回的用户验证数据.不同平台数据字段不同 29 | * 微博平台 30 | * { 31 | * "uid": "123456789", 32 | * "access_token": "2.00vs3XtCI5FevCff4981adb5jj1lXE", 33 | * "expiration_in": "36000" 34 | * } 35 | * QQ平台 36 | * { 37 | * "openid": "0395BA18A5CD6255E5BA185E7BEBA242", 38 | * "access_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU", 39 | * "expires_in": 1382686496 40 | * } 41 | * 微信平台 42 | * { 43 | * "openid": "0395BA18A5CD6255E5BA185E7BEBA242", 44 | * "access_token": "12345678-SaMpLeTuo3m2avZxh5cjJmIrAfx4ZYyamdofM7IjU", 45 | * "expires_in": 1382686496 46 | * } 47 | * @param platform 平台,如weibo、qq、weixin 48 | * @param block 完成后回调 49 | * 50 | */ 51 | +(void)loginWithAuthData:(NSDictionary*)authData platform:(NSString *)platform block:(AVUserResultBlock)block 52 | __deprecated_msg("Deprecated, use +[loginOrSignUpWithAuthData:platform:block:] instead."); 53 | 54 | /** 55 | * 给用户绑定SNS验证数据 56 | * @discussion 登录成功, 如果此用户为新建用户, 则自动设置为currentUser 57 | * 58 | * @param authData SNS平台登录返回的用户验证数据. 不同平台数据字段不同,参考 +[loginWithAuthData:platform:block:] 59 | * @param platform 平台,如weibo、qq、weixin 60 | * @param block 完成后回调 61 | * 62 | */ 63 | -(void)addAuthData:(NSDictionary*)authData platform:(NSString *)platform block:(AVUserResultBlock)block 64 | __deprecated_msg("Deprecated, use -[associateWithAuthData:platform:block:] instead."); 65 | 66 | /** 67 | * 取消SNS绑定 68 | * @discussion 此操作同时会注销指定的平台登录 69 | * @param platform 平台,如weibo、qq、weixin 70 | * @param block 完成后回调 71 | * 72 | */ 73 | -(void)deleteAuthDataForPlatform:(NSString *)platform block:(AVUserResultBlock)block 74 | __deprecated_msg("Deprecated, use -[disassociateWithPlatform:block:] instead."); 75 | 76 | /** 77 | * 用SNS数据登录 78 | * 79 | * @discussion 如果登录成功, 会将currentUser设置为登录后返回的AVUser或其子类 80 | * @param authData SNS平台登录返回的用户验证数据. 必需包含字段:`id`,`access_token`,`expires_at`,`platform` 81 | * @param block 完成后回调 82 | * 83 | */ 84 | +(void)loginWithAuthData:(NSDictionary*)authData block:(AVUserResultBlock)block 85 | __deprecated_msg("使用+[loginWithAuthData:platform:block:]"); 86 | 87 | /** 88 | * 给用户绑定SNS验证数据 89 | * @discussion 登录成功, 如果此用户为新建用户, 则自动设置为currentUser 90 | * 91 | * @param authData SNS平台登录返回的用户验证数据. 必需包含字段:`id`,`access_token`,`expires_at`,`platform` 92 | * @param block 完成后回调 93 | * 94 | */ 95 | -(void)addAuthData:(NSDictionary*)authData block:(AVUserResultBlock)block 96 | __deprecated_msg("使用-[addAuthData:platform:block:]"); 97 | 98 | @end 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /Classes/AVUser+SNS.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVUser+SNS.h 3 | // VZ 4 | // 5 | // Created by Travis on 13-10-28. 6 | // Copyright (c) 2013 AVOS. All rights reserved. 7 | // 8 | #import "AVUser+SNS.h" 9 | #import "AVSNSHttpClient.h" 10 | #import "AVOSCloudSNSUtils.h" 11 | #import "AVOSCloudSNS.h" 12 | #import "AVUser_SNSInternal.h" 13 | 14 | NSString *const AVOSCloudSNSPlatformWeiBo = @"weibo"; 15 | NSString *const AVOSCloudSNSPlatformQQ = @"qq"; 16 | NSString *const AVOSCloudSNSPlatformWeiXin = @"weixin"; 17 | 18 | @implementation AVUser(SNS) 19 | 20 | + (NSString*)nameOfPlatform:(AVOSCloudSNSType)type { 21 | switch (type) { 22 | case AVOSCloudSNSQQ: return @"qq"; 23 | case AVOSCloudSNSSinaWeibo: return @"weibo"; 24 | case AVOSCloudSNSWeiXin: return AVOSCloudSNSPlatformWeiXin; 25 | } 26 | return nil; 27 | } 28 | 29 | - (AVOSCloudSNSType)platformFromName:(NSString *)name { 30 | if ([name isEqualToString:AVOSCloudSNSPlatformQQ]) { 31 | return AVOSCloudSNSQQ; 32 | } else if ([name isEqualToString:AVOSCloudSNSPlatformWeiBo]) { 33 | return AVOSCloudSNSSinaWeibo; 34 | } else if ([name isEqualToString:AVOSCloudSNSPlatformWeiXin]) { 35 | return AVOSCloudSNSWeiXin; 36 | } else { 37 | return -1; 38 | } 39 | } 40 | 41 | - (BOOL)isInternalSupportPlatform:(NSString *)platformName { 42 | return (int)([self platformFromName:platformName]) != -1; 43 | } 44 | 45 | +(NSDictionary *)authDataFromSNSResult:(NSDictionary*)authData{ 46 | NSString *idname=nil; 47 | 48 | AVOSCloudSNSType type=[authData[@"platform"] intValue]; 49 | switch (type) { 50 | case AVOSCloudSNSQQ: 51 | idname=@"openid"; 52 | break; 53 | case AVOSCloudSNSWeiXin: 54 | idname = @"openid"; 55 | break; 56 | case AVOSCloudSNSSinaWeibo: 57 | default: 58 | idname=@"uid"; 59 | break; 60 | } 61 | 62 | id expValue=authData[@"expires_at"]; 63 | NSString *exp=[expValue isKindOfClass:[NSDate class]]?[AVOSCloudSNSUtils stringFromDate:expValue]:expValue; 64 | 65 | return @{ 66 | idname:authData[@"id"], 67 | @"access_token":authData[@"access_token"], 68 | @"expires_at":exp, 69 | }; 70 | } 71 | 72 | +(NSDictionary *)authDataFromSNSResult:(NSDictionary*)authData platform:(NSString *)platform error:(NSError **)error { 73 | if ([authData objectForKey:@"platform"]) { 74 | NSNumber *platform = [authData objectForKey:@"platform"]; 75 | if ([platform isKindOfClass:[NSNumber class]]) { 76 | AVOSCloudSNSType type = [platform intValue]; 77 | if (type == AVOSCloudSNSQQ || type == AVOSCloudSNSSinaWeibo 78 | || type == AVOSCloudSNSWeiXin) { 79 | return [self authDataFromSNSResult:authData]; 80 | } 81 | } 82 | } 83 | if (!authData || !platform) { 84 | *error = [NSError errorWithDomain:@"AVOSClouudSNSDomain" code:0 userInfo:@{@"reason":@"authData or platform is nil"}]; 85 | return nil; 86 | } 87 | NSArray *needKeys = nil; 88 | NSMutableString *keysString = nil; 89 | if ([platform isEqualToString:AVOSCloudSNSPlatformWeiBo]) { 90 | needKeys = @[@"uid", @"access_token", @"expiration_in"]; 91 | } else if ([platform isEqualToString:AVOSCloudSNSPlatformQQ]) { 92 | needKeys = @[@"openid", @"access_token", @"expires_in"]; 93 | } else if ([platform isEqualToString:AVOSCloudSNSPlatformWeiXin]) { 94 | needKeys = @[@"openid", @"access_token", @"expires_in"]; 95 | } else { 96 | return authData; 97 | } 98 | for (NSString *key in needKeys) { 99 | if (!keysString) { 100 | keysString = [[NSMutableString alloc] initWithString:key]; 101 | } else { 102 | [keysString appendFormat:@",%@", key]; 103 | } 104 | } 105 | NSArray *keys = [authData allKeys]; 106 | for (NSString *key in needKeys) { 107 | if (![keys containsObject:key]) { 108 | *error = [NSError errorWithDomain:@"AVOSClouudSNSDomain" code:0 userInfo:@{@"message":[NSString stringWithFormat:@"authData for platform %@ should have keys %@", platform, keysString],@"reason":[NSString stringWithFormat:@"key %@ not found", key]}]; 109 | return nil; 110 | } 111 | } 112 | return authData; 113 | } 114 | 115 | -(void)deleteAuthDataForPlatform:(NSString *)platform block:(AVUserResultBlock)block { 116 | NSMutableDictionary *dict = [[self objectForKey:@"authData"] mutableCopy]; 117 | [dict removeObjectForKey:platform]; 118 | [self setObject:dict forKey:@"authData"]; 119 | 120 | if ([self isInternalSupportPlatform:platform]) { 121 | [AVOSCloudSNS logout:[self platformFromName:platform]]; 122 | } 123 | if (self.objectId && self.sessionToken) { 124 | [self saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) { 125 | [AVOSCloudSNSUtils callUserResultBlock:block user:self error:error]; 126 | }]; 127 | } else { 128 | [AVOSCloudSNSUtils callUserResultBlock:block user:self error:nil]; 129 | } 130 | } 131 | 132 | + (NSString *)platformNameFromAuthData:(NSDictionary *)authData { 133 | NSNumber *platform = [authData objectForKey:@"platform"]; 134 | NSAssert([platform isKindOfClass:[NSNumber class]], @"The authData should have platform number value"); 135 | AVOSCloudSNSType type = [platform intValue]; 136 | NSString *platformName = [[self class] nameOfPlatform:type]; 137 | NSAssert(platformName != nil, @"The platform is not internally supported"); 138 | return platformName; 139 | } 140 | 141 | -(void)addAuthData:(NSDictionary*)authData block:(AVUserResultBlock)block{ 142 | [self addAuthData:authData platform:[[self class] platformNameFromAuthData:authData] block:block]; 143 | } 144 | 145 | -(void)addAuthData:(NSDictionary*)authData platform:(NSString *)platform block:(AVUserResultBlock)block { 146 | NSError *error = nil; 147 | NSDictionary *authDataResult = [[self class] authDataFromSNSResult:authData platform:platform error:&error]; 148 | if (error) { 149 | [AVOSCloudSNSUtils callUserResultBlock:block user:self error:error]; 150 | return; 151 | } 152 | 153 | if (self.objectId && self.sessionToken) { 154 | //目前API不支持添加 只会覆盖 所以临时会把用户所有的绑定数据同时发一次 (如果服务器准备好, 直接删除下面两行即可) 155 | NSMutableDictionary *dict; 156 | id dictionayValue = [self objectForKey:@"authData"]; 157 | if (dictionayValue && (dictionayValue != [NSNull null])) { 158 | dict = [dictionayValue mutableCopy]; 159 | } else { 160 | dict = [[NSMutableDictionary alloc] init]; 161 | } 162 | [dict setObject:authDataResult forKey:platform]; 163 | [self setObject:dict forKey:@"authData"]; 164 | [self saveEventually:^(BOOL succeeded, NSError *error) { 165 | [AVOSCloudSNSUtils callUserResultBlock:block user:self error:error]; 166 | }]; 167 | }else{ 168 | //这个是新产生的用户, 需要在服务器注册 169 | NSDictionary *dict=@{@"authData":@{platform:authDataResult}}; 170 | [[AVSNSHttpClient sharedInstance] postObject:@"users" withParameters:dict block:^(id object, NSError *error) { 171 | if (!error) { 172 | [self objectFromDictionary:object]; 173 | [self setObject:dict[@"authData"] forKey:@"authData"]; 174 | // todo: fix me! 175 | // [self.requestManager clear]; 176 | [[self class] changeCurrentUser:self save:YES]; 177 | } 178 | [AVOSCloudSNSUtils callUserResultBlock:block user:self error:error]; 179 | }]; 180 | } 181 | } 182 | 183 | +(void)loginWithAuthData:(NSDictionary*)authData block:(AVUserResultBlock)block { 184 | [self loginWithAuthData:authData platform:[[self class] platformNameFromAuthData:authData] block:block]; 185 | } 186 | 187 | +(void)loginWithAuthData:(NSDictionary*)authData platform:(NSString *)platform block:(AVUserResultBlock)block { 188 | NSError *error = nil; 189 | NSDictionary *authDataResult = [self authDataFromSNSResult:authData platform:platform error:&error]; 190 | if (error) { 191 | [AVOSCloudSNSUtils callUserResultBlock:block user:nil error:error]; 192 | return; 193 | } 194 | NSDictionary *dict=@{@"authData":@{platform:authDataResult}}; 195 | [[AVSNSHttpClient sharedInstance] postObject:@"users" withParameters:dict block:^(id object, NSError *error) { 196 | AVUser * user = nil; 197 | if (!error) { 198 | if ([object objectForKey:@"objectId"]) { 199 | // 第一次会返回 200 | // objectId = 55b8b76400b066e34529d4a6; 201 | // sessionToken = fzs03y5g7hr4r22iikhv2babe; 202 | // createdAt = "2015-07-29T11:33:03.642Z"; 203 | // username = mzv62gzwrwzqtz75rv51kzye6; 204 | 205 | user = [self userOrSubclassUser]; 206 | [user objectFromDictionary:object]; 207 | if(!object[@"authData"]){ 208 | [user setObject:dict[@"authData"] forKey:@"authData"]; 209 | } 210 | // todo: fix me! 211 | // [user.requestManager clear]; 212 | [[self class] changeCurrentUser:user save:YES]; 213 | } else { 214 | // {code = 1;error = "无效的第三方数据";} 215 | error = [NSError errorWithDomain:AVOSCloudSNSErrorDomain code:AVOSCloudSNSErrorCodeAuthDataError userInfo:@{NSLocalizedFailureReasonErrorKey: object}]; 216 | } 217 | } 218 | [AVOSCloudSNSUtils callUserResultBlock:block user:user error:error]; 219 | }]; 220 | } 221 | 222 | @end 223 | -------------------------------------------------------------------------------- /Classes/AVUser_SNSInternal.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVUser_Internal.h 3 | // LeanCloudSocial 4 | // 5 | // Created by 陈宜龙 on 12/26/16. 6 | // Copyright © 2016 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import "AVUser.h" 10 | 11 | #define AnonymousIdKey @"LeanCloud.AnonymousId" 12 | 13 | @interface AVUser () 14 | 15 | @property (nonatomic, readwrite, copy) NSString *facebookToken; 16 | @property (nonatomic, readwrite, copy) NSString *twitterToken; 17 | @property (nonatomic, readwrite, copy) NSString *sinaWeiboToken; 18 | @property (nonatomic, readwrite, copy) NSString *qqWeiboToken; 19 | @property (nonatomic, readwrite) BOOL isNew; 20 | @property (nonatomic, readwrite) BOOL mobilePhoneVerified; 21 | 22 | - (BOOL)isAuthDataExistInMemory; 23 | 24 | + (AVUser *)userOrSubclassUser; 25 | 26 | + (NSString *)userTag; 27 | + (BOOL)isAutomaticUserEnabled; 28 | + (void)disableAutomaticUser; 29 | 30 | + (NSString *)endPoint; 31 | - (NSString *)internalClassName; 32 | - (void)setNewFlag:(BOOL)isNew; 33 | 34 | + (void)removeCookies; 35 | 36 | - (NSArray *)linkedServiceNames; 37 | 38 | @end 39 | -------------------------------------------------------------------------------- /Classes/AVWebViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVWebViewController.h 3 | // AVOS 4 | // 5 | // Created by Qihe Bian on 11/28/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface AVWebViewController : UIViewController 12 | @property(nonatomic,retain) UIWebView *webView; 13 | -(void)close; 14 | @end 15 | -------------------------------------------------------------------------------- /Classes/AVWebViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVWebViewController.m 3 | // AVOS 4 | // 5 | // Created by Qihe Bian on 11/28/14. 6 | // 7 | // 8 | 9 | #import "AVWebViewController.h" 10 | 11 | @interface AVWebViewController () 12 | 13 | @end 14 | 15 | @implementation AVWebViewController 16 | 17 | - (id)init 18 | { 19 | self = [super init]; 20 | if (self) { 21 | self.webView=[[UIWebView alloc] initWithFrame:self.view.bounds]; 22 | self.webView.autoresizingMask=UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight; 23 | self.webView.delegate=self; 24 | self.webView.scalesPageToFit=YES; 25 | [self.view addSubview:self.webView]; 26 | 27 | } 28 | return self; 29 | } 30 | 31 | -(BOOL)shouldAutorotate{ 32 | return YES; 33 | } 34 | 35 | -(void)dispatchActionWithURL:(NSURL *)url { 36 | 37 | } 38 | 39 | -(void)close{ 40 | if ([self.navigationController isBeingPresented]|| 41 | [self.navigationController isBeingDismissed] 42 | ) { 43 | [self performSelector:@selector(close) withObject:nil afterDelay:0.25]; 44 | }else{ 45 | [self.navigationController dismissViewControllerAnimated:YES completion:^{ 46 | 47 | }]; 48 | } 49 | } 50 | 51 | -(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{ 52 | NSURL *url = request.URL; 53 | if ([[url.scheme lowercaseString] isEqualToString:@"leancloud"]) { 54 | [self dispatchActionWithURL:url]; 55 | return NO; 56 | } else { 57 | return YES; 58 | } 59 | } 60 | 61 | -(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { 62 | 63 | if ((error.code==101 || error.code == 102) && [error.domain isEqualToString:@"WebKitErrorDomain"]) { 64 | //ignore 65 | }else{ 66 | NSLog(@"%@", error); 67 | } 68 | 69 | } 70 | @end 71 | 72 | -------------------------------------------------------------------------------- /Classes/NSURL+AVAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURL+AVAdditions.h 3 | // AVOS 4 | // 5 | // Created by Qihe Bian on 11/28/14. 6 | // 7 | // 8 | 9 | #import 10 | 11 | @interface NSURL (AVAdditions) 12 | /** 13 | * @return URL's query component as keys/values 14 | * Returns nil for an empty query 15 | */ 16 | - (NSDictionary*) av_queryDictionary; 17 | 18 | /** 19 | * @return URL with keys values appending to query string 20 | * @param queryDictionary Query keys/values 21 | * @param sortedKeys Sorted the keys alphabetically? 22 | * @warning If keys overlap in receiver and query dictionary, 23 | * behaviour is undefined. 24 | */ 25 | - (NSURL*) av_URLByAppendingQueryDictionary:(NSDictionary*) queryDictionary 26 | withSortedKeys:(BOOL) sortedKeys; 27 | 28 | /** As above, but `sortedKeys=NO` */ 29 | - (NSURL*) av_URLByAppendingQueryDictionary:(NSDictionary*) queryDictionary; 30 | 31 | @end 32 | 33 | #pragma mark - 34 | 35 | @interface NSString (AVAdditions) 36 | 37 | /** 38 | * @return If the receiver is a valid URL query component, returns 39 | * components as key/value pairs. If couldn't split into *any* pairs, 40 | * returns nil. 41 | */ 42 | - (NSDictionary*) av_URLQueryDictionary; 43 | 44 | @end 45 | 46 | #pragma mark - 47 | 48 | @interface NSDictionary (AVAdditions) 49 | 50 | /** 51 | * @return URL query string component created from the keys and values in 52 | * the dictionary. Returns nil for an empty dictionary. 53 | * @param sortedKeys Sorted the keys alphabetically? 54 | * @see cavetas from the main `NSURL` category as well. 55 | */ 56 | - (NSString*) av_URLQueryStringWithSortedKeys:(BOOL) sortedKeys; 57 | 58 | /** As above, but `sortedKeys=NO` */ 59 | - (NSString*) av_URLQueryString; 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /Classes/NSURL+AVAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURL+AVAdditions.m 3 | // AVOS 4 | // 5 | // Created by Qihe Bian on 11/28/14. 6 | // 7 | // 8 | 9 | #import "NSURL+AVAdditions.h" 10 | 11 | static NSString *const kQuerySeparator = @"&"; 12 | static NSString *const kQueryDivider = @"="; 13 | static NSString *const kQueryBegin = @"?"; 14 | static NSString *const kFragmentBegin = @"#"; 15 | 16 | @implementation NSURL (AVAdditions) 17 | 18 | - (NSDictionary*) av_queryDictionary { 19 | return self.query.av_URLQueryDictionary; 20 | } 21 | 22 | - (NSURL*) av_URLByAppendingQueryDictionary:(NSDictionary*) queryDictionary { 23 | return [self av_URLByAppendingQueryDictionary:queryDictionary withSortedKeys:NO]; 24 | } 25 | 26 | - (NSURL *)av_URLByAppendingQueryDictionary:(NSDictionary *)queryDictionary 27 | withSortedKeys:(BOOL)sortedKeys 28 | { 29 | NSMutableArray *queries = self.query ? @[self.query].mutableCopy : @[].mutableCopy; 30 | NSString *dictionaryQuery = [queryDictionary av_URLQueryStringWithSortedKeys:sortedKeys]; 31 | if (dictionaryQuery) { 32 | [queries addObject:dictionaryQuery]; 33 | } 34 | NSString *newQuery = [queries componentsJoinedByString:kQuerySeparator]; 35 | 36 | if (newQuery.length) { 37 | NSArray *queryComponents = [self.absoluteString componentsSeparatedByString:kQueryBegin]; 38 | if (queryComponents.count) { 39 | return [NSURL URLWithString: 40 | [NSString stringWithFormat:@"%@%@%@%@%@", 41 | queryComponents[0], // existing url 42 | kQueryBegin, 43 | newQuery, 44 | self.fragment.length ? kFragmentBegin : @"", 45 | self.fragment.length ? self.fragment : @""]]; 46 | } 47 | } 48 | return self; 49 | } 50 | 51 | @end 52 | 53 | #pragma mark - 54 | 55 | @implementation NSString (AVAdditions) 56 | 57 | - (NSDictionary*) av_URLQueryDictionary { 58 | NSMutableDictionary *mute = @{}.mutableCopy; 59 | for (NSString *query in [self componentsSeparatedByString:kQuerySeparator]) { 60 | NSArray *components = [query componentsSeparatedByString:kQueryDivider]; 61 | if (components.count == 0) { 62 | continue; 63 | } 64 | NSString *key = [components[0] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 65 | id value = nil; 66 | if (components.count == 1) { 67 | // key with no value 68 | value = [NSNull null]; 69 | } 70 | if (components.count == 2) { 71 | value = [components[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 72 | // cover case where there is a separator, but no actual value 73 | value = [value length] ? value : [NSNull null]; 74 | } 75 | if (components.count > 2) { 76 | // invalid - ignore this pair. is this best, though? 77 | continue; 78 | } 79 | mute[key] = value ?: [NSNull null]; 80 | } 81 | return mute.count ? mute.copy : nil; 82 | } 83 | 84 | @end 85 | 86 | #pragma mark - 87 | 88 | @implementation NSDictionary (AVAdditions) 89 | 90 | - (NSString *)av_URLQueryString { 91 | return [self av_URLQueryStringWithSortedKeys:NO]; 92 | } 93 | 94 | - (NSString*) av_URLQueryStringWithSortedKeys:(BOOL)sortedKeys { 95 | NSMutableString *queryString = @"".mutableCopy; 96 | NSArray *keys = sortedKeys ? [self.allKeys sortedArrayUsingSelector:@selector(compare:)] : self.allKeys; 97 | for (NSString *key in keys) { 98 | id rawValue = self[key]; 99 | NSString *value = nil; 100 | // beware of empty or null 101 | if (!(rawValue == [NSNull null] || ![rawValue description].length)) { 102 | value = [[self[key] description] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; 103 | } 104 | [queryString appendFormat:@"%@%@%@%@", 105 | queryString.length ? kQuerySeparator : @"", // appending? 106 | [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], 107 | value ? kQueryDivider : @"", 108 | value ? value : @""]; 109 | } 110 | return queryString.length ? queryString.copy : nil; 111 | } 112 | 113 | @end 114 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSHTTPRequestOperation.h: -------------------------------------------------------------------------------- 1 | // AFHTTPRequestOperation.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | #import "LCSURLConnectionOperation.h" 24 | 25 | NS_ASSUME_NONNULL_BEGIN 26 | 27 | /** 28 | `AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request. 29 | */ 30 | @interface LCSHTTPRequestOperation : LCSURLConnectionOperation 31 | 32 | ///------------------------------------------------ 33 | /// @name Getting HTTP URL Connection Information 34 | ///------------------------------------------------ 35 | 36 | /** 37 | The last HTTP response received by the operation's connection. 38 | */ 39 | @property (readonly, nonatomic, strong, nullable) NSHTTPURLResponse *response; 40 | 41 | /** 42 | Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an AFHTTPResponse serializer, which uses the raw data as its response object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. 43 | 44 | @warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value 45 | */ 46 | @property (nonatomic, strong) LCSHTTPResponseSerializer * responseSerializer; 47 | 48 | /** 49 | An object constructed by the `responseSerializer` from the response and response data. Returns `nil` unless the operation `isFinished`, has a `response`, and has `responseData` with non-zero content length. If an error occurs during serialization, `nil` will be returned, and the `error` property will be populated with the serialization error. 50 | */ 51 | @property (readonly, nonatomic, strong, nullable) id responseObject; 52 | 53 | ///----------------------------------------------------------- 54 | /// @name Setting Completion Block Success / Failure Callbacks 55 | ///----------------------------------------------------------- 56 | 57 | /** 58 | Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed. 59 | 60 | This method should be overridden in subclasses in order to specify the response object passed into the success block. 61 | 62 | @param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request. 63 | @param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request. 64 | */ 65 | - (void)setCompletionBlockWithSuccess:(nullable void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 66 | failure:(nullable void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure; 67 | 68 | @end 69 | 70 | NS_ASSUME_NONNULL_END 71 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSHTTPRequestOperation.m: -------------------------------------------------------------------------------- 1 | // AFHTTPRequestOperation.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "LCSHTTPRequestOperation.h" 23 | 24 | static dispatch_queue_t http_request_operation_processing_queue() { 25 | static dispatch_queue_t af_http_request_operation_processing_queue; 26 | static dispatch_once_t onceToken; 27 | dispatch_once(&onceToken, ^{ 28 | af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT); 29 | }); 30 | 31 | return af_http_request_operation_processing_queue; 32 | } 33 | 34 | static dispatch_group_t http_request_operation_completion_group() { 35 | static dispatch_group_t af_http_request_operation_completion_group; 36 | static dispatch_once_t onceToken; 37 | dispatch_once(&onceToken, ^{ 38 | af_http_request_operation_completion_group = dispatch_group_create(); 39 | }); 40 | 41 | return af_http_request_operation_completion_group; 42 | } 43 | 44 | #pragma mark - 45 | 46 | @interface LCSURLConnectionOperation () 47 | @property (readwrite, nonatomic, strong) NSURLRequest *request; 48 | @property (readwrite, nonatomic, strong) NSURLResponse *response; 49 | @end 50 | 51 | @interface LCSHTTPRequestOperation () 52 | @property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; 53 | @property (readwrite, nonatomic, strong) id responseObject; 54 | @property (readwrite, nonatomic, strong) NSError *responseSerializationError; 55 | @property (readwrite, nonatomic, strong) NSRecursiveLock *lock; 56 | @end 57 | 58 | @implementation LCSHTTPRequestOperation 59 | @dynamic response; 60 | @dynamic lock; 61 | 62 | - (instancetype)initWithRequest:(NSURLRequest *)urlRequest { 63 | self = [super initWithRequest:urlRequest]; 64 | if (!self) { 65 | return nil; 66 | } 67 | 68 | self.responseSerializer = [LCSHTTPResponseSerializer serializer]; 69 | 70 | return self; 71 | } 72 | 73 | - (void)setResponseSerializer:(LCSHTTPResponseSerializer *)responseSerializer { 74 | NSParameterAssert(responseSerializer); 75 | 76 | [self.lock lock]; 77 | _responseSerializer = responseSerializer; 78 | self.responseObject = nil; 79 | self.responseSerializationError = nil; 80 | [self.lock unlock]; 81 | } 82 | 83 | - (id)responseObject { 84 | [self.lock lock]; 85 | if (!_responseObject && [self isFinished] && !self.error) { 86 | NSError *error = nil; 87 | self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error]; 88 | if (error) { 89 | self.responseSerializationError = error; 90 | } 91 | } 92 | [self.lock unlock]; 93 | 94 | return _responseObject; 95 | } 96 | 97 | - (NSError *)error { 98 | if (_responseSerializationError) { 99 | return _responseSerializationError; 100 | } else { 101 | return [super error]; 102 | } 103 | } 104 | 105 | #pragma mark - LCSHTTPRequestOperation 106 | 107 | - (void)setCompletionBlockWithSuccess:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 108 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 109 | { 110 | // completionBlock is manually nilled out in LCSURLConnectionOperation to break the retain cycle. 111 | #pragma clang diagnostic push 112 | #pragma clang diagnostic ignored "-Warc-retain-cycles" 113 | #pragma clang diagnostic ignored "-Wgnu" 114 | self.completionBlock = ^{ 115 | if (self.completionGroup) { 116 | dispatch_group_enter(self.completionGroup); 117 | } 118 | 119 | dispatch_async(http_request_operation_processing_queue(), ^{ 120 | if (self.error) { 121 | if (failure) { 122 | dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ 123 | failure(self, self.error); 124 | }); 125 | } 126 | } else { 127 | id responseObject = self.responseObject; 128 | if (self.error) { 129 | if (failure) { 130 | dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ 131 | failure(self, self.error); 132 | }); 133 | } 134 | } else { 135 | if (success) { 136 | dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ 137 | success(self, responseObject); 138 | }); 139 | } 140 | } 141 | } 142 | 143 | if (self.completionGroup) { 144 | dispatch_group_leave(self.completionGroup); 145 | } 146 | }); 147 | }; 148 | #pragma clang diagnostic pop 149 | } 150 | 151 | #pragma mark - AFURLRequestOperation 152 | 153 | - (void)pause { 154 | [super pause]; 155 | 156 | u_int64_t offset = 0; 157 | if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) { 158 | offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue]; 159 | } else { 160 | offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length]; 161 | } 162 | 163 | NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy]; 164 | if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) { 165 | [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"]; 166 | } 167 | [mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"]; 168 | self.request = mutableURLRequest; 169 | } 170 | 171 | #pragma mark - NSSecureCoding 172 | 173 | + (BOOL)supportsSecureCoding { 174 | return YES; 175 | } 176 | 177 | - (id)initWithCoder:(NSCoder *)decoder { 178 | self = [super initWithCoder:decoder]; 179 | if (!self) { 180 | return nil; 181 | } 182 | 183 | self.responseSerializer = [decoder decodeObjectOfClass:[LCSHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; 184 | 185 | return self; 186 | } 187 | 188 | - (void)encodeWithCoder:(NSCoder *)coder { 189 | [super encodeWithCoder:coder]; 190 | 191 | [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; 192 | } 193 | 194 | #pragma mark - NSCopying 195 | 196 | - (id)copyWithZone:(NSZone *)zone { 197 | LCSHTTPRequestOperation *operation = [super copyWithZone:zone]; 198 | 199 | operation.responseSerializer = [self.responseSerializer copyWithZone:zone]; 200 | operation.completionQueue = self.completionQueue; 201 | operation.completionGroup = self.completionGroup; 202 | 203 | return operation; 204 | } 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSHTTPRequestOperationManager.m: -------------------------------------------------------------------------------- 1 | // AFHTTPRequestOperationManager.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | 24 | #import "LCSHTTPRequestOperationManager.h" 25 | #import "LCSHTTPRequestOperation.h" 26 | 27 | #import 28 | #import 29 | 30 | #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 31 | #import 32 | #endif 33 | 34 | @interface LCSHTTPRequestOperationManager () 35 | @property (readwrite, nonatomic, strong) NSURL *baseURL; 36 | @end 37 | 38 | @implementation LCSHTTPRequestOperationManager 39 | 40 | + (instancetype)manager { 41 | return [[self alloc] initWithBaseURL:nil]; 42 | } 43 | 44 | - (instancetype)init { 45 | return [self initWithBaseURL:nil]; 46 | } 47 | 48 | - (instancetype)initWithBaseURL:(NSURL *)url { 49 | self = [super init]; 50 | if (!self) { 51 | return nil; 52 | } 53 | 54 | // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected 55 | if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { 56 | url = [url URLByAppendingPathComponent:@""]; 57 | } 58 | 59 | self.baseURL = url; 60 | 61 | self.requestSerializer = [LCSHTTPRequestSerializer serializer]; 62 | self.responseSerializer = [LCSJSONResponseSerializer serializer]; 63 | 64 | self.securityPolicy = [LCSSecurityPolicy defaultPolicy]; 65 | 66 | self.reachabilityManager = [LCSNetworkReachabilityManager sharedManager]; 67 | 68 | self.operationQueue = [[NSOperationQueue alloc] init]; 69 | 70 | self.shouldUseCredentialStorage = YES; 71 | 72 | return self; 73 | } 74 | 75 | #pragma mark - 76 | 77 | #ifdef _SYSTEMCONFIGURATION_H 78 | #endif 79 | 80 | - (void)setRequestSerializer:(LCSHTTPRequestSerializer *)requestSerializer { 81 | NSParameterAssert(requestSerializer); 82 | 83 | _requestSerializer = requestSerializer; 84 | } 85 | 86 | - (void)setResponseSerializer:(LCSHTTPResponseSerializer *)responseSerializer { 87 | NSParameterAssert(responseSerializer); 88 | 89 | _responseSerializer = responseSerializer; 90 | } 91 | 92 | #pragma mark - 93 | 94 | - (LCSHTTPRequestOperation *)HTTPRequestOperationWithHTTPMethod:(NSString *)method 95 | URLString:(NSString *)URLString 96 | parameters:(id)parameters 97 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 98 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 99 | { 100 | NSError *serializationError = nil; 101 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; 102 | if (serializationError) { 103 | if (failure) { 104 | #pragma clang diagnostic push 105 | #pragma clang diagnostic ignored "-Wgnu" 106 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 107 | failure(nil, serializationError); 108 | }); 109 | #pragma clang diagnostic pop 110 | } 111 | 112 | return nil; 113 | } 114 | 115 | return [self HTTPRequestOperationWithRequest:request success:success failure:failure]; 116 | } 117 | 118 | - (LCSHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request 119 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 120 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 121 | { 122 | LCSHTTPRequestOperation *operation = [[LCSHTTPRequestOperation alloc] initWithRequest:request]; 123 | operation.responseSerializer = self.responseSerializer; 124 | operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage; 125 | operation.credential = self.credential; 126 | operation.securityPolicy = self.securityPolicy; 127 | 128 | [operation setCompletionBlockWithSuccess:success failure:failure]; 129 | operation.completionQueue = self.completionQueue; 130 | operation.completionGroup = self.completionGroup; 131 | 132 | return operation; 133 | } 134 | 135 | #pragma mark - 136 | 137 | - (LCSHTTPRequestOperation *)GET:(NSString *)URLString 138 | parameters:(id)parameters 139 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 140 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 141 | { 142 | LCSHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; 143 | 144 | [self.operationQueue addOperation:operation]; 145 | 146 | return operation; 147 | } 148 | 149 | - (LCSHTTPRequestOperation *)HEAD:(NSString *)URLString 150 | parameters:(id)parameters 151 | success:(void (^)(LCSHTTPRequestOperation *operation))success 152 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 153 | { 154 | LCSHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(LCSHTTPRequestOperation *requestOperation, __unused id responseObject) { 155 | if (success) { 156 | success(requestOperation); 157 | } 158 | } failure:failure]; 159 | 160 | [self.operationQueue addOperation:operation]; 161 | 162 | return operation; 163 | } 164 | 165 | - (LCSHTTPRequestOperation *)POST:(NSString *)URLString 166 | parameters:(id)parameters 167 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 168 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 169 | { 170 | LCSHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; 171 | 172 | [self.operationQueue addOperation:operation]; 173 | 174 | return operation; 175 | } 176 | 177 | - (LCSHTTPRequestOperation *)POST:(NSString *)URLString 178 | parameters:(id)parameters 179 | constructingBodyWithBlock:(void (^)(id formData))block 180 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 181 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 182 | { 183 | NSError *serializationError = nil; 184 | NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; 185 | if (serializationError) { 186 | if (failure) { 187 | #pragma clang diagnostic push 188 | #pragma clang diagnostic ignored "-Wgnu" 189 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 190 | failure(nil, serializationError); 191 | }); 192 | #pragma clang diagnostic pop 193 | } 194 | 195 | return nil; 196 | } 197 | 198 | LCSHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; 199 | 200 | [self.operationQueue addOperation:operation]; 201 | 202 | return operation; 203 | } 204 | 205 | - (LCSHTTPRequestOperation *)PUT:(NSString *)URLString 206 | parameters:(id)parameters 207 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 208 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 209 | { 210 | LCSHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; 211 | 212 | [self.operationQueue addOperation:operation]; 213 | 214 | return operation; 215 | } 216 | 217 | - (LCSHTTPRequestOperation *)PATCH:(NSString *)URLString 218 | parameters:(id)parameters 219 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 220 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 221 | { 222 | LCSHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; 223 | 224 | [self.operationQueue addOperation:operation]; 225 | 226 | return operation; 227 | } 228 | 229 | - (LCSHTTPRequestOperation *)DELETE:(NSString *)URLString 230 | parameters:(id)parameters 231 | success:(void (^)(LCSHTTPRequestOperation *operation, id responseObject))success 232 | failure:(void (^)(LCSHTTPRequestOperation *operation, NSError *error))failure 233 | { 234 | LCSHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; 235 | 236 | [self.operationQueue addOperation:operation]; 237 | 238 | return operation; 239 | } 240 | 241 | #pragma mark - NSObject 242 | 243 | - (NSString *)description { 244 | return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.operationQueue]; 245 | } 246 | 247 | #pragma mark - NSSecureCoding 248 | 249 | + (BOOL)supportsSecureCoding { 250 | return YES; 251 | } 252 | 253 | - (id)initWithCoder:(NSCoder *)decoder { 254 | NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))]; 255 | 256 | self = [self initWithBaseURL:baseURL]; 257 | if (!self) { 258 | return nil; 259 | } 260 | 261 | self.requestSerializer = [decoder decodeObjectOfClass:[LCSHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; 262 | self.responseSerializer = [decoder decodeObjectOfClass:[LCSHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; 263 | LCSSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[LCSSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))]; 264 | if (decodedPolicy) { 265 | self.securityPolicy = decodedPolicy; 266 | } 267 | 268 | return self; 269 | } 270 | 271 | - (void)encodeWithCoder:(NSCoder *)coder { 272 | [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; 273 | [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; 274 | [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; 275 | [coder encodeObject:self.securityPolicy forKey:NSStringFromSelector(@selector(securityPolicy))]; 276 | } 277 | 278 | #pragma mark - NSCopying 279 | 280 | - (id)copyWithZone:(NSZone *)zone { 281 | LCSHTTPRequestOperationManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL]; 282 | 283 | HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; 284 | HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; 285 | HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone]; 286 | 287 | return HTTPClient; 288 | } 289 | 290 | @end 291 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSHTTPSessionManager.m: -------------------------------------------------------------------------------- 1 | // AFHTTPSessionManager.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "LCSHTTPSessionManager.h" 23 | 24 | #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || TARGET_WATCH_OS 25 | 26 | #import "LCSURLRequestSerialization.h" 27 | #import "LCSURLResponseSerialization.h" 28 | 29 | #import 30 | #import 31 | 32 | #ifdef _SYSTEMCONFIGURATION_H 33 | #import 34 | #import 35 | #import 36 | #import 37 | #import 38 | #endif 39 | 40 | #if TARGET_OS_IOS 41 | #import 42 | #elif TARGET_OS_WATCH 43 | #import 44 | #endif 45 | 46 | @interface LCSHTTPSessionManager () 47 | @property (readwrite, nonatomic, strong) NSURL *baseURL; 48 | @end 49 | 50 | @implementation LCSHTTPSessionManager 51 | @dynamic responseSerializer; 52 | 53 | + (instancetype)manager { 54 | return [[[self class] alloc] initWithBaseURL:nil]; 55 | } 56 | 57 | - (instancetype)init { 58 | return [self initWithBaseURL:nil]; 59 | } 60 | 61 | - (instancetype)initWithBaseURL:(NSURL *)url { 62 | return [self initWithBaseURL:url sessionConfiguration:nil]; 63 | } 64 | 65 | - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { 66 | return [self initWithBaseURL:nil sessionConfiguration:configuration]; 67 | } 68 | 69 | - (instancetype)initWithBaseURL:(NSURL *)url 70 | sessionConfiguration:(NSURLSessionConfiguration *)configuration 71 | { 72 | self = [super initWithSessionConfiguration:configuration]; 73 | if (!self) { 74 | return nil; 75 | } 76 | 77 | // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected 78 | if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { 79 | url = [url URLByAppendingPathComponent:@""]; 80 | } 81 | 82 | self.baseURL = url; 83 | 84 | self.requestSerializer = [LCSHTTPRequestSerializer serializer]; 85 | self.responseSerializer = [LCSJSONResponseSerializer serializer]; 86 | 87 | return self; 88 | } 89 | 90 | #pragma mark - 91 | 92 | #ifdef _SYSTEMCONFIGURATION_H 93 | #endif 94 | 95 | - (void)setRequestSerializer:(LCSHTTPRequestSerializer *)requestSerializer { 96 | NSParameterAssert(requestSerializer); 97 | 98 | _requestSerializer = requestSerializer; 99 | } 100 | 101 | - (void)setResponseSerializer:(LCSHTTPResponseSerializer *)responseSerializer { 102 | NSParameterAssert(responseSerializer); 103 | 104 | [super setResponseSerializer:responseSerializer]; 105 | } 106 | 107 | #pragma mark - 108 | 109 | - (NSURLSessionDataTask *)GET:(NSString *)URLString 110 | parameters:(id)parameters 111 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 112 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 113 | { 114 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; 115 | 116 | [dataTask resume]; 117 | 118 | return dataTask; 119 | } 120 | 121 | - (NSURLSessionDataTask *)HEAD:(NSString *)URLString 122 | parameters:(id)parameters 123 | success:(void (^)(NSURLSessionDataTask *task))success 124 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 125 | { 126 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(NSURLSessionDataTask *task, __unused id responseObject) { 127 | if (success) { 128 | success(task); 129 | } 130 | } failure:failure]; 131 | 132 | [dataTask resume]; 133 | 134 | return dataTask; 135 | } 136 | 137 | - (NSURLSessionDataTask *)POST:(NSString *)URLString 138 | parameters:(id)parameters 139 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 140 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 141 | { 142 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; 143 | 144 | [dataTask resume]; 145 | 146 | return dataTask; 147 | } 148 | 149 | - (NSURLSessionDataTask *)POST:(NSString *)URLString 150 | parameters:(id)parameters 151 | constructingBodyWithBlock:(void (^)(id formData))block 152 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 153 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 154 | { 155 | NSError *serializationError = nil; 156 | NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; 157 | if (serializationError) { 158 | if (failure) { 159 | #pragma clang diagnostic push 160 | #pragma clang diagnostic ignored "-Wgnu" 161 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 162 | failure(nil, serializationError); 163 | }); 164 | #pragma clang diagnostic pop 165 | } 166 | 167 | return nil; 168 | } 169 | 170 | __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { 171 | if (error) { 172 | if (failure) { 173 | failure(task, error); 174 | } 175 | } else { 176 | if (success) { 177 | success(task, responseObject); 178 | } 179 | } 180 | }]; 181 | 182 | [task resume]; 183 | 184 | return task; 185 | } 186 | 187 | - (NSURLSessionDataTask *)PUT:(NSString *)URLString 188 | parameters:(id)parameters 189 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 190 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 191 | { 192 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; 193 | 194 | [dataTask resume]; 195 | 196 | return dataTask; 197 | } 198 | 199 | - (NSURLSessionDataTask *)PATCH:(NSString *)URLString 200 | parameters:(id)parameters 201 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 202 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 203 | { 204 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; 205 | 206 | [dataTask resume]; 207 | 208 | return dataTask; 209 | } 210 | 211 | - (NSURLSessionDataTask *)DELETE:(NSString *)URLString 212 | parameters:(id)parameters 213 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 214 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 215 | { 216 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; 217 | 218 | [dataTask resume]; 219 | 220 | return dataTask; 221 | } 222 | 223 | - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method 224 | URLString:(NSString *)URLString 225 | parameters:(id)parameters 226 | success:(void (^)(NSURLSessionDataTask *, id))success 227 | failure:(void (^)(NSURLSessionDataTask *, NSError *))failure 228 | { 229 | NSError *serializationError = nil; 230 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; 231 | if (serializationError) { 232 | if (failure) { 233 | #pragma clang diagnostic push 234 | #pragma clang diagnostic ignored "-Wgnu" 235 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 236 | failure(nil, serializationError); 237 | }); 238 | #pragma clang diagnostic pop 239 | } 240 | 241 | return nil; 242 | } 243 | 244 | __block NSURLSessionDataTask *dataTask = nil; 245 | dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { 246 | if (error) { 247 | if (failure) { 248 | failure(dataTask, error); 249 | } 250 | } else { 251 | if (success) { 252 | success(dataTask, responseObject); 253 | } 254 | } 255 | }]; 256 | 257 | return dataTask; 258 | } 259 | 260 | #pragma mark - NSObject 261 | 262 | - (NSString *)description { 263 | return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; 264 | } 265 | 266 | #pragma mark - NSSecureCoding 267 | 268 | + (BOOL)supportsSecureCoding { 269 | return YES; 270 | } 271 | 272 | - (id)initWithCoder:(NSCoder *)decoder { 273 | NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))]; 274 | NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; 275 | if (!configuration) { 276 | NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; 277 | if (configurationIdentifier) { 278 | #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100) 279 | configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier]; 280 | #else 281 | configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier]; 282 | #endif 283 | } 284 | } 285 | 286 | self = [self initWithBaseURL:baseURL sessionConfiguration:configuration]; 287 | if (!self) { 288 | return nil; 289 | } 290 | 291 | self.requestSerializer = [decoder decodeObjectOfClass:[LCSHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; 292 | self.responseSerializer = [decoder decodeObjectOfClass:[LCSHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; 293 | LCSSecurityPolicy *decodedPolicy = [decoder decodeObjectOfClass:[LCSSecurityPolicy class] forKey:NSStringFromSelector(@selector(securityPolicy))]; 294 | if (decodedPolicy) { 295 | self.securityPolicy = decodedPolicy; 296 | } 297 | 298 | return self; 299 | } 300 | 301 | - (void)encodeWithCoder:(NSCoder *)coder { 302 | [super encodeWithCoder:coder]; 303 | 304 | [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; 305 | if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) { 306 | [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; 307 | } else { 308 | [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"]; 309 | } 310 | [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; 311 | [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; 312 | [coder encodeObject:self.securityPolicy forKey:NSStringFromSelector(@selector(securityPolicy))]; 313 | } 314 | 315 | #pragma mark - NSCopying 316 | 317 | - (id)copyWithZone:(NSZone *)zone { 318 | LCSHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration]; 319 | 320 | HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; 321 | HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; 322 | HTTPClient.securityPolicy = [self.securityPolicy copyWithZone:zone]; 323 | return HTTPClient; 324 | } 325 | 326 | @end 327 | 328 | #endif 329 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSNetworkReachabilityManager.h: -------------------------------------------------------------------------------- 1 | // AFNetworkReachabilityManager.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | 24 | #if !TARGET_OS_WATCH 25 | #import 26 | 27 | #ifndef NS_DESIGNATED_INITIALIZER 28 | #if __has_attribute(objc_designated_initializer) 29 | #define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) 30 | #else 31 | #define NS_DESIGNATED_INITIALIZER 32 | #endif 33 | #endif 34 | 35 | typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { 36 | AFNetworkReachabilityStatusUnknown = -1, 37 | AFNetworkReachabilityStatusNotReachable = 0, 38 | AFNetworkReachabilityStatusReachableViaWWAN = 1, 39 | AFNetworkReachabilityStatusReachableViaWiFi = 2, 40 | }; 41 | 42 | NS_ASSUME_NONNULL_BEGIN 43 | 44 | /** 45 | `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. 46 | 47 | Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. 48 | 49 | See Apple's Reachability Sample Code (https://developer.apple.com/library/ios/samplecode/reachability/) 50 | 51 | @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. 52 | */ 53 | @interface LCSNetworkReachabilityManager : NSObject 54 | 55 | /** 56 | The current network reachability status. 57 | */ 58 | @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; 59 | 60 | /** 61 | Whether or not the network is currently reachable. 62 | */ 63 | @property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; 64 | 65 | /** 66 | Whether or not the network is currently reachable via WWAN. 67 | */ 68 | @property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; 69 | 70 | /** 71 | Whether or not the network is currently reachable via WiFi. 72 | */ 73 | @property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; 74 | 75 | ///--------------------- 76 | /// @name Initialization 77 | ///--------------------- 78 | 79 | /** 80 | Returns the shared network reachability manager. 81 | */ 82 | + (instancetype)sharedManager; 83 | 84 | /** 85 | Creates and returns a network reachability manager for the specified domain. 86 | 87 | @param domain The domain used to evaluate network reachability. 88 | 89 | @return An initialized network reachability manager, actively monitoring the specified domain. 90 | */ 91 | + (instancetype)managerForDomain:(NSString *)domain; 92 | 93 | /** 94 | Creates and returns a network reachability manager for the socket address. 95 | 96 | @param address The socket address (`sockaddr_in`) used to evaluate network reachability. 97 | 98 | @return An initialized network reachability manager, actively monitoring the specified socket address. 99 | */ 100 | + (instancetype)managerForAddress:(const void *)address; 101 | 102 | /** 103 | Initializes an instance of a network reachability manager from the specified reachability object. 104 | 105 | @param reachability The reachability object to monitor. 106 | 107 | @return An initialized network reachability manager, actively monitoring the specified reachability. 108 | */ 109 | - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; 110 | 111 | ///-------------------------------------------------- 112 | /// @name Starting & Stopping Reachability Monitoring 113 | ///-------------------------------------------------- 114 | 115 | /** 116 | Starts monitoring for changes in network reachability status. 117 | */ 118 | - (void)startMonitoring; 119 | 120 | /** 121 | Stops monitoring for changes in network reachability status. 122 | */ 123 | - (void)stopMonitoring; 124 | 125 | ///------------------------------------------------- 126 | /// @name Getting Localized Reachability Description 127 | ///------------------------------------------------- 128 | 129 | /** 130 | Returns a localized string representation of the current network reachability status. 131 | */ 132 | - (NSString *)localizedNetworkReachabilityStatusString; 133 | 134 | ///--------------------------------------------------- 135 | /// @name Setting Network Reachability Change Callback 136 | ///--------------------------------------------------- 137 | 138 | /** 139 | Sets a callback to be executed when the network availability of the `baseURL` host changes. 140 | 141 | @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. 142 | */ 143 | - (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block; 144 | 145 | @end 146 | 147 | ///---------------- 148 | /// @name Constants 149 | ///---------------- 150 | 151 | /** 152 | ## Network Reachability 153 | 154 | The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. 155 | 156 | enum { 157 | AFNetworkReachabilityStatusUnknown, 158 | AFNetworkReachabilityStatusNotReachable, 159 | AFNetworkReachabilityStatusReachableViaWWAN, 160 | AFNetworkReachabilityStatusReachableViaWiFi, 161 | } 162 | 163 | `AFNetworkReachabilityStatusUnknown` 164 | The `baseURL` host reachability is not known. 165 | 166 | `AFNetworkReachabilityStatusNotReachable` 167 | The `baseURL` host cannot be reached. 168 | 169 | `AFNetworkReachabilityStatusReachableViaWWAN` 170 | The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. 171 | 172 | `AFNetworkReachabilityStatusReachableViaWiFi` 173 | The `baseURL` host can be reached via a Wi-Fi connection. 174 | 175 | ### Keys for Notification UserInfo Dictionary 176 | 177 | Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. 178 | 179 | `AFNetworkingReachabilityNotificationStatusItem` 180 | A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. 181 | The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. 182 | */ 183 | 184 | ///-------------------- 185 | /// @name Notifications 186 | ///-------------------- 187 | 188 | /** 189 | Posted when network reachability changes. 190 | This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. 191 | 192 | @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). 193 | */ 194 | FOUNDATION_EXPORT NSString * const LCSNetworkingReachabilityDidChangeNotification; 195 | FOUNDATION_EXPORT NSString * const LCSNetworkingReachabilityNotificationStatusItem; 196 | 197 | ///-------------------- 198 | /// @name Functions 199 | ///-------------------- 200 | 201 | /** 202 | Returns a localized string representation of an `AFNetworkReachabilityStatus` value. 203 | */ 204 | FOUNDATION_EXPORT NSString * LCSStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); 205 | 206 | NS_ASSUME_NONNULL_END 207 | #endif 208 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSNetworkReachabilityManager.m: -------------------------------------------------------------------------------- 1 | // AFNetworkReachabilityManager.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "LCSNetworkReachabilityManager.h" 23 | #if !TARGET_OS_WATCH 24 | 25 | #import 26 | #import 27 | #import 28 | #import 29 | #import 30 | 31 | NSString * const LCSNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; 32 | NSString * const LCSNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; 33 | 34 | typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); 35 | 36 | NSString * LCSStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { 37 | switch (status) { 38 | case AFNetworkReachabilityStatusNotReachable: 39 | return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); 40 | case AFNetworkReachabilityStatusReachableViaWWAN: 41 | return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); 42 | case AFNetworkReachabilityStatusReachableViaWiFi: 43 | return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); 44 | case AFNetworkReachabilityStatusUnknown: 45 | default: 46 | return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); 47 | } 48 | } 49 | 50 | static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { 51 | BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); 52 | BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); 53 | BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); 54 | BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); 55 | BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); 56 | 57 | AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; 58 | if (isNetworkReachable == NO) { 59 | status = AFNetworkReachabilityStatusNotReachable; 60 | } 61 | #if TARGET_OS_IPHONE 62 | else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { 63 | status = AFNetworkReachabilityStatusReachableViaWWAN; 64 | } 65 | #endif 66 | else { 67 | status = AFNetworkReachabilityStatusReachableViaWiFi; 68 | } 69 | 70 | return status; 71 | } 72 | 73 | /** 74 | * Queue a status change notification for the main thread. 75 | * 76 | * This is done to ensure that the notifications are received in the same order 77 | * as they are sent. If notifications are sent directly, it is possible that 78 | * a queued notification (for an earlier status condition) is processed after 79 | * the later update, resulting in the listener being left in the wrong state. 80 | */ 81 | static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) { 82 | AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); 83 | dispatch_async(dispatch_get_main_queue(), ^{ 84 | if (block) { 85 | block(status); 86 | } 87 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 88 | NSDictionary *userInfo = @{ LCSNetworkingReachabilityNotificationStatusItem: @(status) }; 89 | [notificationCenter postNotificationName:LCSNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; 90 | }); 91 | } 92 | 93 | static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { 94 | AFPostReachabilityStatusChange(flags, (__bridge AFNetworkReachabilityStatusBlock)info); 95 | } 96 | 97 | static const void * AFNetworkReachabilityRetainCallback(const void *info) { 98 | return Block_copy(info); 99 | } 100 | 101 | static void AFNetworkReachabilityReleaseCallback(const void *info) { 102 | if (info) { 103 | Block_release(info); 104 | } 105 | } 106 | 107 | @interface LCSNetworkReachabilityManager () 108 | @property (readwrite, nonatomic, strong) id networkReachability; 109 | @property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; 110 | @property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; 111 | @end 112 | 113 | @implementation LCSNetworkReachabilityManager 114 | 115 | + (instancetype)sharedManager { 116 | static LCSNetworkReachabilityManager *_sharedManager = nil; 117 | static dispatch_once_t onceToken; 118 | dispatch_once(&onceToken, ^{ 119 | struct sockaddr_in address; 120 | bzero(&address, sizeof(address)); 121 | address.sin_len = sizeof(address); 122 | address.sin_family = AF_INET; 123 | 124 | _sharedManager = [self managerForAddress:&address]; 125 | }); 126 | 127 | return _sharedManager; 128 | } 129 | 130 | #ifndef __clang_analyzer__ 131 | + (instancetype)managerForDomain:(NSString *)domain { 132 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); 133 | 134 | LCSNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; 135 | 136 | return manager; 137 | } 138 | #endif 139 | 140 | #ifndef __clang_analyzer__ 141 | + (instancetype)managerForAddress:(const void *)address { 142 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); 143 | LCSNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; 144 | 145 | return manager; 146 | } 147 | #endif 148 | 149 | - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { 150 | self = [super init]; 151 | if (!self) { 152 | return nil; 153 | } 154 | 155 | self.networkReachability = CFBridgingRelease(reachability); 156 | self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; 157 | 158 | return self; 159 | } 160 | 161 | - (instancetype)init NS_UNAVAILABLE 162 | { 163 | return nil; 164 | } 165 | 166 | - (void)dealloc { 167 | [self stopMonitoring]; 168 | } 169 | 170 | #pragma mark - 171 | 172 | - (BOOL)isReachable { 173 | return [self isReachableViaWWAN] || [self isReachableViaWiFi]; 174 | } 175 | 176 | - (BOOL)isReachableViaWWAN { 177 | return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; 178 | } 179 | 180 | - (BOOL)isReachableViaWiFi { 181 | return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; 182 | } 183 | 184 | #pragma mark - 185 | 186 | - (void)startMonitoring { 187 | [self stopMonitoring]; 188 | 189 | if (!self.networkReachability) { 190 | return; 191 | } 192 | 193 | __weak __typeof(self)weakSelf = self; 194 | AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { 195 | __strong __typeof(weakSelf)strongSelf = weakSelf; 196 | 197 | strongSelf.networkReachabilityStatus = status; 198 | if (strongSelf.networkReachabilityStatusBlock) { 199 | strongSelf.networkReachabilityStatusBlock(status); 200 | } 201 | 202 | }; 203 | 204 | id networkReachability = self.networkReachability; 205 | SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; 206 | SCNetworkReachabilitySetCallback((__bridge SCNetworkReachabilityRef)networkReachability, AFNetworkReachabilityCallback, &context); 207 | SCNetworkReachabilityScheduleWithRunLoop((__bridge SCNetworkReachabilityRef)networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); 208 | 209 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ 210 | SCNetworkReachabilityFlags flags; 211 | if (SCNetworkReachabilityGetFlags((__bridge SCNetworkReachabilityRef)networkReachability, &flags)) { 212 | AFPostReachabilityStatusChange(flags, callback); 213 | } 214 | }); 215 | } 216 | 217 | - (void)stopMonitoring { 218 | if (!self.networkReachability) { 219 | return; 220 | } 221 | 222 | SCNetworkReachabilityUnscheduleFromRunLoop((__bridge SCNetworkReachabilityRef)self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); 223 | } 224 | 225 | #pragma mark - 226 | 227 | - (NSString *)localizedNetworkReachabilityStatusString { 228 | return LCSStringFromNetworkReachabilityStatus(self.networkReachabilityStatus); 229 | } 230 | 231 | #pragma mark - 232 | 233 | - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { 234 | self.networkReachabilityStatusBlock = block; 235 | } 236 | 237 | #pragma mark - NSKeyValueObserving 238 | 239 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 240 | if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { 241 | return [NSSet setWithObject:@"networkReachabilityStatus"]; 242 | } 243 | 244 | return [super keyPathsForValuesAffectingValueForKey:key]; 245 | } 246 | 247 | @end 248 | #endif 249 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSNetworking.h: -------------------------------------------------------------------------------- 1 | // AFNetworking.h 2 | // 3 | // Copyright (c) 2013 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | #import 25 | 26 | #ifndef _AFNETWORKING_ 27 | #define _AFNETWORKING_ 28 | 29 | #import "LCSURLRequestSerialization.h" 30 | #import "LCSURLResponseSerialization.h" 31 | #import "LCSSecurityPolicy.h" 32 | #if !TARGET_OS_WATCH 33 | #import "LCSNetworkReachabilityManager.h" 34 | #import "LCSURLConnectionOperation.h" 35 | #import "LCSHTTPRequestOperation.h" 36 | #import "LCSHTTPRequestOperationManager.h" 37 | #endif 38 | 39 | #if ( ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || \ 40 | ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 ) || \ 41 | TARGET_OS_WATCH ) 42 | #import "LCSURLSessionManager.h" 43 | #import "LCSHTTPSessionManager.h" 44 | #endif 45 | 46 | #endif /* _AFNETWORKING_ */ 47 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSSecurityPolicy.h: -------------------------------------------------------------------------------- 1 | // AFSecurityPolicy.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | #import 24 | 25 | typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { 26 | AFSSLPinningModeNone, 27 | AFSSLPinningModePublicKey, 28 | AFSSLPinningModeCertificate, 29 | }; 30 | 31 | /** 32 | `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. 33 | 34 | Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. 35 | */ 36 | 37 | NS_ASSUME_NONNULL_BEGIN 38 | 39 | @interface LCSSecurityPolicy : NSObject 40 | 41 | /** 42 | The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. 43 | */ 44 | @property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; 45 | 46 | /** 47 | The certificates used to evaluate server trust according to the SSL pinning mode. By default, this property is set to any (`.cer`) certificates included in the app bundle. Note that if you create an array with duplicate certificates, the duplicate certificates will be removed. Note that if pinning is enabled, `evaluateServerTrust:forDomain:` will return true if any pinned certificate matches. 48 | */ 49 | @property (nonatomic, strong, nullable) NSArray *pinnedCertificates; 50 | 51 | /** 52 | Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`. 53 | */ 54 | @property (nonatomic, assign) BOOL allowInvalidCertificates; 55 | 56 | /** 57 | Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`. 58 | */ 59 | @property (nonatomic, assign) BOOL validatesDomainName; 60 | 61 | ///----------------------------------------- 62 | /// @name Getting Specific Security Policies 63 | ///----------------------------------------- 64 | 65 | /** 66 | Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys. 67 | 68 | @return The default security policy. 69 | */ 70 | + (instancetype)defaultPolicy; 71 | 72 | ///--------------------- 73 | /// @name Initialization 74 | ///--------------------- 75 | 76 | /** 77 | Creates and returns a security policy with the specified pinning mode. 78 | 79 | @param pinningMode The SSL pinning mode. 80 | 81 | @return A new security policy. 82 | */ 83 | + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; 84 | 85 | ///------------------------------ 86 | /// @name Evaluating Server Trust 87 | ///------------------------------ 88 | 89 | /** 90 | Whether or not the specified server trust should be accepted, based on the security policy. 91 | 92 | This method should be used when responding to an authentication challenge from a server. 93 | 94 | @param serverTrust The X.509 certificate trust of the server. 95 | 96 | @return Whether or not to trust the server. 97 | 98 | @warning This method has been deprecated in favor of `-evaluateServerTrust:forDomain:`. 99 | */ 100 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust DEPRECATED_ATTRIBUTE; 101 | 102 | /** 103 | Whether or not the specified server trust should be accepted, based on the security policy. 104 | 105 | This method should be used when responding to an authentication challenge from a server. 106 | 107 | @param serverTrust The X.509 certificate trust of the server. 108 | @param domain The domain of serverTrust. If `nil`, the domain will not be validated. 109 | 110 | @return Whether or not to trust the server. 111 | */ 112 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust 113 | forDomain:(nullable NSString *)domain; 114 | 115 | @end 116 | 117 | NS_ASSUME_NONNULL_END 118 | 119 | ///---------------- 120 | /// @name Constants 121 | ///---------------- 122 | 123 | /** 124 | ## SSL Pinning Modes 125 | 126 | The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. 127 | 128 | enum { 129 | AFSSLPinningModeNone, 130 | AFSSLPinningModePublicKey, 131 | AFSSLPinningModeCertificate, 132 | } 133 | 134 | `AFSSLPinningModeNone` 135 | Do not used pinned certificates to validate servers. 136 | 137 | `AFSSLPinningModePublicKey` 138 | Validate host certificates against public keys of pinned certificates. 139 | 140 | `AFSSLPinningModeCertificate` 141 | Validate host certificates against pinned certificates. 142 | */ 143 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSSecurityPolicy.m: -------------------------------------------------------------------------------- 1 | // AFSecurityPolicy.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "LCSSecurityPolicy.h" 23 | 24 | #import 25 | 26 | #if !TARGET_OS_IOS && !TARGET_OS_WATCH 27 | static NSData * AFSecKeyGetData(SecKeyRef key) { 28 | CFDataRef data = NULL; 29 | 30 | __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); 31 | 32 | return (__bridge_transfer NSData *)data; 33 | 34 | _out: 35 | if (data) { 36 | CFRelease(data); 37 | } 38 | 39 | return nil; 40 | } 41 | #endif 42 | 43 | static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { 44 | #if TARGET_OS_IOS || TARGET_OS_WATCH 45 | return [(__bridge id)key1 isEqual:(__bridge id)key2]; 46 | #else 47 | return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; 48 | #endif 49 | } 50 | 51 | static id AFPublicKeyForCertificate(NSData *certificate) { 52 | id allowedPublicKey = nil; 53 | SecCertificateRef allowedCertificate; 54 | SecCertificateRef allowedCertificates[1]; 55 | CFArrayRef tempCertificates = nil; 56 | SecPolicyRef policy = nil; 57 | SecTrustRef allowedTrust = nil; 58 | SecTrustResultType result; 59 | 60 | allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); 61 | __Require_Quiet(allowedCertificate != NULL, _out); 62 | 63 | allowedCertificates[0] = allowedCertificate; 64 | tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); 65 | 66 | policy = SecPolicyCreateBasicX509(); 67 | __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); 68 | __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); 69 | 70 | allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); 71 | 72 | _out: 73 | if (allowedTrust) { 74 | CFRelease(allowedTrust); 75 | } 76 | 77 | if (policy) { 78 | CFRelease(policy); 79 | } 80 | 81 | if (tempCertificates) { 82 | CFRelease(tempCertificates); 83 | } 84 | 85 | if (allowedCertificate) { 86 | CFRelease(allowedCertificate); 87 | } 88 | 89 | return allowedPublicKey; 90 | } 91 | 92 | static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { 93 | BOOL isValid = NO; 94 | SecTrustResultType result; 95 | __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); 96 | 97 | isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); 98 | 99 | _out: 100 | return isValid; 101 | } 102 | 103 | static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { 104 | CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); 105 | NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; 106 | 107 | for (CFIndex i = 0; i < certificateCount; i++) { 108 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); 109 | [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; 110 | } 111 | 112 | return [NSArray arrayWithArray:trustChain]; 113 | } 114 | 115 | static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { 116 | SecPolicyRef policy = SecPolicyCreateBasicX509(); 117 | CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); 118 | NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; 119 | for (CFIndex i = 0; i < certificateCount; i++) { 120 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); 121 | 122 | SecCertificateRef someCertificates[] = {certificate}; 123 | CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); 124 | 125 | SecTrustRef trust; 126 | __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); 127 | 128 | SecTrustResultType result; 129 | __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); 130 | 131 | [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; 132 | 133 | _out: 134 | if (trust) { 135 | CFRelease(trust); 136 | } 137 | 138 | if (certificates) { 139 | CFRelease(certificates); 140 | } 141 | 142 | continue; 143 | } 144 | CFRelease(policy); 145 | 146 | return [NSArray arrayWithArray:trustChain]; 147 | } 148 | 149 | #pragma mark - 150 | 151 | @interface LCSSecurityPolicy() 152 | @property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; 153 | @property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys; 154 | @end 155 | 156 | @implementation LCSSecurityPolicy 157 | 158 | + (NSArray *)defaultPinnedCertificates { 159 | static NSArray *_defaultPinnedCertificates = nil; 160 | static dispatch_once_t onceToken; 161 | dispatch_once(&onceToken, ^{ 162 | NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 163 | NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; 164 | 165 | NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]]; 166 | for (NSString *path in paths) { 167 | NSData *certificateData = [NSData dataWithContentsOfFile:path]; 168 | [certificates addObject:certificateData]; 169 | } 170 | 171 | _defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates]; 172 | }); 173 | 174 | return _defaultPinnedCertificates; 175 | } 176 | 177 | + (instancetype)defaultPolicy { 178 | LCSSecurityPolicy *securityPolicy = [[self alloc] init]; 179 | securityPolicy.SSLPinningMode = AFSSLPinningModeNone; 180 | 181 | return securityPolicy; 182 | } 183 | 184 | + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { 185 | LCSSecurityPolicy *securityPolicy = [[self alloc] init]; 186 | securityPolicy.SSLPinningMode = pinningMode; 187 | 188 | [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]]; 189 | 190 | return securityPolicy; 191 | } 192 | 193 | - (id)init { 194 | self = [super init]; 195 | if (!self) { 196 | return nil; 197 | } 198 | 199 | self.validatesDomainName = YES; 200 | 201 | return self; 202 | } 203 | 204 | - (void)setPinnedCertificates:(NSArray *)pinnedCertificates { 205 | _pinnedCertificates = [[NSOrderedSet orderedSetWithArray:pinnedCertificates] array]; 206 | 207 | if (self.pinnedCertificates) { 208 | NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]]; 209 | for (NSData *certificate in self.pinnedCertificates) { 210 | id publicKey = AFPublicKeyForCertificate(certificate); 211 | if (!publicKey) { 212 | continue; 213 | } 214 | [mutablePinnedPublicKeys addObject:publicKey]; 215 | } 216 | self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys]; 217 | } else { 218 | self.pinnedPublicKeys = nil; 219 | } 220 | } 221 | 222 | #pragma mark - 223 | 224 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust { 225 | return [self evaluateServerTrust:serverTrust forDomain:nil]; 226 | } 227 | 228 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust 229 | forDomain:(NSString *)domain 230 | { 231 | if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { 232 | // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html 233 | // According to the docs, you should only trust your provided certs for evaluation. 234 | // Pinned certificates are added to the trust. Without pinned certificates, 235 | // there is nothing to evaluate against. 236 | // 237 | // From Apple Docs: 238 | // "Do not implicitly trust self-signed certificates as anchors (kSecTrustOptionImplicitAnchors). 239 | // Instead, add your own (self-signed) CA certificate to the list of trusted anchors." 240 | NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning."); 241 | return NO; 242 | } 243 | 244 | NSMutableArray *policies = [NSMutableArray array]; 245 | if (self.validatesDomainName) { 246 | [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; 247 | } else { 248 | [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; 249 | } 250 | 251 | SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); 252 | 253 | if (self.SSLPinningMode == AFSSLPinningModeNone) { 254 | return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); 255 | } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { 256 | return NO; 257 | } 258 | 259 | NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); 260 | switch (self.SSLPinningMode) { 261 | case AFSSLPinningModeNone: 262 | default: 263 | return NO; 264 | case AFSSLPinningModeCertificate: { 265 | NSMutableArray *pinnedCertificates = [NSMutableArray array]; 266 | for (NSData *certificateData in self.pinnedCertificates) { 267 | [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; 268 | } 269 | SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); 270 | 271 | if (!AFServerTrustIsValid(serverTrust)) { 272 | return NO; 273 | } 274 | 275 | NSUInteger trustedCertificateCount = 0; 276 | for (NSData *trustChainCertificate in serverCertificates) { 277 | if ([self.pinnedCertificates containsObject:trustChainCertificate]) { 278 | trustedCertificateCount++; 279 | } 280 | } 281 | return trustedCertificateCount > 0; 282 | } 283 | case AFSSLPinningModePublicKey: { 284 | NSUInteger trustedPublicKeyCount = 0; 285 | NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); 286 | 287 | for (id trustChainPublicKey in publicKeys) { 288 | for (id pinnedPublicKey in self.pinnedPublicKeys) { 289 | if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { 290 | trustedPublicKeyCount += 1; 291 | } 292 | } 293 | } 294 | return trustedPublicKeyCount > 0; 295 | } 296 | } 297 | 298 | return NO; 299 | } 300 | 301 | #pragma mark - NSKeyValueObserving 302 | 303 | + (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { 304 | return [NSSet setWithObject:@"pinnedCertificates"]; 305 | } 306 | 307 | #pragma mark - NSSecureCoding 308 | 309 | + (BOOL)supportsSecureCoding { 310 | return YES; 311 | } 312 | 313 | - (instancetype)initWithCoder:(NSCoder *)decoder { 314 | 315 | self = [self init]; 316 | if (!self) { 317 | return nil; 318 | } 319 | 320 | self.SSLPinningMode = [[decoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(SSLPinningMode))] unsignedIntegerValue]; 321 | self.allowInvalidCertificates = [decoder decodeBoolForKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; 322 | self.validatesDomainName = [decoder decodeBoolForKey:NSStringFromSelector(@selector(validatesDomainName))]; 323 | self.pinnedCertificates = [decoder decodeObjectOfClass:[NSArray class] forKey:NSStringFromSelector(@selector(pinnedCertificates))]; 324 | 325 | return self; 326 | } 327 | 328 | - (void)encodeWithCoder:(NSCoder *)coder { 329 | [coder encodeObject:[NSNumber numberWithUnsignedInteger:self.SSLPinningMode] forKey:NSStringFromSelector(@selector(SSLPinningMode))]; 330 | [coder encodeBool:self.allowInvalidCertificates forKey:NSStringFromSelector(@selector(allowInvalidCertificates))]; 331 | [coder encodeBool:self.validatesDomainName forKey:NSStringFromSelector(@selector(validatesDomainName))]; 332 | [coder encodeObject:self.pinnedCertificates forKey:NSStringFromSelector(@selector(pinnedCertificates))]; 333 | } 334 | 335 | #pragma mark - NSCopying 336 | 337 | - (instancetype)copyWithZone:(NSZone *)zone { 338 | LCSSecurityPolicy *securityPolicy = [[[self class] allocWithZone:zone] init]; 339 | securityPolicy.SSLPinningMode = self.SSLPinningMode; 340 | securityPolicy.allowInvalidCertificates = self.allowInvalidCertificates; 341 | securityPolicy.validatesDomainName = self.validatesDomainName; 342 | securityPolicy.pinnedCertificates = [self.pinnedCertificates copyWithZone:zone]; 343 | 344 | return securityPolicy; 345 | } 346 | 347 | @end 348 | -------------------------------------------------------------------------------- /Classes/Vendor/AFNetworking/LCSURLResponseSerialization.h: -------------------------------------------------------------------------------- 1 | // AFURLResponseSerialization.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | #import 24 | 25 | NS_ASSUME_NONNULL_BEGIN 26 | 27 | /** 28 | The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. 29 | 30 | For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. 31 | */ 32 | @protocol AFURLResponseSerialization 33 | 34 | /** 35 | The response object decoded from the data associated with a specified response. 36 | 37 | @param response The response to be processed. 38 | @param data The response data to be decoded. 39 | @param error The error that occurred while attempting to decode the response data. 40 | 41 | @return The object decoded from the specified response data. 42 | */ 43 | - (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response 44 | data:(nullable NSData *)data 45 | error:(NSError * __nullable __autoreleasing *)error 46 | #ifdef NS_SWIFT_NOTHROW 47 | NS_SWIFT_NOTHROW 48 | #endif 49 | ; 50 | 51 | @end 52 | 53 | #pragma mark - 54 | 55 | /** 56 | `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. 57 | 58 | Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. 59 | */ 60 | @interface LCSHTTPResponseSerializer : NSObject 61 | 62 | - (instancetype)init; 63 | 64 | /** 65 | The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default. 66 | */ 67 | @property (nonatomic, assign) NSStringEncoding stringEncoding; 68 | 69 | /** 70 | Creates and returns a serializer with default configuration. 71 | */ 72 | + (instancetype)serializer; 73 | 74 | ///----------------------------------------- 75 | /// @name Configuring Response Serialization 76 | ///----------------------------------------- 77 | 78 | /** 79 | The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation. 80 | 81 | See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 82 | */ 83 | @property (nonatomic, copy, nullable) NSIndexSet *acceptableStatusCodes; 84 | 85 | /** 86 | The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation. 87 | */ 88 | @property (nonatomic, copy, nullable) NSSet *acceptableContentTypes; 89 | 90 | /** 91 | Validates the specified response and data. 92 | 93 | In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks. 94 | 95 | @param response The response to be validated. 96 | @param data The data associated with the response. 97 | @param error The error that occurred while attempting to validate the response. 98 | 99 | @return `YES` if the response is valid, otherwise `NO`. 100 | */ 101 | - (BOOL)validateResponse:(nullable NSHTTPURLResponse *)response 102 | data:(nullable NSData *)data 103 | error:(NSError * __nullable __autoreleasing *)error; 104 | 105 | @end 106 | 107 | #pragma mark - 108 | 109 | 110 | /** 111 | `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. 112 | 113 | By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: 114 | 115 | - `application/json` 116 | - `text/json` 117 | - `text/javascript` 118 | */ 119 | @interface LCSJSONResponseSerializer : LCSHTTPResponseSerializer 120 | 121 | - (instancetype)init; 122 | 123 | /** 124 | Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. 125 | */ 126 | @property (nonatomic, assign) NSJSONReadingOptions readingOptions; 127 | 128 | /** 129 | Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`. 130 | */ 131 | @property (nonatomic, assign) BOOL removesKeysWithNullValues; 132 | 133 | /** 134 | Creates and returns a JSON serializer with specified reading and writing options. 135 | 136 | @param readingOptions The specified JSON reading options. 137 | */ 138 | + (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions; 139 | 140 | @end 141 | 142 | #pragma mark - 143 | 144 | /** 145 | `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. 146 | 147 | By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: 148 | 149 | - `application/xml` 150 | - `text/xml` 151 | */ 152 | @interface LCSXMLParserResponseSerializer : LCSHTTPResponseSerializer 153 | 154 | @end 155 | 156 | #pragma mark - 157 | 158 | #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED 159 | 160 | /** 161 | `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. 162 | 163 | By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: 164 | 165 | - `application/xml` 166 | - `text/xml` 167 | */ 168 | @interface AFXMLDocumentResponseSerializer : LCSHTTPResponseSerializer 169 | 170 | - (instancetype)init; 171 | 172 | /** 173 | Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. 174 | */ 175 | @property (nonatomic, assign) NSUInteger options; 176 | 177 | /** 178 | Creates and returns an XML document serializer with the specified options. 179 | 180 | @param mask The XML document options. 181 | */ 182 | + (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask; 183 | 184 | @end 185 | 186 | #endif 187 | 188 | #pragma mark - 189 | 190 | /** 191 | `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. 192 | 193 | By default, `AFPropertyListResponseSerializer` accepts the following MIME types: 194 | 195 | - `application/x-plist` 196 | */ 197 | @interface LCSPropertyListResponseSerializer : LCSHTTPResponseSerializer 198 | 199 | - (instancetype)init; 200 | 201 | /** 202 | The property list format. Possible values are described in "NSPropertyListFormat". 203 | */ 204 | @property (nonatomic, assign) NSPropertyListFormat format; 205 | 206 | /** 207 | The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions." 208 | */ 209 | @property (nonatomic, assign) NSPropertyListReadOptions readOptions; 210 | 211 | /** 212 | Creates and returns a property list serializer with a specified format, read options, and write options. 213 | 214 | @param format The property list format. 215 | @param readOptions The property list reading options. 216 | */ 217 | + (instancetype)serializerWithFormat:(NSPropertyListFormat)format 218 | readOptions:(NSPropertyListReadOptions)readOptions; 219 | 220 | @end 221 | 222 | #pragma mark - 223 | 224 | /** 225 | `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. 226 | 227 | By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: 228 | 229 | - `image/tiff` 230 | - `image/jpeg` 231 | - `image/gif` 232 | - `image/png` 233 | - `image/ico` 234 | - `image/x-icon` 235 | - `image/bmp` 236 | - `image/x-bmp` 237 | - `image/x-xbitmap` 238 | - `image/x-win-bitmap` 239 | */ 240 | @interface LCSImageResponseSerializer : LCSHTTPResponseSerializer 241 | 242 | #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 243 | /** 244 | The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance. 245 | */ 246 | @property (nonatomic, assign) CGFloat imageScale; 247 | 248 | /** 249 | Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default. 250 | */ 251 | @property (nonatomic, assign) BOOL automaticallyInflatesResponseImage; 252 | #endif 253 | 254 | @end 255 | 256 | #pragma mark - 257 | 258 | /** 259 | `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. 260 | */ 261 | @interface LCSCompoundResponseSerializer : LCSHTTPResponseSerializer 262 | 263 | /** 264 | The component response serializers. 265 | */ 266 | @property (readonly, nonatomic, copy) NSArray *responseSerializers; 267 | 268 | /** 269 | Creates and returns a compound serializer comprised of the specified response serializers. 270 | 271 | @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. 272 | */ 273 | + (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers; 274 | 275 | @end 276 | 277 | ///---------------- 278 | /// @name Constants 279 | ///---------------- 280 | 281 | /** 282 | ## Error Domains 283 | 284 | The following error domain is predefined. 285 | 286 | - `NSString * const AFURLResponseSerializationErrorDomain` 287 | 288 | ### Constants 289 | 290 | `AFURLResponseSerializationErrorDomain` 291 | AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. 292 | */ 293 | FOUNDATION_EXPORT NSString * const LCSURLResponseSerializationErrorDomain; 294 | 295 | /** 296 | ## User info dictionary keys 297 | 298 | These keys may exist in the user info dictionary, in addition to those defined for NSError. 299 | 300 | - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` 301 | - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` 302 | 303 | ### Constants 304 | 305 | `AFNetworkingOperationFailingURLResponseErrorKey` 306 | The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. 307 | 308 | `AFNetworkingOperationFailingURLResponseDataErrorKey` 309 | The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. 310 | */ 311 | FOUNDATION_EXPORT NSString * const LCSNetworkingOperationFailingURLResponseErrorKey; 312 | 313 | FOUNDATION_EXPORT NSString * const LCSNetworkingOperationFailingURLResponseDataErrorKey; 314 | 315 | NS_ASSUME_NONNULL_END 316 | -------------------------------------------------------------------------------- /LeanCloudSocial.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "LeanCloudSocial" 3 | s.version = "2.0.0" 4 | s.summary = "LeanCloud iOS Social SDK for mobile backend." 5 | s.homepage = "https://leancloud.cn" 6 | s.license = { :type => "Commercial", :text => "© Copyright 2017 LeanCloud, Inc. See https://leancloud.cn/terms.html" } 7 | s.author = { "LeanCloud" => "support@leancloud.cn" } 8 | s.documentation_url = "https://leancloud.cn/docs/sns.html" 9 | s.platform = :ios, "7.0" 10 | s.source = { :git => "https://github.com/leancloud/leancloud-social-ios.git", :tag => s.version.to_s } 11 | s.source_files = "Classes/**/*.{h,m}" 12 | s.public_header_files = "Classes/AVUser+SNS.h", "Classes/AVOSCloudSNS.h" 13 | 14 | s.dependency "AVOSCloud" 15 | end 16 | -------------------------------------------------------------------------------- /LeanCloudSocial.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocial.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocial.xcodeproj/xcshareddata/xcschemes/LeanCloudSocial.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 35 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 67 | 68 | 74 | 75 | 76 | 77 | 78 | 79 | 85 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocial.xcodeproj/xcshareddata/xcschemes/LeanCloudSocialDynamic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocial.xcodeproj/xcshareddata/xcschemes/LeanCloudSocialTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 35 | 41 | 42 | 43 | 44 | 45 | 51 | 52 | 53 | 54 | 55 | 56 | 67 | 68 | 69 | 70 | 76 | 77 | 79 | 80 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocial/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocialDynamic/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocialTests/AVOSCloudSNSTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVOSCloudSNSTest.m 3 | // LeanCloudSocial 4 | // 5 | // Created by lzw on 15/10/21. 6 | // Copyright © 2015年 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import "AVSNSTestCase.h" 10 | 11 | @interface AVOSCloudSNS(Test) 12 | 13 | +(NSMutableDictionary*)ssoConfigs; 14 | 15 | @end 16 | 17 | @interface AVOSCloudSNSTest : AVSNSTestCase 18 | 19 | @end 20 | 21 | @implementation AVOSCloudSNSTest 22 | 23 | - (void)setUp { 24 | [super setUp]; 25 | } 26 | 27 | - (void)tearDown { 28 | [super tearDown]; 29 | } 30 | 31 | - (void)testSetupPlatform { 32 | [AVOSCloudSNS setupPlatform:AVOSCloudSNSSinaWeibo withAppKey:@"2548122881" andAppSecret:@"ba37a6eb3018590b0d75da733c4998f8" andRedirectURI:@"http://wanpaiapp.com/oauth/callback/sina"]; 33 | expect([AVOSCloudSNS ssoConfigs]).notTo.beNil(); 34 | expect([AVOSCloudSNS ssoConfigs][@(AVOSCloudSNSSinaWeibo)][@"appkey"]).to.equal(@"2548122881"); 35 | } 36 | 37 | - (void)testIsAppInstalledForType { 38 | for (AVOSCloudSNSType type = AVOSCloudSNSSinaWeibo; type <= AVOSCloudSNSWeiXin; type ++) { 39 | BOOL installed = [AVOSCloudSNS isAppInstalledForType:type]; 40 | expect(installed).to.beFalsy; 41 | } 42 | } 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocialTests/AVSNSTestCase.h: -------------------------------------------------------------------------------- 1 | // 2 | // AVSNSTestCase.h 3 | // LeanCloudSocial 4 | // 5 | // Created by lzw on 15/10/21. 6 | // Copyright © 2015年 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | const static void *AVOSCloudSNSNotifation = &AVOSCloudSNSNotifation; 15 | 16 | #define WAIT [self waitNotification:AVOSCloudSNSNotifation]; 17 | #define NOTIFY [self postNotification:AVOSCloudSNSNotifation]; 18 | 19 | @interface AVSNSTestCase : XCTestCase 20 | 21 | - (void)waitNotification:(const void *)notification; 22 | - (void)postNotification:(const void *)notification; 23 | 24 | - (AVUser *)registerOrLoginWithUsername:(NSString *)username password:(NSString *)password; 25 | 26 | @end -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocialTests/AVSNSTestCase.m: -------------------------------------------------------------------------------- 1 | // 2 | // AVSNSTestCase.m 3 | // LeanCloudSocial 4 | // 5 | // Created by lzw on 15/10/21. 6 | // Copyright © 2015年 LeanCloud. All rights reserved. 7 | // 8 | #import "AVSNSTestCase.h" 9 | 10 | @implementation AVSNSTestCase 11 | 12 | + (void)setUp { 13 | [super setUp]; 14 | NSString *appId = @"2jjvnj3938p6pns11r41dlte2n98bm6m7bkblm1cysttm7in"; 15 | NSString *appKey = @"7dtvdetcfggpalwtf91pdoootc7csxx0vxyi3ayqtbnlklq2"; 16 | [AVOSCloud setApplicationId:appId clientKey:appKey]; 17 | [AVOSCloud setAllLogsEnabled:YES]; 18 | } 19 | 20 | - (void)setUp { 21 | [super setUp]; 22 | } 23 | 24 | - (void)tearDown { 25 | [super tearDown]; 26 | } 27 | 28 | #pragma mark - Utils 29 | 30 | - (void)waitNotification:(const void *)notification { 31 | NSString *name = [NSString stringWithFormat:@"%p", notification]; 32 | [self expectationForNotification:name object:nil handler:nil]; 33 | [self waitForExpectationsWithTimeout:60 handler:nil]; 34 | } 35 | 36 | - (void)postNotification:(const void *)notification { 37 | NSString *name = [NSString stringWithFormat:@"%p", notification]; 38 | [[NSNotificationCenter defaultCenter] postNotificationName:name object:nil]; 39 | } 40 | 41 | - (AVUser *)registerOrLoginWithUsername:(NSString *)username password:(NSString *)password { 42 | AVUser *user = [AVUser user]; 43 | user.username = username; 44 | user.password = password; 45 | NSError *error; 46 | [user signUp:&error]; 47 | if (!error) { 48 | return user; 49 | } else if (error.code == kAVErrorUsernameTaken){ 50 | NSError *loginError; 51 | AVUser *loginUser = [AVUser logInWithUsername:username password:password error:&loginError]; 52 | XCTAssertNil(loginError); 53 | return loginUser; 54 | } else { 55 | [NSException raise:NSInternalInconsistencyException format:@"can not sign up or login"]; 56 | return nil; 57 | } 58 | } 59 | 60 | 61 | @end 62 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocialTests/AVUserSNSTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // LeanCloudSocialTests.m 3 | // LeanCloudSocialTests 4 | // 5 | // Created by lzw on 15/10/20. 6 | // Copyright © 2015年 LeanCloud. All rights reserved. 7 | // 8 | 9 | 10 | #import "AVSNSTestCase.h" 11 | 12 | @interface AVUserSNSTest : AVSNSTestCase 13 | 14 | @end 15 | 16 | @implementation AVUserSNSTest 17 | 18 | - (void)setUp { 19 | 20 | } 21 | 22 | - (void)tearDown { 23 | 24 | } 25 | 26 | - (void)testWeiboLogin { 27 | NSDictionary *authData = @{@"access_token": @"2.00_hkjqBJKf8mCe3c8acf73as6LRxC", @"id":@"1695406573", @"expires_at": @"2015-10-27T18:59:46.676Z", @"platform": @1}; 28 | [AVUser loginWithAuthData:authData platform:AVOSCloudSNSPlatformWeiBo block:^(AVUser *user, NSError *error) { 29 | expect(error).to.beNil(); 30 | expect(user).notTo.beNil(); 31 | expect(user.objectId).equal(@"55b8a95000b066e34529739d"); 32 | NOTIFY 33 | }]; 34 | WAIT 35 | } 36 | 37 | - (void)testAddOrDeleteAuthData { 38 | AVUser *user = [self registerOrLoginWithUsername:NSStringFromSelector(_cmd) password:@"123456"]; 39 | NSDictionary *authData = @{@"access_token":@"OezXcEiiBSKSxW0eoylIeN_WWsgxroiydYCNnIX5hyDjK3CwA1hc2bvS1oaaaYqwk8o-2bKJz2qlhCTl5MJBIw70tud0svhSApBGYTXjV5CNzUbvZUoIlo10kJg81IGht1bzyQ4-rVHJ3x4baiYz-g", @"expires_in":@(3600), @"openid":@"oazTlwQwmWLyzz7wxnAXDsSZUjcM"}; 40 | [user addAuthData:authData platform:AVOSCloudSNSPlatformWeiXin block:^(AVUser *user, NSError *error) { 41 | expect(error).to.beNil(); 42 | expect(user).notTo.beNil(); 43 | expect(user[@"authData"][@"weixin"]).beSupersetOf(authData); 44 | 45 | [user deleteAuthDataForPlatform:AVOSCloudSNSPlatformWeiXin block:^(AVUser *user, NSError *error) { 46 | expect(error).to.beNil(); 47 | expect(user).notTo.beNil(); 48 | expect(user[@"authData"][@"weixin"]).beNil(); 49 | NOTIFY 50 | }]; 51 | }]; 52 | WAIT 53 | } 54 | 55 | @end 56 | 57 | 58 | -------------------------------------------------------------------------------- /LeanCloudSocial/LeanCloudSocialTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // LeanCloudSocialDemo 4 | // 5 | // Created by Feng Junwen on 5/22/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import 12 | 13 | // 静态库方式引入 14 | #import 15 | 16 | // 动态库方式引入 17 | //#import 18 | 19 | @interface AppDelegate : UIResponder 20 | 21 | @property (strong, nonatomic) UIWindow *window; 22 | 23 | 24 | @end 25 | 26 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // LeanCloudSocialDemo 4 | // 5 | // Created by Feng Junwen on 5/22/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | NSString *appId = @"2jjvnj3938p6pns11r41dlte2n98bm6m7bkblm1cysttm7in"; 21 | NSString *appKey = @"7dtvdetcfggpalwtf91pdoootc7csxx0vxyi3ayqtbnlklq2"; 22 | [AVOSCloud setApplicationId:appId clientKey:appKey]; 23 | NSLog(@"setAppId:%@, appKey:%@", appId, appKey); 24 | [AVOSCloud setAllLogsEnabled:YES]; 25 | [AVOSCloud setLastModifyEnabled:YES]; 26 | return YES; 27 | } 28 | 29 | - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { 30 | return [AVOSCloudSNS handleOpenURL:url]; 31 | } 32 | 33 | - (void)applicationWillResignActive:(UIApplication *)application { 34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 35 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 36 | } 37 | 38 | - (void)applicationDidEnterBackground:(UIApplication *)application { 39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 41 | } 42 | 43 | - (void)applicationWillEnterForeground:(UIApplication *)application { 44 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 45 | } 46 | 47 | - (void)applicationDidBecomeActive:(UIApplication *)application { 48 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 49 | } 50 | 51 | - (void)applicationWillTerminate:(UIApplication *)application { 52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-Small.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-Small@2x.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small@3x.png", 19 | "scale" : "3x" 20 | }, 21 | { 22 | "size" : "40x40", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small-40@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-Small-40@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "57x57", 35 | "idiom" : "iphone", 36 | "filename" : "Icon.png", 37 | "scale" : "1x" 38 | }, 39 | { 40 | "size" : "57x57", 41 | "idiom" : "iphone", 42 | "filename" : "Icon@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "29x29", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-Small-1.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-Small@2x-1.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "40x40", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-Small-40.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-Small-40@2x-1.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "50x50", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-Small-50.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "50x50", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-Small-50@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "72x72", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-72.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "72x72", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-72@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "76x76", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-76.png", 109 | "scale" : "1x" 110 | }, 111 | { 112 | "size" : "76x76", 113 | "idiom" : "ipad", 114 | "filename" : "Icon-76@2x.png", 115 | "scale" : "2x" 116 | }, 117 | { 118 | "size" : "120x120", 119 | "idiom" : "car", 120 | "filename" : "Icon-60@2x-1.png", 121 | "scale" : "1x" 122 | }, 123 | { 124 | "size" : "24x24", 125 | "idiom" : "watch", 126 | "scale" : "2x", 127 | "role" : "notificationCenter", 128 | "subtype" : "38mm" 129 | }, 130 | { 131 | "size" : "27.5x27.5", 132 | "idiom" : "watch", 133 | "scale" : "2x", 134 | "role" : "notificationCenter", 135 | "subtype" : "42mm" 136 | }, 137 | { 138 | "size" : "29x29", 139 | "idiom" : "watch", 140 | "role" : "companionSettings", 141 | "scale" : "2x" 142 | }, 143 | { 144 | "size" : "29x29", 145 | "idiom" : "watch", 146 | "role" : "companionSettings", 147 | "scale" : "3x" 148 | }, 149 | { 150 | "size" : "40x40", 151 | "idiom" : "watch", 152 | "scale" : "2x", 153 | "role" : "appLauncher", 154 | "subtype" : "38mm" 155 | }, 156 | { 157 | "size" : "44x44", 158 | "idiom" : "watch", 159 | "scale" : "2x", 160 | "role" : "longLook", 161 | "subtype" : "42mm" 162 | }, 163 | { 164 | "size" : "86x86", 165 | "idiom" : "watch", 166 | "scale" : "2x", 167 | "role" : "quickLook", 168 | "subtype" : "38mm" 169 | }, 170 | { 171 | "size" : "98x98", 172 | "idiom" : "watch", 173 | "scale" : "2x", 174 | "role" : "quickLook", 175 | "subtype" : "42mm" 176 | } 177 | ], 178 | "info" : { 179 | "version" : 1, 180 | "author" : "xcode" 181 | } 182 | } -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x-1.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-72.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-1.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x-1.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/leancloud/leancloud-social-ios/126689432c5532490a855e1e87ebc53bfdf6c06a/LeanCloudSocialDemo/LeanCloudSocialDemo/Images.xcassets/AppIcon.appiconset/Icon@2x.png -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleURLTypes 22 | 23 | 24 | CFBundleTypeRole 25 | Editor 26 | CFBundleURLName 27 | weibo 28 | CFBundleURLSchemes 29 | 30 | sinaweibosso.2548122881 31 | 32 | 33 | 34 | CFBundleTypeRole 35 | Editor 36 | CFBundleURLName 37 | qzone 38 | CFBundleURLSchemes 39 | 40 | tencent100512940 41 | 42 | 43 | 44 | CFBundleTypeRole 45 | Editor 46 | CFBundleURLName 47 | weixin 48 | CFBundleURLSchemes 49 | 50 | wxa3eacc1c86a717bc 51 | 52 | 53 | 54 | CFBundleVersion 55 | 1.0.0 56 | LSApplicationQueriesSchemes 57 | 58 | mqqOpensdkSSoLogin 59 | weixin 60 | sinaweibohdsso 61 | sinaweibosso 62 | 63 | LSRequiresIPhoneOS 64 | 65 | NSAppTransportSecurity 66 | 67 | NSExceptionDomains 68 | 69 | gtimg.cn 70 | 71 | NSIncludesSubdomains 72 | 73 | NSThirdPartyExceptionAllowsInsecureHTTPLoads 74 | 75 | 76 | idqqimg.com 77 | 78 | NSIncludesSubdomains 79 | 80 | NSThirdPartyExceptionAllowsInsecureHTTPLoads 81 | 82 | 83 | qq.com 84 | 85 | NSIncludesSubdomains 86 | 87 | NSThirdPartyExceptionAllowsInsecureHTTPLoads 88 | 89 | 90 | weibo.cn 91 | 92 | NSIncludesSubdomains 93 | 94 | NSThirdPartyExceptionRequiresForwardSecrecy 95 | 96 | 97 | weibo.com 98 | 99 | NSIncludesSubdomains 100 | 101 | NSThirdPartyExceptionAllowsInsecureHTTPLoads 102 | 103 | NSThirdPartyExceptionRequiresForwardSecrecy 104 | 105 | 106 | 107 | 108 | UILaunchStoryboardName 109 | LaunchScreen 110 | UIMainStoryboardFile 111 | Main 112 | UIRequiredDeviceCapabilities 113 | 114 | armv7 115 | 116 | UISupportedInterfaceOrientations 117 | 118 | UIInterfaceOrientationPortrait 119 | UIInterfaceOrientationLandscapeLeft 120 | UIInterfaceOrientationLandscapeRight 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/ResultViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ResultViewController.h 3 | // LeanCloudSocialDemo 4 | // 5 | // Created by Feng Junwen on 5/26/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ResultViewController : UIViewController 12 | 13 | @property (nonatomic, strong) NSString *infoText; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/ResultViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ResultViewController.m 3 | // LeanCloudSocialDemo 4 | // 5 | // Created by Feng Junwen on 5/26/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import "ResultViewController.h" 10 | 11 | @interface ResultViewController () 12 | 13 | @property (weak, nonatomic) IBOutlet UILabel *infoLabel; 14 | 15 | @end 16 | 17 | @implementation ResultViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | self.infoLabel.text = self.infoText; 22 | } 23 | 24 | - (void)didReceiveMemoryWarning { 25 | [super didReceiveMemoryWarning]; 26 | // Dispose of any resources that can be recreated. 27 | } 28 | 29 | /* 30 | #pragma mark - Navigation 31 | 32 | // In a storyboard-based application, you will often want to do a little preparation before navigation 33 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 34 | // Get the new view controller using [segue destinationViewController]. 35 | // Pass the selected object to the new view controller. 36 | } 37 | */ 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // LeanCloudSocialDemo 4 | // 5 | // Created by Feng Junwen on 5/22/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // LeanCloudSocialDemo 4 | // 5 | // Created by Feng Junwen on 5/22/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "AppDelegate.h" 11 | #import "ResultViewController.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | [AVOSCloudSNS setupPlatform:AVOSCloudSNSSinaWeibo withAppKey:@"2548122881" andAppSecret:@"ba37a6eb3018590b0d75da733c4998f8" andRedirectURI:@"http://wanpaiapp.com/oauth/callback/sina"]; 22 | [AVOSCloudSNS setupPlatform:AVOSCloudSNSQQ withAppKey:@"100512940" andAppSecret:@"afbfdff94b95a2fb8fe58a8e24c4ba5f" andRedirectURI:nil]; 23 | [AVOSCloudSNS setupPlatform:AVOSCloudSNSWeiXin withAppKey:@"wxa3eacc1c86a717bc" andAppSecret:@"b5bf245970b2a451fb8cebf8a6dff0c1" andRedirectURI:nil]; 24 | } 25 | 26 | - (void)didReceiveMemoryWarning { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | - (IBAction)weiboLogin:(id)sender { 32 | // 如果安装了微博,直接跳转到微博应用,否则跳转至网页登录 33 | [AVOSCloudSNS loginWithCallback:^(id object, NSError *error) { 34 | if (error) { 35 | NSLog(@"failed to get authentication from weibo. error: %@", error.description); 36 | } else { 37 | #pragma clang diagnostic push 38 | #pragma clang diagnostic ignored "-Wdeprecated" 39 | [AVUser loginWithAuthData:object platform:AVOSCloudSNSPlatformWeiBo block:^(AVUser *user, NSError *error) { 40 | if ([self filterError:error]) { 41 | [self loginSucceedWithUser:user authData:object]; 42 | } 43 | }]; 44 | #pragma clang diagnostic pop 45 | } 46 | } toPlatform:AVOSCloudSNSSinaWeibo]; 47 | } 48 | 49 | - (IBAction)weiboWebLogin:(id)sender { 50 | //此处的 URL 从网站管理台获取,组件->社交,把 AppId 和 Secret Key 写在管理台 51 | //管理台生成网页url,来跳转至第三方登录的地址 52 | [AVOSCloudSNS loginWithURL:[NSURL URLWithString:@"https://leancloud.cn/1.1/sns/goto/vdhgf2lq96udqd73"] callback:^(id object, NSError *error) { 53 | NSLog(@"object : %@, error : %@", object, error); 54 | if ([self filterError:error]) { 55 | #pragma clang diagnostic push 56 | #pragma clang diagnostic ignored "-Wdeprecated" 57 | [AVUser loginWithAuthData:object platform:AVOSCloudSNSPlatformWeiBo block:^(AVUser *user, NSError *error) { 58 | if ([self filterError:error]) { 59 | [self loginSucceedWithUser:user authData:object]; 60 | } 61 | }]; 62 | #pragma clang diagnostic pop 63 | } 64 | }]; 65 | } 66 | 67 | - (IBAction)qzoneLogin:(id)sender { 68 | // 如果安装了QQ,则跳转至应用,否则跳转至网页 69 | [AVOSCloudSNS loginWithCallback:^(id object, NSError *error) { 70 | if (error) { 71 | NSLog(@"failed to get authentication from weibo. error: %@", error.description); 72 | } else { 73 | #pragma clang diagnostic push 74 | #pragma clang diagnostic ignored "-Wdeprecated" 75 | [AVUser loginWithAuthData:object platform:AVOSCloudSNSPlatformQQ block:^(AVUser *user, NSError *error) { 76 | if ([self filterError:error]) { 77 | [self loginSucceedWithUser:user authData:object]; 78 | } 79 | }]; 80 | #pragma clang diagnostic pop 81 | } 82 | } toPlatform:AVOSCloudSNSQQ]; 83 | } 84 | 85 | - (IBAction)qzoneLogin2:(id)sender { 86 | // 这个需要到后台填写 应用 id 和 secret key 87 | [AVOSCloudSNS loginWithURL:[NSURL URLWithString:@"https://leancloud.cn/1.1/sns/goto/36wvmahsj3davi90"] callback:^(id object, NSError *error) { 88 | NSLog(@"object : %@ error: %@", object, error); 89 | if ([self filterError:error]) { 90 | // clean authData; 91 | NSMutableDictionary *authData = [NSMutableDictionary dictionary]; 92 | [authData setObject:[object objectForKey:@"openid"] forKey:@"openid"]; 93 | [authData setObject:[object objectForKey:@"expires_in"] forKey:@"expires_in"]; 94 | [authData setObject:[object objectForKey:@"access_token"] forKey:@"access_token"]; 95 | #pragma clang diagnostic push 96 | #pragma clang diagnostic ignored "-Wdeprecated" 97 | [AVUser loginWithAuthData:authData platform:AVOSCloudSNSPlatformQQ block:^(AVUser *user, NSError *error) { 98 | if ([self filterError:error]) { 99 | [self loginSucceedWithUser:user authData:authData]; 100 | } 101 | }]; 102 | #pragma clang diagnostic pop 103 | } 104 | }]; 105 | } 106 | 107 | - (IBAction)weixinLogin:(id)sender { 108 | if ([AVOSCloudSNS isAppInstalledForType:AVOSCloudSNSWeiXin]) { 109 | // 请到真机测试 110 | [AVOSCloudSNS loginWithCallback:^(id object, NSError *error) { 111 | // { 112 | // "access_token" = "OezXcEiiBSKSxW0eoylIeN_WWsgxroiydYCNnIX5hyDjK3CwA1hc2bvS1oaaaYqwpP7_vb7nhWadkCXGQukQ0hVjCPvWDHjGqSAF0utf2xvXG5coBh2RZViBKxd0POkMDYu0vNLQoBOTfl9yDzzLJQ"; 113 | // avatar = "http://wx.qlogo.cn/mmopen/3Qx7ibib84ibZMVgJAaEAN7HW8Kyc3s0hLTKcuSlzSJibG8Mbr4g3PsApj8G1u5XxLq9Dnp7XiafxL9h4RSCUIbX39l6lc90Kyzcx/0"; 114 | // "expires_at" = "2015-07-30 08:38:24 +0000"; 115 | // id = oazTlwQwmWLyzz7wxnAXDsSZUjcM; 116 | // platform = 3; 117 | // "raw-user" = { 118 | // city = ""; 119 | // country = CN; 120 | // headimgurl = "http://wx.qlogo.cn/mmopen/3Qx7ibib84ibZMVgJAaEAN7HW8Kyc3s0hLTKcuSlzSJibG8Mbr4g3PsApj8G1u5XxLq9Dnp7XiafxL9h4RSCUIbX39l6lc90Kyzcx/0"; 121 | // language = "zh_CN"; 122 | // nickname = "\U674e\U667a\U7ef4"; 123 | // openid = oazTlwQwmWLyzz7wxnAXDsSZUjcM; 124 | // privilege = ( 125 | // ); 126 | // province = Beijing; 127 | // sex = 1; 128 | // unionid = ox7NLs813rA9sP6QPbadkulxgHn8; 129 | // }; 130 | // username = "\U674e\U667a\U7ef4"; 131 | // } 132 | 133 | NSLog(@"object : %@ error:%@", object, error); 134 | if ([self filterError:error]) { 135 | #pragma clang diagnostic push 136 | #pragma clang diagnostic ignored "-Wdeprecated" 137 | [AVUser loginWithAuthData:object platform:AVOSCloudSNSPlatformWeiXin block:^(AVUser *user, NSError *error) { 138 | if ([self filterError:error]) { 139 | [self loginSucceedWithUser:user authData:object]; 140 | } 141 | }]; 142 | #pragma clang diagnostic pop 143 | } 144 | } toPlatform:AVOSCloudSNSWeiXin]; 145 | } else { 146 | [self alert:@"没有安装微信,暂不能登录"]; 147 | } 148 | } 149 | 150 | - (IBAction)wechatLogin:(id)sender { 151 | // [AVOSCloudSNS loginWithURL:[NSURL URLWithString:@"https://leancloud.cn/1.1/sns/goto/1t261wmvqzthpx0y"] callback:^(id object, NSError *error) { 152 | // NSLog(@"object : %@ error: %@", object, error); 153 | // }]; 154 | } 155 | 156 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 157 | [super prepareForSegue:segue sender:sender]; 158 | if ([segue.identifier isEqualToString:@"goLoginResult"]) { 159 | ResultViewController *vc = (ResultViewController *)segue.destinationViewController; 160 | vc.infoText = sender; 161 | } 162 | } 163 | 164 | - (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender 165 | { 166 | } 167 | 168 | - (void)alert:(NSString *)message { 169 | UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:message delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil]; 170 | [alert show]; 171 | } 172 | 173 | - (BOOL)filterError:(NSError *)error { 174 | if (error) { 175 | [self alert:[error localizedDescription]]; 176 | return NO; 177 | } 178 | return YES; 179 | } 180 | 181 | - (void)loginSucceedWithUser:(AVUser *)user authData:(NSDictionary *)authData{ 182 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle: nil]; 183 | ResultViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"ResultViewControllerID"]; 184 | vc.infoText = [NSString stringWithFormat:@"authData:%@", authData]; 185 | [self presentViewController:vc animated:YES completion:nil]; 186 | } 187 | 188 | 189 | @end 190 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // LeanCloudSocialDemo 4 | // 5 | // Created by Feng Junwen on 5/22/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LeanCloudSocialDemo/LeanCloudSocialDemoTests/LeanCloudSocialDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // LeanCloudSocialDemoTests.m 3 | // LeanCloudSocialDemoTests 4 | // 5 | // Created by Feng Junwen on 5/22/15. 6 | // Copyright (c) 2015 LeanCloud. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface LeanCloudSocialDemoTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation LeanCloudSocialDemoTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /LeanCloudSocialDynamic.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | component = "LeanCloudSocial" 4 | 5 | s.name = "LeanCloudSocialDynamic" 6 | s.version = "0.0.9" 7 | s.summary = "LeanCloud iOS Social SDK for mobile backend." 8 | s.homepage = "https://leancloud.cn" 9 | s.license = { :type => "Commercial", :text => "© Copyright 2015 LeanCloud, Inc. See https://leancloud.cn/terms.html" } 10 | s.author = { "LeanCloud" => "support@leancloud.cn" } 11 | s.documentation_url = "https://leancloud.cn/docs/sns.html" 12 | s.platform = :ios, "8.0" 13 | s.source = { :git => "https://github.com/leancloud/leancloud-social-ios.git", :tag => s.version.to_s } 14 | s.source_files = "Classes/*.{h,m}" 15 | s.public_header_files = "Classes/AVUser+SNS.h", "Classes/AVOSCloudSNS.h" 16 | 17 | s.dependency "AVOSCloudDynamic" 18 | s.dependency "AFNetworking", "~> 2.0" 19 | 20 | s.xcconfig = { 21 | "OTHER_LDFLAGS" => "$(inherited) -ObjC", 22 | "FRAMEWORK_SEARCH_PATHS" => "\"${PODS_ROOT}/#{s.name}/**\"", 23 | "LD_RUNPATH_SEARCH_PATHS" => "@loader_path/../Frameworks" 24 | } 25 | end 26 | -------------------------------------------------------------------------------- /LeanCloudSocialDynamic/LeanCloudSocialDynamic.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LeanCloudSocialDynamic/LeanCloudSocialDynamic/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | 3 | workspace 'LeanCloudSocial.xcworkspace' 4 | 5 | xcodeproj 'LeanCloudSocialDemo/LeanCloudSocialDemo.xcodeproj' 6 | 7 | target 'LeanCloudSocial' do 8 | platform :ios, '7.0' 9 | 10 | xcodeproj 'LeanCloudSocial/LeanCloudSocial.xcodeproj' 11 | pod 'AVOSCloud' 12 | end 13 | 14 | target 'LeanCloudSocialTests' do 15 | platform :ios, '7.0' 16 | xcodeproj 'LeanCloudSocial/LeanCloudSocial.xcodeproj' 17 | pod 'LeanCloudSocial', :path => '.' 18 | pod 'Expecta' 19 | end 20 | 21 | target 'LeanCloudSocialDemo' do 22 | platform :ios, '7.0' 23 | 24 | xcodeproj 'LeanCloudSocialDemo/LeanCloudSocialDemo.xcodeproj' 25 | pod 'LeanCloudSocial', :path => '.' 26 | end 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 本 Demo 不再更新维护。 3 | 4 | ## leancloud-social-ios 5 | 6 | [![Podspec][podspec-svg]][podspec-link] 7 | ![Platforms][platforms-svg] 8 | [![Dependencies][dependencies-svg]][dependencies-link] 9 | 10 | LeanCloud Social Network 模块是 [LeanCloud](https://leancloud.cn) 开源的一个第三方平台登录、分享组件,目前支持 QQ 空间、新浪微博、微信三个平台,具体使用方法可以参看[文档](https://leancloud.cn/docs/sns.html)。 11 | 12 | ## 视频演示 13 | 请用浏览器打开[视频](http://ac-x3o016bx.clouddn.com/a294809feb0c6a8a.mp4) ,相应的代码见[这里](https://github.com/leancloud/leanchat-ios/blob/master/LeanChat/LeanChat/controllers/entry/CDLoginVC.m#L252-L278)。 14 | 15 | ## 如何运行 demo 16 | 17 | ``` 18 | cd leancloud-social-ios 19 | pod install --verbose (如果本地安装过 AVOSCloud 库,则可以加选项 --no-repo-update,加快速度) 20 | open LeanCloudSocial.xcworkspace 21 | ``` 22 | 23 | ## 使用方法 24 | 具体的使用方法可以参看[文档](https://leancloud.cn/docs/sns.html)。 25 | 我们推荐用 pod 方式来安装这一组件 26 | ``` 27 | pod 'LeanCloudSocial' 28 | ``` 29 | 30 | 也可参考 [LeanChat](https://github.com/leancloud/leanchat-ios),这个应用使用了这一组件来实现第三方登录,像上面视频所演示的那样。相应代码见[这里](https://github.com/leancloud/leanchat-ios/blob/master/LeanChat/LeanChat/controllers/entry/CDLoginVC.m#L181-L278)。 31 | 32 | ## 宝贵意见 33 | 如果有任何问题,欢迎提 [issue](https://github.com/leancloud/leancloud-social-ios/issues) ,写上你不明白的地方,看到后会尽快给予帮助。 34 | 35 | ## 如何贡献 36 | 你可以通过提 issue 或者发 pull request 的方式,来贡献代码。开源世界因你我参与而更加美好。 37 | 38 | 39 | ## 项目结构 40 | 为了便于测试,我们将 Social Network 模块的代码和 demo 都放在了一起,整个 repo 结构如下: 41 | 42 | ``` 43 | . 44 | ├── LeanCloudSocial <--- LeanCloudSocial 框架代码 45 | ├── LeanCloudSocial.podspec <--- podspec 描述 46 | ├── LeanCloudSocialDemo <--- Demo 项目 47 | │   ├── LeanCloudSocialDemo 48 | │   ├── LeanCloudSocialDemo.xcodeproj 49 | │   ├── LeanCloudSocialDemo.xcworkspace <--- Demo及框架 workspace,这里打开 50 | │   └── Podfile <--- Demo和框架的 Podfile 51 | ├── LeanCloudSocialTests 52 | └── README.md 53 | ``` 54 | 55 | ## 如何编译 56 | ### Xcode 编译 57 | 在 Xcode 中选择 UniversalFramework Target,设备选为 iOS Device,在 Product 菜单中选择 Archive 即可开始编译。编译完成之后会在当前 build 目录下 58 | 59 | ``` 60 | . 61 | ├── LeanCloudSocial.build 62 | │   ├── Release-iphoneos 63 | │   │   └── LeanCloudSocial.build 64 | │   └── Release-iphonesimulator 65 | │   └── LeanCloudSocial.build 66 | └── Release-iphoneuniversal 67 | └── LeanCloudSocial.framework <------ 这里就是编译出来的 framework 68 | ``` 69 | 70 | ### 命令行编译 71 | 在项目根目录下执行如下语句,即可开始编译 72 | 73 | ``` 74 | xcodebuild -target UniversalFramework -config Release 75 | ``` 76 | 77 | 编译之后的结果文件目录和上面示例一致。 78 | 79 | ## 其他问题 80 | 81 | Q: 使用 CocoaPods 安装,提示无法找到 ` AVUser+SNS.h ` 文件的错误 82 | 83 | A:删除你项目根目录下的 Pods 文件夹以及 Podfile.lock这个文件,然后重新 pod install 84 | 85 | ![enter image description here](http://i68.tinypic.com/28u19xx.jpg) 86 | 87 | 88 | Q :我要增加其他平台,该怎么做? 89 | 90 | A:我可以使用其他 SDK 来做登录,然后把授权信息绑定到 AVUser 吗? 91 | 92 | 93 | ## 发布日志 94 | 发布流程:更改 podspec 版本,打 tag,推送到仓库,执行`pod trunk push LeanCloudSocial.podspec --verbose --allow-warnings --use-libraries`。 95 | 96 | 0.0.8 97 | 98 | * fix bug:修复因为 null 而引起崩溃的 bug 99 | 100 | 0.0.7 101 | * 因为 AFNetworking 2.6 最低支持 7.0,同时考虑到微信等应用也是最低支持 7.0。故 LeanCloudSocial 项目支持的 iOS 版本从 6.0 提升成 7.0。 102 | * 仅公开 AVUser+SNS.h、AVOSCloudSNS.h 头文件。 103 | 104 | 0.0.6 105 | * 调整目录结构。同时发布动态库,可通过 `pod LeanCloudSocialDynamic` 引入到项目中。 106 | 107 | 0.0.5 108 | * 重构部分函数,使命名更符合 Cocoa 规范 109 | 110 | 0.0.4 111 | * 支持微信 SSO 登录,对 -[AVOSCloudSNS loginWithCallback:toPlatform] 第二个参数传入 AVOSCloudSNSWeiXin 即可。 112 | * 同时提供 -[AVOSCloudSNS isAppInstalledWithType] 来检测相应的应用有没安装。 113 | 114 | 0.0.3 115 | * 重命名 LCHttpClient 至 AVSNSHttpClient,避免和其它LC的模块冲突 116 | 117 | 0.0.2 118 | * 使用 AFNetworking ~2.0 版本,使得主项目能够和此库共用同一个 AFNetworking 版本。如果主项目使用的是 AFNetworking 1.0,推荐使用 LeanCloudSocial 0.0.1 版本。 119 | 120 | 0.0.1 121 | * 重命名模块后发布 122 | 123 | ## License 124 | MIT 125 | 126 | [podspec-svg]: https://img.shields.io/cocoapods/v/LeanCloudSocial.svg 127 | [podspec-link]: https://cocoapods.org/pods/LeanCloudSocial 128 | 129 | [platforms-svg]: https://img.shields.io/badge/platform-ios-lightgrey.svg 130 | 131 | [dependencies-svg]: https://img.shields.io/badge/dependencies-2-yellowgreen.svg 132 | [dependencies-link]: https://github.com/leancloud/leancloud-social-ios/blob/master/LeanCloudSocial.podspec#L15-L16 133 | -------------------------------------------------------------------------------- /build-framework.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -v 2 | 3 | # 使用这个脚本来创建 LeanCloudSocial 的静态库与动态库 4 | # 动态库最低支持 iOS 8.0 5 | 6 | xcodebuild -workspace LeanCloudSocial.xcworkspace -scheme UniversalFramework -configuration Release 7 | --------------------------------------------------------------------------------