Amarronの日記

iOSやMac、Web系の記事を書きます。

iOS SNS シェア 実装方法

概要

SNS等のシェア機能の実装方法の説明です。 実装内容はシェアボタン押下時にアクションシートを表示し、シェアしたい内容を選びシェアするような実装内容です。
Webページを表示しシェアしていますが、それ以外でも同じような実装方法でシェア可能です。

シェア先は次の6個!(mixiは登記簿謄本が必要な為今回は対象外としました)

実装結果イメージ(「safari」と「URLをコピー」はおまけ)

f:id:Amarron:20140828232150p:plain

この記事では、「UIApplication sharedApplication」で実装しています。
iOS6移行はUIActivityViewControllerで実装するのが楽だと思うので、iOS6移行は他の記事を参考にする方がお勧めです!

前提条件

TWITTERFACEBOOKiOS6以上)

Xcodeのライブラリーに「Social.framework」を追加する必要があります。
(TARGETS->MyApp->Build Phases->Link Binary With Libraries)

Google+EverNote

Google+EverNoteは、SDKやサービスの登録が必要となります。
EverNoteはサービス登録が必要になるので、少し時間と手間がかかります)

簡単な概要は次の通りです。

Google+

名称 Google+ Platform
URL https://developers.google.com/+/
必要なライブラリ Security.framework,SystemConfiguration.framework
手順 1. APIs Console プロジェクトを作成する
2. SDK をダウンロードして、サンプル アプリを実行する
3. SDK のダウンロードとインストール
4. プログラム修正
参考URL スタートガイド(iOS)
Googleでログイン
インタラクティブな投稿の作成
備考 開発用と製品用でIDが異なる。使えないhtmlタグがある。

EverNote

