アプリからサインイン無しでGoogleスプレットシートのデータを取得する方法

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

以前、Googleサインインを組み込む方法をブログで書いたのですが、 今回はサインイン無しでアプリからGoogleスプレットシートのデータを取得する方法について記載します。

サインイン無しで利用するにはGoogle Apps Scriptを利用します。
概要イメージ図
※ イメージ図


注意事項
但し、今回の方法は公開権限を制限することが出来ません。後述で出てくるGoogle Apps ScriptのURLが流出すると、おそらく誰でもデータを取得/編集することが出来てしまいます。
セキュリティのリスクを把握したうえで、正しくご利用ください。

環境

今回実施している環境は下記の通りです。

XCode 8.0
Swift 3.0
CocoaPods 1.0.0
iOS SDK 9.0 以上

※ 今回はiOSで試していますが、アプリ側の実装はAndroidでもXamarinでも何でもOKです。

準備

スプレットシートを用意する

データを読み書きする対象のスプレットシートを作成します。
今回は、サンプルとして会社に置いてあるボードゲームのリストを書いてみました。

スプレットシートサンプル

ここでメモしておく必要があるのが、シートIDです。 シートIDはURLの

URLサンプル

https://docs.google.com/spreadsheets/d/{ここの部分}/edit#gid=0

になります。

また、対象のシート名もスクリプトで使用するのでメモしておきましょう。

シート名をメモしておく

Google Apps Scriptの記述

スプレットシートのメニューの ツール > スクリプトエディタ をクリックします。
すると、スクリプトエディタが新規でひらきます。

ツール>スクリプトエディタ

タイトル部分をクリックするとプロジェクト名編集ダイアログが表示されるので、適宜名前を付けます。

また、コード.gsとなっているコードの名前も変更しましょう。

コードの名称変更

リストを取得するスクリプト

以下のスクリプトは対象のシートの1行目キーにしたJSONを返すスクリプトです。
"{シートID}"には準備の時に作成したスプレットシートのシートIDを、 "{シート名}"の部分にはスプレットシートのシート名を入力します。

function getList(id, sheetName) {
  var sheet = SpreadsheetApp.openById(id).getSheetByName(sheetName);
  var rows = sheet.getDataRange().getValues();
  var keys = rows.splice(0, 1)[0];
  return rows.map(function(row) {
    var obj = {};
    row.map(function(item, index) {
      if (item != "") {
        obj[keys[index]] = item;
      }
    });
    return obj;
  });
}

function doGet() {
  var data = getList("{シートID}", "{シート名}");
  return ContentService.createTextOutput(JSON.stringify(data, null, 3)).setMimeType(ContentService.MimeType.JSON)
}

スクリプトの呼び出しについて

今回はこのスクリプトをWebアプリケーションとして公開します。
その際、GETで呼び出した時はdoGet()、POSTで呼び出した時はdoPost()が呼び出されます。 とりあえず、この doGet() or doPost() からスタートされると思っていて良いと思います。
[参考] Content Service | Google Apps Script

スクリプトの簡単な説明

SpreadsheetApp.openById(id)

指定したIDのスプレットシートを開きます。同じくURLからスプレットシートを開く、 openByUrl(url)もありますが、URLの中にIDが含まれているのでopenByIdの方が短く書けて良いと思います。
[参考] Class SpreadsheetApp | Google Apps Script

getSheetByName(name)

openByIdで開いたスプレットシートからシートを名前で取得します。
[参考] Class Spreadsheet | Google Apps Script

getDataRange()

getSheetByNameで取得したシートからデータが入力されている範囲を取得します。
[参考] Class Sheet | Google Apps Script

JSON.stringify()

JSON 文字列に変換します。
[参考] JSON.stringifyを改めて調べる。| Qiita

ContentService.createTextOutput()

テキストコンテンツを返します。
.setMimeType(ContentService.MimeType.JSON) をつけることでJSON形式として扱います。
[参考] Class ContentService| Google Apps Script

スクリプトの実行

スクリプトが書けたら実行します。メニューバーのボタンをクリックします。

実行してみる

