iOS开发总结--三方平台开发之分享
1.前言
在公司参与了多个应用三方平台的开发,涉及微信、微博、QQ、Facebook、meetup等,总结一下一般的接入三方平台SDK方法。
2.接入三方SDK
任何应用要接入三方平台,都需要在该平台上填写应用相关信息,创建应用。比如,bundle等。
I)微博
1)创建应用
创建好微博应用之后,就可以获得微博的App Key和App Secret,需要设置回调页面的可以到高级设置中设置。
创建完应用之后,还要上传应用图标以及宣传海报。宣传海报比较蛋疼,一定要体现应用和微博的关系,否则不会过审,但App Key是可以用的,AppKey主要是用于应用中的scheme打开微博,同样,其他平台的App Key的作用也是如此。
2)下载SDK
接入微博,可以下载SDK,利用SDK中提供的方法集成分享
https://github.com/sinaweibosdk/weibo_ios_sdk
也可以用微博提供的API http://open.weibo.com/wiki/%E5%BE%AE%E5%8D%9AAPI 进行数据交互
直接用API相对麻烦点,所以还是SDK吧少年。
下载完SDK就可以直接拖进项目了。我的习惯是,在项目文件夹中创建一个新的文件目录存放SDK,然后在项目中add进去。这样的好处是,你不用担心link的path出问题,而且Xcode会自动添加这些文件到项目的bundle中,避免出现各种architecture的错误。
3)添加URL TYPE
可以在Targets->Info->URL Types中添加微博的scheme,也可以到info.plist中修改,open as source code打开,修改plist的xml 。
补充:plist是键值对的形式,其中URL Types的key为CFBundleURLTypes,值得类型为数组。例如下面的XML
<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLName</key> <string>weixin</string> <key>CFBundleURLSchemes</key> <array> <string>微信的app ID</string> </array> </dict> <dict> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLName</key> <string>com.weibo</string> <key>CFBundleURLSchemes</key> <array> <string>wb微博的App key</string> </array> </dict> <dict> <key>CFBundleTypeRole</key> <string>Editor</string> <key>CFBundleURLName</key> <string>tencent</string> <key>CFBundleURLSchemes</key> <array> <string>tencentQQ的 App ID</string> </array> </dict> </array>
这里,补充一下,微博的scheme的URL形式是 wb+微博的App key;微信的URL的scheme就是 微信的AppID;QQ的URL的scheme为 tencent+QQ的App ID。
4)适配iOS9
升级iOS 9之后,苹果为了保证用户的隐私安全,采用了安全传输协议,所以,要在info.plist设置一些属性。具体如下。
<!-- URL 白名单 --> <key>LSApplicationQueriesSchemes</key> <array> <string>wechat</string> <string>weixin</string> <string>sinaweibohd</string> <string>sinaweibo</string> <string>sinaweibosso</string> <string>weibosdk</string> <string>weibosdk2.5</string> <string>mqqapi</string> <string>mqq</string> <string>mqqOpensdkSSoLogin</string> <string>mqqconnect</string> <string>mqqopensdkdataline</string> <string>mqqopensdkgrouptribeshare</string> <string>mqqopensdkfriend</string> <string>mqqopensdkapi</string> <string>mqqopensdkapiV2</string> <string>mqqopensdkapiV3</string> <string>mqzoneopensdk</string> <string>wtloginmqq</string> <string>wtloginmqq2</string> <string>mqqwpa</string> <string>mqzone</string> <string>mqzonev2</string> <string>mqzoneshare</string> <string>wtloginqzone</string> <string>mqzonewx</string> <string>mqzoneopensdkapiV2</string> <string>mqzoneopensdkapi19</string> <string>mqzoneopensdkapi</string> <string>mqzoneopensdk</string> <string>alipay</string> <string>alipayshare</string> <string>fbapi</string> <string>fb-messenger-api</string> <string>fbauth2</string> <string>fbshareextension</string> </array> <!-- http请求 --> <key>LSRequiresIPhoneOS</key> <true/> <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> <key>NSExceptionDomains</key> <dict> <key>akamaihd.net</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>facebook.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>fbcdn.net</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>sina.cn</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>sina.com.cn</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>sinaimg.cn</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>sinajs.cn</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>weibo.cn</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> <key>weibo.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key> <true/> <key>NSThirdPartyExceptionRequiresForwardSecrecy</key> <false/> </dict> </dict> </dict>
5)引入framework
三方平台的sdk中需要苹果的基本框架,为避免报错直接引入。以下是整理的微博、微信、QQ等需要引入的framework。
在Targets->Build Phases->Link Binary With Libraries中添加QuartzCore.framework、ImageIO.framework、SystemConfiguration.framework、
Security.framework、CoreTelephony.framework、CoreText.framework、 UIKit.framework、Foundation.framework、CoreGraphics.framework 、libz.dylib、 libsqlite3.dylib、SystemConfiguration.framework,libz.dylib,libsqlite3.0.dylib,libc++.dylib,
”Security.framework”, “libiconv.dylib”,“SystemConfiguration.framework”,“CoreGraphics.Framework”、“libsqlite3.dylib”、“CoreTelephony.framework”、“libstdc++.dylib”、“libz.dylib”。
以上的framework加入工程之后,微信、微博、QQ等平台不会报错。
6)设置link flags
程序 Target->Buid Settings->Linking 下 Other Linker Flags 项添加-ObjC。
II)微信SDK接入 III)QQ SDK接入 暂不赘述。参照微博。
微信的开放平台: https://open.weixin.qq.com
微博的开放平台:http://open.weibo.com
QQ的开放平台:http://open.qq.com/
切记,任何三方平台都需要先在该平台创建应用,填写相关信息,上传应用图标。
3.分享的通用单例
I)创建通用分享单例
创建新的Objective-C文件,在头文件中设置分享平台类型,三方平台应用相关宏定义,具体如下。
#import "NYShareObject.h" /** * 分享类型:微信、朋友圈、微博、QQ */ typedef NS_ENUM(NSInteger, NYShareType) { NYShareTypeQQ, /**< QQ */ NYShareTypeWeChatSession, /**< 微信好友对话 */ NYShareTypeWeChatMomemts, /**< 微信朋友圈 */ NYShareTypeWeibo, /**< 新浪微博 */ }; /** * 错误码类型 **/ typedef NS_ENUM(NSInteger, NYErrorCode) { NYErrorCodeShareWeibo, NYErrorCodeShareWeChatMoments, NYErrorCodeShareWeChatSession, }; typedef void(^NYShareComletion)(NSError *error, id response);/**< 回调 */ /** * 出错域 **/ #define ERROR_DOMAIN_SHARE_WEIBO @"WEIBO_SHARE" #define ERROR_DOMAIN_SHARE_WECHAT_MOMENTS @"WECHAT_MOMENTS" #define ERROR_DOMAIN_SHARE_WECHAT_SESSION @"WECHAT_SESSION" //三方平台注册应用 ///--- 微信 --- #define WX_APP_ID @"微信应用的App ID" #define WX_APP_SECRET @"微信应用的App Secret" ///--- 微博 --- #define WB_APP_KEY @"微博应用的App Key" #define WB_APP_SECRET @"微博应用的App Secret" #define WB_REDIRECT_URL @"微博应用的回调接口" //QQ 创友 #define QQ_APP_ID @"QQ的App ID" #define QQ_APP_KEY @"QQ的App Key" /** * 三方分享类 **/ @interface NYShareEngine : NSObject <WBHttpRequestDelegate, WeiboSDKDelegate, WXApiDelegate> @property (strong, nonatomic) NSString *WeiboToken; /**< weibo token*/ @property (strong, nonatomic) NSString *WeChatToken;/**< wechat */ @property (strong, nonatomic) NSString *WeChatRefreshToken;/**< wechat */ @end
解释:
a)以上代码中NYShareType用于区分分享的类型;
b)出错域以及出错码用于自定义NSError对象,方便查找错误。
c)NYShareComletion用于回调方法。
d)单例须要遵从微信微博的协议,以便对请求和响应、应用代理做数据处理。
e)NYShareObject 是用于分享的数据模型,比如同样是由标题、内容、图片等信息,分享到不同平台具体的分享的对象形式不一样,在这个文件中集成。在这个文件中,引入了需要的SDK头文件,如
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "WeiboSDK.h" #import "WXApi.h" #import <TencentOpenAPI/QQApiInterface.h> #import <TencentOpenAPI/QQApiInterfaceObject.h> #import <TencentOpenAPI/TencentOAuthObject.h> #import <TencentOpenAPI/TencentOAuth.h>
II)公有方法
在共享单例的头文件中加入以下代码
+ (instancetype)sharedEngine;/**< 单例 */ - (void)registerApp;/**< 注册应用 */ /** * 分享统一方法 * @param shareType 分享类型,包括QQ、微信好友、微信朋友圈、微博 @see NYShareType * @param object 分享的图文对象 * @param completion 回调函数 **/ - (void)shareWithShareType:(NYShareType)shareType object:(NYShareObject *)object completion:(NYShareComletion)completion; #pragma mark - delegate /** * 分享打开三方应用代理回调方法 **/ - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url; /** * 分享打开三方应用代理回调方法 **/ - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation;
III)单例方法
如果做通用分享的业务功能,可以创建单例,不仅可以降低内存损耗,也能统一进行不同平台的分享操作,减少代码量,也使项目结构更清晰。
+ (instancetype)sharedEngine { static NYShareEngine *share = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ share = [[self alloc] init]; }); return share; }
以上是单例方法,然后重写init方法,这样在创建单例的时候会被调用。
- (instancetype)init { self = [super init]; if (self) { } return self; }
IV)注册应用
如果要使用三方平台的SDK方法,第一步要做的就是注册应用,具体如下。
- (void)registerApp { [WeiboSDK registerApp:WB_APP_KEY]; [WeiboSDK enableDebugMode:NO]; [WXApi registerApp:WX_APP_ID]; [[TencentOAuth alloc] initWithAppId:QQ_APP_ID andDelegate:nil]; }
V)通用分享方法
- (void)shareWithShareType:(NYShareType)shareType object:(NYShareObject *)object completion:(NYShareComletion)completion { self.completion = completion; self.shareType = shareType; self.shareObject = object; [self shareRequest:shareType]; } - (void)shareRequest:(NYShareType)shareType { switch (shareType) { case NYShareTypeQQ: { SendMessageToQQReq *request = [SendMessageToQQReq reqWithContent:[self.shareObject QQNewsObject]]; [QQApiInterface sendReq:request]; break; } case NYShareTypeWeChatSession: case NYShareTypeWeChatMomemts: { SendMessageToWXReq *req = [[SendMessageToWXReq alloc] init]; req.bText = NO; req.message = [self.shareObject WeChatMessageObject]; req.scene = (self.shareType == NYShareTypeWeChatSession)?WXSceneSession:WXSceneTimeline; [WXApi sendReq:req]; break; } case NYShareTypeWeibo: { WBAuthorizeRequest *request = [[WBAuthorizeRequest alloc] init]; request.redirectURI = WB_REDIRECT_URL; request.scope = @"all"; WBSendMessageToWeiboRequest *sendMsg = [WBSendMessageToWeiboRequest requestWithMessage:[self.shareObject WeiboMessageObject] authInfo:request access_token:self.WeiboToken]; [WeiboSDK sendRequest:sendMsg]; break; } default: break; } }
解释:
a)- (void)shareWithShareType:(NYShareType)shareType object:(NYShareObject *)object completion:(NYShareComletion)completion方法是通用分享方法,设置分享单例的具体分享类型、分享的对象以及分享后的回调函数。
b)- (void)shareRequest:(NYShareType)shareType方法调用SDK分享方法,整合在一个方法中,私有方法。
VI)微博、微信响应及请求的协议方法
#pragma mark - weibo delegate - (void)didReceiveWeiboRequest:(WBBaseRequest *)request { } - (void)didReceiveWeiboResponse:(WBBaseResponse *)response { if ([response isKindOfClass:[WBAuthorizeResponse class]]) { //获得授权响应 WBAuthorizeResponse *autorize = (WBAuthorizeResponse *)response; self.WeiboToken = autorize.accessToken; if (self.WeiboToken.length != 0) { //获得认证口令 if (self.completion != nil) { self.completion(nil, nil); } } else { NSError *fail = [NSError errorWithDomain:ERROR_DOMAIN_SHARE_WEIBO code:NYErrorCodeShareWeibo userInfo:@{NSLocalizedDescriptionKey: @"获得授权失败!"}]; if (self.completion != nil) { self.completion(fail, nil); } } } else if ([response isKindOfClass:[WBSendMessageToWeiboResponse class]]) { } } #pragma mark - wechat delegate - (void)onReq:(BaseReq *)req { } - (void)onResp:(BaseResp *)resp { if ([resp isKindOfClass:[SendMessageToWXResp class]]) { //微信响应的方法 } }
VII)appdelegate的代理方法
#pragma mark - application delegate - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url { BOOL result = NO; if ([url.scheme isEqualToString:[NSString stringWithFormat:@"wb%@", WB_APP_KEY]]) { //微博 result = [WeiboSDK handleOpenURL:url delegate:self]; } else if ([url.scheme isEqualToString:[NSString stringWithFormat:@"%@", WX_APP_ID]]) { result = [WXApi handleOpenURL:url delegate:self]; } else if ([url.scheme isEqualToString:[NSString stringWithFormat:@"tencent%@", QQ_APP_ID]]) { result = [TencentOAuth HandleOpenURL:url]; } return result; } - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { BOOL result = NO; if ([url.scheme isEqualToString:[NSString stringWithFormat:@"wb%@", WB_APP_KEY]]) { //微博 result = [WeiboSDK handleOpenURL:url delegate:self]; } else if ([url.scheme isEqualToString:[NSString stringWithFormat:@"%@", WX_APP_ID]]) { result = [WXApi handleOpenURL:url delegate:self]; } else if ([url.scheme isEqualToString:[NSString stringWithFormat:@"tencent%@", QQ_APP_ID]]) { result = [TencentOAuth HandleOpenURL:url]; } return result; }
根据URL的scheme判断是打开的哪个应用,并返回BOOL值
4.分享对象的内容类
I)头文件
#import <Foundation/Foundation.h> #import <UIKit/UIKit.h> #import "WeiboSDK.h" #import "WXApi.h" #import <TencentOpenAPI/QQApiInterface.h> #import <TencentOpenAPI/QQApiInterfaceObject.h> #import <TencentOpenAPI/TencentOAuthObject.h> #import <TencentOpenAPI/TencentOAuth.h> #define SHARE_BASE_URL @"" /** * 分享对象类型 **/ typedef NS_ENUM(NSInteger, NYShareObjectType) { NYShareObjectTypeProject,/**< 分享HTM项目 */ NYShareObjectTypeActivity,/**< 分享HTM活动 */ NYShareObjectTypeInvestor,/**< 分享HTM投资人 */ }; /** * 分享的对象 * @description 分享类型:项目、活动、投资人; **/ @interface NYShareObject : NSObject @property (strong, nonatomic) NSString *title; /**< 分享的标题[项目名称|活动名称|投资人姓名] */ @property (strong, nonatomic) NSString *content; /**< 分享的内容[项目简介|活动地点-时间|投资人简介] */ @property (strong, nonatomic) NSString *shareImageURL; /**< 分享的图片URL */ @property (strong, nonatomic) UIImage *shareImage; /**< 分享的图片对象[项目Logo|活动Logo|投资人头像] */ @property (strong, nonatomic) NSString *shareURL; /**< 分享的网页URL */ @property (assign, nonatomic) NYShareObjectType objectType;/**< 分享内容的类型[项目、活动、投资人] */ @property (strong, nonatomic) NSNumber *shareID; /**< URL的ID */ @property (strong, nonatomic) NSString *location; /**< 活动地点 */ @property (strong, nonatomic) NSString *date; /**< 活动时间 */ - (WBMessageObject *)WeiboMessageObject;/**< 获得微博分享的对象 */ - (WXMediaMessage *)WeChatMessageObject;/**< 获得分享到微信的对象 */ - (QQApiNewsObject *)QQNewsObject; /**< 分享QQ新闻链接 */ @end
解释:
a)NYShareObjectType是自定义分享对象的类型,视具体项目而定。我负责的项目中,需要分享项目、投资人以及活动资讯等内容,因此这样定义。自己创建可作调整。
II)具体实现
#import "NYShareObject.h" @implementation NYShareObject @synthesize objectType = _objectType; - (void)setObjectType:(NYShareObjectType)objectType { switch (objectType) { case NYShareObjectTypeProject: { self.title = [NSString stringWithFormat:@"合投猫推荐项目[%@]", _title]; self.content = [NSString stringWithFormat:@"%@", _content]; self.shareURL = [NSString stringWithFormat:@"%@/share/proj?id=%@&from=appshare", SHARE_BASE_URL, _shareID]; self.shareImage = _shareImage; break; } case NYShareObjectTypeActivity: { self.title = [NSString stringWithFormat:@"合投猫活动[%@]", _title]; self.content = [NSString stringWithFormat:@"%@ %@", _location, _date]; self.shareURL = [NSString stringWithFormat:@"%@/act/act_details/?id=%@&from=appshare", SHARE_BASE_URL, _shareID]; self.shareImage = _shareImage; break; } case NYShareObjectTypeInvestor: { self.title = [NSString stringWithFormat:@"合投猫推荐投资人[%@]", _title]; self.content = [NSString stringWithFormat:@"%@", _content]; self.shareURL = [NSString stringWithFormat:@"%@/share/invester?id=%@&from=appshare", SHARE_BASE_URL, _shareID]; self.shareImage = _shareImage; break; } default: break; } } - (WBMessageObject *)WeiboMessageObject { //!!!判断为空等情况 WBImageObject *imageObj = [WBImageObject object]; imageObj.imageData = UIImageJPEGRepresentation(self.shareImage, 0); WBMessageObject *message = [WBMessageObject message]; message.text = [NSString stringWithFormat:@"%@:%@。分享链接:%@", self.title, self.content, self.shareURL]; message.imageObject = imageObj; return message; } - (WXMediaMessage *)WeChatMessageObject { WXMediaMessage *message = [WXMediaMessage message]; message.title = [NSString stringWithFormat:@"%@", self.title]; message.description = (self.content.length > 50)?[self.content substringToIndex:50]:self.content; if (self.shareImage != nil) { //!!!需要压缩 NSData *imgData = UIImageJPEGRepresentation(self.shareImage, 0); UIImage *smallImage = [UIImage imageWithData:imgData]; [message setThumbImage:smallImage]; } WXWebpageObject *ext = [WXWebpageObject object]; ext.webpageUrl = [NSString stringWithFormat:@"%@", self.shareURL]; message.mediaObject = ext; return message; } - (QQApiNewsObject *)QQNewsObject { NSURL *previewURL = [NSURL URLWithString:self.shareImageURL]; NSURL *url = [NSURL URLWithString:self.shareURL]; QQApiNewsObject *obj = [QQApiNewsObject objectWithURL:url title:self.title description:self.content previewImageURL:previewURL]; return obj; } @end
5.使用方法。
I)在appdelegate的
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions方法或者试图控制器的
- (void)viewDidLoad方法中调用注册应用方法
两者都可,但一定要调用,否则SDK方法不能被调用
[[NYShareEngine sharedEngine] registerApp];
II)创建分享对象。
如在
- (void)viewDidLoad方法中创建
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [[NYShareEngine sharedEngine] registerApp]; self.shareObject = [[NYShareObject alloc] init]; self.shareObject.title = @"我是好人"; self.shareObject.shareImage = [UIImage imageNamed:@"a.jpg"]; self.shareObject.shareImageURL = @"http://p2.wmpic.me/article/2015/03/16/1426483393_yCesCgkT.jpeg"; self.shareObject.shareID = @1; self.shareObject.content = @"自己给自己发好人卡~"; self.shareObject.location = @"上海市浦东新区商城路"; self.shareObject.date = @"2015-12-12"; self.shareObject.objectType = NYShareObjectTypeActivity; }
III)创建Button关联时间方法
- (IBAction)wechatPay:(id)sender { [[NYPaymentManager defaultManager] payForType:NYPaymentTypeWeChat paymentObject:self.paymentObject completion:^(NSError *error, id requestObject, id responseObject) { if (error != nil) { NSLog(@"Domain:%@\n Description:%@\n request:%@\n", error.domain, error.description, requestObject); } else { NSLog(@"%@", responseObject); } }]; } - (IBAction)shareToWeibo:(id)sender { [[NYShareEngine sharedEngine] shareWithShareType:NYShareTypeWeibo object:self.shareObject completion:^(NSError *error, id response) { }]; } - (IBAction)shareToQQ:(id)sender { [[NYShareEngine sharedEngine] shareWithShareType:NYShareTypeQQ object:self.shareObject completion:nil]; } - (IBAction)shareToWeChatMoments:(id)sender { [[NYShareEngine sharedEngine] shareWithShareType:NYShareTypeWeChatMomemts object:self.shareObject completion:^(NSError *error, id response) { if (error != nil) { } else { } }]; } - (IBAction)shareToWeChatSession:(id)sender { [[NYShareEngine sharedEngine] shareWithShareType:NYShareTypeWeChatSession object:self.shareObject completion:^(NSError *error, id response) { if (error != nil) { } else { } }]; }
6.具体项目地址
https://github.com/leo90821/NYLib
7.总结
1)一开始做三方平台的分享、登录、支付功能会觉得很头大,根本原因是不看开发文档,或者说不仔细看官方开发文档导致的。心态很重要。
2)若文中有错误,望批评指正,我的QQ 1034586012,欢迎交流.
3)下一篇将介绍微信支付的开发方法