20211013のSwiftに関する記事は4件です。

iOSアプリからスプレッドシードのデータを読み込む

iOSアプリからスプレッドシートのデータを読み込みたい時が人生にはあります。その方法です。 大まかな流れ iOSからスプレッドシートのデータを読むためには、スプレッドシート側にGAS(Google Apps Script)を実装する必要があります。GASでスプレッドシートをReadするGETを実装し、iOSからそこ目掛けて通信する実装をすれば完成です。 GASの実装 スプレッドシートの「シート1」シートに下記のようなデータがあるものとします。 スプレッドシートメニューのツール>スクリプトエディタを選択します。 するとApps Scriptエディタが開きます。 ここに処理を実装していきます。 Readする スプレッドシートのデータを読み込む方法は以下の通りです。 function myFunction(){ var targetFileID = DriveApp.getFileById("xxxxx") var targetFile = SpreadsheetApp.open(targetFileID) var targetSheet = targetFile.getSheetByName("シート1") var values = targetSheet.getRange(3, 1, 1, 4).getValues() console.log(values) } //出力結果 22:51:30 情報 [ [ '花子', 22, 15, 5 ] ] DriveApp.getFileById("xxxxx")でGoogle Drive内にある対象ファイルのIDを取得します。※1 SpreadsheetApp.open(targetFileID)で対象ファイルを取得します。 targetFile.getSheetByName("シート1")で対象ファイル内の対象シートを取得します。 targetSheet.getRange(3, 1, 1, 4).getValues()で対象シート内の3行目1列目のセルを起点に、1行分4列目まで取得します。 デバッグするにはメニューバーにある▷実行を押します。 スプレッドシートの内容にアクセスする場合、初回はアクセス権限に関する表示が出るので対処します。 ※1:ファイルIDはURLの https://docs.google.com/spreadsheets/d/xxxxx/edit#gid=0 の「xxxxx」にあたる部分です GETを実装する GASでWebAPIを実装できます。GASの中でGETならdoGet(e)を定義し実装します。これをウェブアプリとして公開するとWebAPIが発行され外部からアクセスできるようになります。 doGet(e) スプレッドシートの内容をWebAPI経由で取得する方法は以下の通りです。 function doGet(e){ var targetFileID = DriveApp.getFileById("xxxxx") var targetFile = SpreadsheetApp.open(targetFileID) var targetSheet = targetFile.getSheetByName("シート1") var values = targetSheet.getRange(3, 1, 1, 4).getValues() return ContentService.createTextOutput(JSON.stringify(values)).setMimeType(ContentService.MimeType.JSON) } myFunction()をdoGet(e)に書き換えます。 データの取得まではReadすると同じです。 returnに ContentService.createTextOutput(JSON.stringify(values)).setMimeType(ContentService.MimeType.JSON) を書きます。スプレッドシートの情報をJSON形式で返します。 デプロイを行い、WebAPIを発行します。 こちらの手順を参考にして下さい。 すると、このような形でWebAPIが取得できます。 試しにブラウザにそのURLを入力するとdoGet(e)内で取得された情報が返ってくるのが確認できます。 iOSの実装 ここではiOSからGASのWebAPIへのアクセスを容易にするためにAlamofireとSwiftyJSONを用います。適宜インストールして下さい。 import UIKit import Alamofire import SwiftyJSON class ViewController: UIViewController { @IBOutlet weak var name: UILabel! @IBOutlet weak var count: UILabel! override func viewDidLoad() { super.viewDidLoad() let url = "https://script.google.com/macros/s/xxx/exec" //上記で取得したWebAPI AF.request(url, method: .get).responseJSON{ (response) in switch response.result { case .success(let elm): let json = JSON(elm as Any)// [["花子", 22, 15, 5]] self.name.text = json[0][0].stringValue self.count.text = json[0][2].description case .failure(_): print("failure") } } } } AF.request(_, method: .get)で上で取得したWebAPIへGETで通信を行います response.resultにレスポンスが入るので、Switch文で分岐処理します レスポンスをJSONオブジェクトに変換します JSONオブジェクトインスタンスの各要素にアクセスし、型に応じて変換処理をかけます これをシュミレータで実行するとこうなります。 まとめ iOSアプリからスプレッドシートのデータを読み込む方法をまとめました。 この様な流れでiOSからスプレッドシートのデータを読み込むことができます。 パラメータを付けて何行目のデータを取得する等、動的にすることも可能です。 スプレッドシートにデータを書き込む方法もまとめましたので、よろしければご覧ください。 おまけ doGet()を実装する際、デバッグする度iOS側からアクセスするのは面倒です。 下記のリンクに簡易デバッグの方法を記載しましたのでご参照ください。 GoogleAppsScriptでdoGet()をdebugする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TableViewCellを並び替える機能を使ってみる

