SwiftUI

【SwiftUIでiOSアプリ開発】GASからデータを取得するアプリの作り方

iOSアプリ開発の初心者向けに、Google Apps Script(GAS)のデータをJSON形式で取得してiPhone上に表示させるやり方について解説します。

本記事では以下の内容が学べます。

GAS側


・GASのスクリプトエディタの使い方
・GASをWebアプリとして使用する方法

Swift側


・JSON形式をResponseオブジェクトに変換
・GASから取得した配列からデータをリスト形式で表示

本記事では中身を簡単にするため、取得するデータを一つにしていますが、GAS側でコードを付け加えれば連続したデータを取得できるような仕様にしています。

作成するiOSアプリ

GASに入力したデータを取得してiPhone上にリスト形式で表示するシンプルなアプリです。

{"results":[{"id":1,"内容":"テスト"}]}

JSON形式ではこのようなデータ形式にしたものをSwift側で取り扱っていきます。

開発環境

Xcode Version 12.5 (12E262)
Apple Watch Ver.7.4.1(18T201)
iPhone X Ver.14.5.1 

GASでコードを書く

新しいスプレッドシートを開いて以下のように入力しておきます。

次にスクリプトエディタを開きます。

以下のコードを入力します。

resultというIDと内容データを格納したリストを定義して、JSON形式で出力するマクロです。

function doGet(e) {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = ss.getSheetByName('シート1');

  var number = sheet1.getRange("A2").getValue(); 
  var contents = sheet1.getRange("B2").getValue(); 

  let result = {"results":[
    {"id":number, "value":contents}
  ]};
  
  var out = ContentService.createTextOutput();

  out.setMimeType(ContentService.MimeType.JSON);
  out.setContent(JSON.stringify(result));

  return out;

}

入力が終わったらデプロイボタンを押し、ウェブアプリとしてデプロイします。

その際、セキュリティ上の注意画面が表示された場合は写真の順序で進めていけば大丈夫です。

説明とアクセスできるユーザー(今回はSwiftからアクセスできるように全員にしました)を選択してデプロイボタンを押します。

※全員にした場合、URLをネット上に公開してしまうと誰でもアクセスできるようになってしまうので注意してください

ウェブアプリ用のURLが表示されるのでコピーして次に解説するSwiftコードに貼り付けられるようにしておきます。

GAS側の解説は以上です。

Xcodeでコードを書く

Xcodeで新規プロジェクトの作り方が分からない方はまずはこちらの記事を読んでください。

【SwiftUIでiOSアプリ開発】Xcodeの使い方とサンプルコード実行 自分で作ったアプリをiPhoneで起動できた時は感動しました。 そんな体験ができるまでの手順を紹介します。 私はApple Devel...

 

GASで使用した変数を定義する部分

SwiftUIをインポートし、ResponseとResultと呼ぶデータを格納する構造体を定義します。

GAS内で定義している、今回のアプリに必要な項目をSwift側でも定義します。

import SwiftUI

/// GASから取得する戻り値の型
struct Response: Codable {
    var results: [Result]  // GASで定義
}
 
/// 個々の情報の型
struct Result: Codable {
    var id: Int             // GASで定義
    var value: String?     //GASで定義
}

JSON形式をResponseオブジェクトに変換する部分

func loadData()でJSON形式をSwiftで扱えるように変換を行います。

●●●の部分にGASで取得したURLを貼り付けます。

    func loadData() {
        /// URLの生成
        let urlString = "●●●".addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
        guard let url = URL(string: urlString!) else {
            /// 文字列が有効なURLでない場合の処理
            return
        }
        /// URLリクエストの生成
        let request = URLRequest(url: url)
        /// URLにアクセス
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {    ///データ取得チェック
                ///JSON形式をResponseオブジェクトに変換
                let decoder = JSONDecoder()
                guard let decodedResponse = try? decoder.decode(Response.self, from: data) else {
                    print("Json decode エラー")
                    return
                }
                ///情報をUIに適用
                DispatchQueue.main.async {
                    results = decodedResponse.results
                }
            } else {
                /// データが取得できなかった場合の処理
                print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
            }
        }.resume()
    }