すると承認が必要でダイアログが表示されるので、許可を確認ボタンをクリックします。

許可を確認

別ウィンドウでスプレットシートの許可リクエストが表示されるので、スプレットシートに対しての許可をします。

許可のリクエスト

ウェブアプリケーションとして導入

アプリでGoogle Apps Scriptを利用するにはウェブアプリケーションとして導入し、URLを発行?する必要があります。
公開 > ウェブアプリケーションとして導入 をクリックします。

スクリーンショット_2016-11-28_14_15_56

次のダイアログが表示されます。
アプリケーションにアクセス出来るユーザーを全員(匿名ユーザーを含む)にして、導入ボタンをクリックします。

ScriptBoardGameList

すると、URLが表示されるので、ここに表示されているURLをコピーします。
また、最新のコード部分をクリックすると、このスクリプトの挙動を確認することが出来るので、どんな値が返ってくるのか予め確認すると良いと思います。

ScriptBoardGameList 3

このURLが発行されたらGoogle Apps Script側の作業は完了です。

アプリ側の実装

次にアプリ側の実装に入ります。今回はサンプルプロジェクトとして、TableViewで一覧が表示される簡単なものです。

プロジェクトの作成

XCodeでプロジェクトを新規に作成します。
今回はBoardGameListViewerというプロジェクトを作成しました。

そして、CocoaPodsで各種ライブラリをインストールします。
今回はAlamofireとJSONのパース用にArgoというライブラリを使ってますが、お好みにあわせて別のライブラリでも問題ありません。

use_frameworks!
target 'BoardGameListViewer'
pod 'Alamofire'
pod 'Argo'
pod 'Curry'

post_install do |installer|
    installer.pods_project.targets.each do |target|
        target.build_configurations.each do |configuration|
            configuration.build_settings['SWIFT_VERSION'] = "3.0"
        end
    end
end

サンプルコード

取得するスプレットシートにあったデータを定義します。今回はゲーム一覧なので表示する各項目を定義しています。

import Argo
import Curry
import Runes

struct GameItem {
    let name: String
    let tag1: String?
    let tag2: String?
    let tag3: String?
    let playerMin: Int?
    let playerMax: Int?
    let timeMin: Int?
    let timeMax: Int?
}

extension GameItem: Decodable {
    public static func decode(_ json: JSON) -> Decoded<GameItem> {

        return curry(GameItem.init)
            <^> (json <|? "name").map { $0 ?? "***" }
            <*> (json <|? "tag1")
            <*> (json <|? "tag2")
            <*> (json <|? "tag3")
            <*> (json <|? "player_min")
            <*> (json <|? "player_max")
            <*> (json <|? "time_min")
            <*> (json <|? "time_max")
    }
}

今回はサンプルなので簡単に、TableViewController側の処理です。 "https://script.google.com/macros/s/XXXXXXXXXX/exec"の部分にGoogle Apps Scriptで発行されたURLを入力します。

import UIKit
import Alamofire
import Argo

class GameListTableViewController: UITableViewController {

    private var list: [GameItem] = []

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        loadList()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    // MARK: - Private

    private func loadList() {
        Alamofire.request("https://script.google.com/macros/s/XXXXXXXXXX/exec").responseJSON { response in
            if let json = response.result.value {
                guard let list: [JSON] = decode(json) else {
                    return
                }
                self.list = []
                for item in list {
                    if let gameItem = GameItem.decode(item).value {
                        self.list.append(gameItem)
                    }
                }
                self.tableView.reloadData()
            }
        }
    }

    // TableViewの実装部分は省略
}

実行結果

Googleスプレットシートの内容がアプリに表示されました。

結果

さいごに

個人で利用したり、開発中のAPIモックとして利用したりするのにちょうどいいと思いました。
Google Apps Scriptを使えばスプレットシート以外にも色々利用出来ます。

Apps_Script_ _ _Google_Developers

詳細は公式のhttps://developers.google.com/apps-script/をご覧ください。

参考

スプレッドシートとgoogle apps scriptで簡易APIを作ってalamofireからget/postする|Qiita