名称 EverNote Developers
URL https://dev.evernote.com/intl/jp/
必要なライブラリ Security.framework
手順 1.サンドボックスのアカウント登録(アプリごとで共通のアカウントを利用する)
2. Evernote API キー取得(アプリごと別々に取得)
3. SDK のダウンロードとインストール
4.プログラム修正・サンドボックス上のEverNoteでテスト
5.サンドボックスからプロダクションに変更依頼(アクティベーション
6.プログラム修正(プロダクションに変更(ホストを「BootstrapServerBaseURLStringSandbox」→「BootstrapServerBaseURLStringUS」))
参考URL スタートガイド
サンドボックス
アクティベーション(プロダクションAPIキーの有効化)
よくある質問
備考 開発用と製品用でIDが異なる。使えないhtmlタグがある。
アクティベーションの申請時にはgoogleアカウントは使用できないので個人名・個人用メールアドレスで依頼
アクティベーションの承認待ち時間は約2〜3日

プログラム

流れ

  1. アクションシートの生成
  2. アクションシートの表示(シェアボタン押下時)
  3. アクションシートの選択した内容でシェア

サンプルコード

#import <Social/Social.h>
#import "EvernoteSession.h"
#import "EvernoteUserStore.h"
#import "EvernoteNoteStore.h"
#import "GTLPlusConstants.h"
#import "GPPShare.h"

@implementation MainController
    UIActionSheet *_actionsheet;
    UIWebView *_webView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // 1. アクションシートの生成
    _actionsheet = [[UIActionSheet alloc] initWithTitle:@"共有"
                                               delegate:self
                                      cancelButtonTitle:@"キャンセル"
                                 destructiveButtonTitle:nil
                                      otherButtonTitles:@"LINE", @"Twitter", @"Facebook", @"Google+", @"メール", @"Safari", @"Evernote", @"URLをコピー", nil];
    [_webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://google.co.jp"]]];

}

#pragma mark - IBAction
// 2. アクションシートの表示(シェアボタン押下時)
- (IBAction)shareAction:(id)sender
{
    if([[UIDevice currentDevice].model isEqualToString:@"iPad"]){
        [_actionsheet showFromBarButtonItem:sender animated:YES];
    } else {
        [_actionsheet showInView:self.view];
    }
}

#pragma mark UIActionSheetDelegate
// 3. アクションシートの選択した内容でシェア
-(void)actionSheet:(UIActionSheet*)actionSheet
clickedButtonAtIndex:(NSInteger)buttonIndex {
    NSURL *url = [NSURL URLWithString:[_webView stringByEvaluatingJavaScriptFromString:@"document.URL"]];
    NSString *title = [_webView stringByEvaluatingJavaScriptFromString:@"document.title"]; 

    if (0 == buttonIndex) { // LINE
        NSString *contentURL = [self _escapeURL:[url absoluteString]];
        NSString *contentTitleText = [self _escapeURL:title];
        NSURL *lineURL = [NSURL URLWithString:[NSString stringWithFormat: @"http://line.naver.jp/R/msg/text/%@?%@", contentURL, contentTitleText]];
        [[UIApplication sharedApplication] openURL:lineURL];
           
        
    } else if (1 == buttonIndex) { // TWITTER
        SLComposeViewController *postVC = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
        [postVC setInitialText:title];
        [postVC addURL:url];
        [self presentViewController:postVC animated:YES completion:nil];
        
    } else if (2 == buttonIndex) { // FACEBOOK
        SLComposeViewController *postVC = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];
        [postVC setInitialText:title];
        [postVC addURL:url];
        [self presentViewController:postVC animated:YES completion:nil];

    } else if (3 == buttonIndex) { // Google+
        id<GPPShareBuilder> shareBuilder = [[GPPShare sharedInstance] shareDialog];
        [shareBuilder setPrefillText:title];
        [shareBuilder setURLToShare:url];
        [shareBuilder open];
        
    } else if (4 == buttonIndex) { // メール
        NSString *conSbjText = [self _escapeURL:title];
        NSString *conBodyText = [self _escapeURL:[url absoluteString]];
        NSString *mail = [NSString stringWithFormat:@"mailto:%@?Subject=%@&body=%@", @"", conSbjText, conBodyText];
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:mail]];
        
    } else if (5 == buttonIndex) { // Safari
        [[UIApplication sharedApplication] openURL:url];
        
    } else if (6 == buttonIndex) { // EverNote
        EvernoteSession *session = [EvernoteSession sharedSession];
        [session authenticateWithViewController:self completionHandler:^(NSError *error) {
            if (!error && session.isAuthenticated){
                // We're authenticated!
                EvernoteUserStore *userStore = [EvernoteUserStore userStore];
                [userStore getUserWithSuccess:^(EDAMUser *user) {
                    // success
                    NSLog(@"Authenticated as %@", [user username]);
                    
                    EDAMNote *note = [[EDAMNote alloc] init];
                    
                    note.title = title;
                    
                    NSString *conDes = [_webView stringByEvaluatingJavaScriptFromString:
                                          @"function a(){l=document.getElementsByTagName('meta');for(i=0;i<=l.length;i++){if(l[i].name=='description') return l[i].content}};a();"];
                    NSString *conImg = [_webView stringByEvaluatingJavaScriptFromString:
                                        @"function a(){l=document.getElementsByTagName('meta');for(i=0;i<=l.length;i++){if(l[i].getAttribute('property')=='og:image') return l[i].content}};a();"];
                    
                    NSString *enNote = [NSString stringWithFormat:@"<p>%@</p><a href='%@'></a>%@<img src='%@' />",
                                        conDes,
                                        [self _escapeXML:[url absoluteString]],
                                        [self _escapeXML:[url absoluteString]],
                                        conImg];
                    note.content = [NSString stringWithFormat:@"<?xml version=\"1.0\" encoding=\"UTF-8\"?><!DOCTYPE en-note SYSTEM \"http://xml.evernote.com/pub/enml2.dtd\"><en-note>%@</en-note>", enNote];
                    
                    EvernoteNoteStore *store = [[EvernoteNoteStore alloc] initWithSession:[EvernoteSession sharedSession]];
                    [store createNote:note success:^(EDAMNote *note) {
                        [[[UIAlertView alloc] initWithTitle:@"Evernoteに保存しました。" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
                    } failure:^(NSError *error) {
                        [[[UIAlertView alloc] initWithTitle:@"Evernoteの保存に失敗しました。" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
                    }];
                } failure:^(NSError *error) {
                    [[[UIAlertView alloc] initWithTitle:@"Evernoteの保存に失敗しました。" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];

                } ];
            }
        }];
    } else if (7 == buttonIndex) { // コピー
        [[UIPasteboard generalPasteboard] setValue:[url absoluteString] forPasteboardType:@"public.text"];
        [[[UIAlertView alloc] initWithTitle:@"URLをコピーしました。" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show];
    }
}

#pragma mark - private methods
- (NSString *)_escapeURL:(NSString *) url
{
    return (__bridge NSString *)CFURLCreateStringByAddingPercentEscapes(NULL,
                                                                         (__bridge CFStringRef)url,
                                                                         NULL,
                                                                         (CFStringRef)@"!*'();:@&=+$,/?%#[]",
                                                                         kCFStringEncodingUTF8 );

}
- (NSString *)_escapeXML:(NSString *) xml
{
    xml = [xml stringByReplacingOccurrencesOfString:@"&" withString:@"&amp;"];
    xml = [xml stringByReplacingOccurrencesOfString:@"\""withString:@"&quot;"];
    xml = [xml stringByReplacingOccurrencesOfString:@"'"withString:@"&apos;"];
    xml = [xml stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];
    xml = [xml stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
    return xml;
}

エンコードについて

Line、メール、EverNoteは、直接URLをシェア出来ません。
シェアするためには、htmlのエスケープが必要になります。
urlのエスケープ(line,mail)→16進表記(文字参照
xmlのエスケープ(EverNote)→実態参照

文字 実体参照 10進表記(文字参照 16進表記(文字参照
"&" &amp; &#38; &#x26;
""" &quot; &#34; &#x22;
"<" &lt; &#60; &#x3C;
">" &gt; &#62; &#x3E;
" " &nbsp; &#160; &#xA0;