ContetViewの部分

VStackを使用してNavigationViewとGASから取得した全てのidのデータ(今回は一つ)を並べたリストを縦方向に整列させて表示させます。

struct ContentView: View {
    @State private var item = ""
    @State private var results = [Result]()   /// 空の配列を生成
    
    var body: some View {
        VStack{
            NavigationView {
                VStack{
                    List(results, id: \.id) { item in
                        VStack(alignment: .leading) {
                            Text(item.value ?? "")
                        }
                    }.navigationTitle("内容")
                }
            }.onAppear(perform: loadData)           /// データ読み込み処理
        }
    }
}

Swift側のコード紹介は以上です。

コード.gs

function doGet(e) {
  
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet1 = ss.getSheetByName('シート1');

  var number = sheet1.getRange("A2").getValue(); 
  var contents = sheet1.getRange("B2").getValue(); 

  let result = {"results":[
    {"id":number, "value":contents}
  ]};
  
  var out = ContentService.createTextOutput();

  out.setMimeType(ContentService.MimeType.JSON);
  out.setContent(JSON.stringify(result));

  return out;

}

ContentView.swift

//
//  ContentView.swift
//
//
//  Created on 2021/05/31.
//

import SwiftUI

/// GASから取得する戻り値の型
struct Response: Codable {
    var results: [Result]
}
 
/// 個々の情報の型
struct Result: Codable {
    var id: Int             // GASで定義
    var value: String?     //GASで定義
}

struct ContentView: View {
    @State private var item = ""
    @State private var results = [Result]()   /// 空の配列を生成
    
    var body: some View {
        VStack{
            NavigationView {
                VStack{
                    List(results, id: \.id) { item in
                        VStack(alignment: .leading) {
                            Text(item.value ?? "")
                        }
                    }.navigationTitle("内容")
                }
            }.onAppear(perform: loadData)           /// データ読み込み処理
        }
    }
    /// データ読み込み処理
    func loadData() {
        /// URLの生成
        let urlString = "●●●".addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)
        guard let url = URL(string: urlString!) else {
            /// 文字列が有効なURLでない場合の処理
            return
        }
        /// URLリクエストの生成
        let request = URLRequest(url: url)
        /// URLにアクセス
        URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {    ///データ取得チェック
                ///JSON形式をResponseオブジェクトに変換
                let decoder = JSONDecoder()
                guard let decodedResponse = try? decoder.decode(Response.self, from: data) else {
                    print("Json decode エラー")
                    return
                }
                ///情報をUIに適用
                DispatchQueue.main.async {
                    results = decodedResponse.results
                }
            } else {
                /// データが取得できなかった場合の処理
                print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
            }
        }.resume()
    }
}

参考サイト

https://capibara1969.com/2946/

ABOUT ME
Mickey@コーヒー好きエンジニア
【製造業×プログラミング×AI】Python/VBAを活用した業務改善、Streamlit/Plotlyを活用したWebアプリ開発について初心者向けに発信中|趣味は自家焙煎コーヒー作り|noteでは焙煎理論を発信|ココナラではプログラミングに関する相談,就職/転職やコーヒーに関する相談などのサービスをやっています
【製造×プログラミング×AI】
Mickey@コーヒー好きエンジニア
【製造業×プログラミング×AI】ロボット×画像処理×AI×3現主義が得意な生産技術者|Python/VBAを活用した業務改善、Streamlit/Plotly/PySimpleGUIなどを活用したアプリ開発について初心者向けに発信中|趣味は自家焙煎コーヒー作り|noteでは焙煎理論を発信|ココナラではPython/iOS/VBA開発の支援,就職/転職相談などのサービスもやっています↓ Pythonを使ったWebアプリ開発を支援します 成果物が明確なのでPythonを学びたい人にオススメです
\ Follow me /