今回の内容 コードと簡単解説 func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {}でcellを並び替える事を可能にするかをtrueまたはfalseで設定します。 func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {}で、cellを並び替えるときに働かせる処理を設定します。 func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {}でcellの左側に表示する内容を設定します。今回は、.noneで何も表示しないようにしています。 editingStyleForRowAtを.noneで設定した事によって残ってしまった空白を削除する為に、func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {}をfalseで設定します。 import UIKit class ViewController: UIViewController { let tableView = UITableView() var cellContentsArray = ["UIKit","SwiftUI","MapKit","ARKit","CoreML","WebKit","Foundation","LocalAuthentication","CoreGraphics","Combine"] override func viewDidLoad() { super.viewDidLoad() tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell") tableView.frame = CGRect(x: view.frame.minX, y: view.frame.minY, width: view.frame.width, height: view.frame.height) tableView.isEditing = true view.addSubview(tableView) tableView.dataSource = self tableView.delegate = self } } extension ViewController:UITableViewDataSource{ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cellContentsArray.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) cell.textLabel?.text = cellContentsArray[indexPath.row] return cell } func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool { return true } func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) { let cellContent = cellContentsArray[sourceIndexPath.row] cellContentsArray.remove(at: sourceIndexPath.row) cellContentsArray.insert(cellContent, at: destinationIndexPath.row) } } extension ViewController:UITableViewDelegate{ func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle { return .none } func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool { return false } } 終わり ご指摘、ご質問などありましたら、コメントまでお願い致します。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】GCDについて

GCDとは GCD(Dispatch)はGrand Central Dispatch の略で、DipatchQueueに処理を投入することで、コードを同時実行することができます。 DispatchQueueとは GCDのキューはディスパッチキューといいます。キューに行いたい処理をクロージャで渡し実行します。ディスパッチキューの実行方式には以下があります。 直列→他のスレッド終了を待機し、完了後タスク処理を実行します。 並列→他のスレッド処理を待機せず、タスク処理を同時実行します。 キューの取得 GCDには既存のディスパッチキューとして以下の2つがあります。 mainQueue : mainQueueはメインスレッドでタスクを実行する直列キューです。 iOSやmacOSのアプリケーションにおいては、UIの更新処理は常にmainQueueで行われます。なので、他のQueueで実行した処理の結果をUIに反映させるにはmainQueueを呼び出してタスクを渡す必要があります。 globalQueue: システム全体で共有される並列キュー。実行優先度(Qos)を指定して取得。Qosは優先度の高い順に次の5種類があります。 使用例 DispatchQueue.main.sync ⇨ 直列キューなので処理は順番に実行される。またsyncなので、タスク終了まで次の処理に進みません。 DispatchQueue.main.async { for i in 0 ... 5 { print("DispatchQueue.main.async ",i) } } print("finish") --------- DispatchQueue.main.async 0 DispatchQueue.main.async 1 DispatchQueue.main.async 2 DispatchQueue.main.async 3 finish DispatchQueue.main.async ⇨直列キューなので処理は順番に実行される。asyncなので、タスクをキューに追加したら次の処理に進みます。 DispatchQueue.main.async { for i in 0 ... 5 { print("DispatchQueue.main.async ",i) } } print("finish") --------- finish DispatchQueue.main.async 0 DispatchQueue.main.async 1 DispatchQueue.main.async 2 DispatchQueue.main.async 3 DispatchQueue.global.sync ⇨ 並列に実行するので、実行順は保証されません。またsyncなので、タスク終了まで次の処理に進みません。 DispatchQueue.global().sync { for i in 0 ... 3 { print("DispatchQueue.global.async1 ",i) } } print("finish1") DispatchQueue.global().sync { for i in 0 ... 3 { print("DispatchQueue.global.async2 ",i) } } print("finish2") ---- DispatchQueue.global.async1 0 DispatchQueue.global.async1 1 DispatchQueue.global.async1 2 DispatchQueue.global.async1 3 finish1 DispatchQueue.global.async2 0 DispatchQueue.global.async2 1 DispatchQueue.global.async2 2 DispatchQueue.global.async2 3 finish2 DispatchQueue.global.async ⇨並列に実行するので、実行順は保証されません。asyncなので、タスクをキューに追加したら次の処理に進みます。 DispatchQueue.global().async { for i in 0 ... 3 { print("DispatchQueue.global.async1 ",i) } } print("finish1") DispatchQueue.global().async { for i in 0 ... 3 { print("DispatchQueue.global.async2 ",i) } } print("finish2") ---- finish1 finish2 DispatchQueue.global.async1 0 DispatchQueue.global.async2 0 DispatchQueue.global.async1 1 DispatchQueue.global.async2 1 DispatchQueue.global.async1 2 DispatchQueue.global.async2 2 DispatchQueue.global.async1 3 DispatchQueue.global.async2 3 キューの生成 GCDには既存のディスパッチキューだけでなく、新規のディスパッチを生成することができます。このキューは並列、直列どちらでも作成することができます。 終わりに 終わりです。随時更新します
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

非同期処理をasync/awaitを利用して書くことのメリット

WWDC21で発表され、もはやどこでも発表されつくされた感のあるasync / awit。 遅ればせながら私も async/awaitをやっときちんと調べましたので、記事の形にしてみようと思います。 1. 非同期処理をclosure(もしくはdelegate)を利用して書く場合の注意点 例えば、下記のようなコードを書きたいとします。よくある非同期処理ですね。 func someLongLongTimeTask(completion: @escaping ((Data) -> Void), errorHandler: @escaping ((Error) -> Void)) { precheck() guard isReady else { errorHandler(LongLongFuncError.setupFailure) return } someLongLongLong(completion: { data in guard self.validate(of: data) else { errorHandler(LongLongFuncError.dataValidationFailure) return } guard self.updated else { errorHandler(LongLongFuncError.selfUpdateFailure) return } completion(data) }) } 上記のコードでは、処理の完了やエラーの発生を引数として渡しているclosureで管理しようとしています。 ですが、closureは引数として渡されているため、Swiftのコンパイラ側で発火を保証することができません。 例えばこのコードは下記のように書き換えたとしても正常に動作します。 func someLongLongTimeTask(completion: @escaping ((Data) -> Void), errorHandler: @escaping ((Error) -> Void)) { precheck() guard isReady else { return } someLongLongLong(completion: { data in guard self.validate(of: data) else { return } guard self.updated else { return } completion(data) }) } 上記コードはerrorHandlerの呼び出しをサボっただけのコードですが、この場合呼び出し元はエラーの発生を検知できません。その結果プログレスバーが表示されたままになるなどのバグが生まれる可能性があります。 またエラーの発生が極めて限定的な状況下で発生する場合、動作テストや社内テストで検知することができず、お客様トラブルへと発展する恐れがあります。 上記のテストコードはclosureを利用したコードでしたが、これをdelegateを利用して読み替えたとしても同様にdelegateの発火を強制することはできませんし、コンパイラに確認させることもできません。 このように、closure(もしくはdelegate)を利用した非同期処理はその性質上注意深く実装し、またテストを十二分に行わないと容易にバグを生む可能性がある コードになります この問題を解決するのが async/awaitです。 2. 上記のコードをasync/awaitを利用して書き換えてみる 上記のコードをasync/awaitを利用して書き換えてみると下記のようになります。 func someLongLongTimeTask() async throws -> Data { precheck() guard isReady else { throw LongLongFuncError.setupFailure } let data = try await someLongLongLong() guard validate(of: data) else { throw LongLongFuncError.dataValidationFailure} guard updated else { throw LongLongFuncError.selfUpdateFailure} return data } 上記のようにasync/awiatを利用してコードを書き換えると下記のようなメリットが生まれます - closureを利用することによるコードのネスト構造はなくなり、直線的なコードになりました。 - また、guard節を利用したvalidationも明快になり、同期関数と似たような見た目になりました - そしてさらに、Swiftのコンパイラがエラーのthrowかデータの返却を確認するようになりました。 例えば、下記のようなコードはコンパイルが通りません。これはつまり、SwiftコンパイラによってErrorのThrowを保証できているということになります。 func someLongLongTimeTask() async throws -> Data { precheck() guard isReady else { return } let data = try await someLongLongLong() guard validate(of: data) else { return } guard updated else { return } return data } このように、非同期処理にasync/awaitを利用することで、closureやdelegateを利用した非同期処理より、よりかんたんに、そしてより安全にコードを記述することができます。 3. async/awaitの注意点 async/awaitはこのように安全にコードを書くことができますが、いつ処理が発火されるかわからないため、状態管理やスレッドには十分に注意を払う必要がある という点には注意が必要です。 async/awaitを利用すると、それぞれの処理がいつ行われるかはOSによって決定されるため、実行されるThreadは多くの場合呼び出しスレッドとは違うでしょうし、もしかしたら更新中の配列に同時アクセスしてデータ競合や内部状態の破損をもたらしてしまうかもしれません。 このような問題の多くはasync/awaitと同時に発表されたactorという概念を利用することで解決できます (後日こちらについても記事を書く予定です) 4. おわりに このようにasync/awaitを利用して非同期処理を記述すると、closure /delegateを利用して書く場合に比べてよりかんたんに、そして安全にコードを書くことができます。 データの非同期処理をかんたんにするためにSingleを利用していたプロジェクトなどは、async/awaitに切り替えることで外部ライブラリの仕様を減らすことができ、良いのではないでしょうか。 (私が参画しているプロジェクトでも、十分に検討した上で随時async/awaitへの移行を進めようと考えています)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む