- 投稿日:2020-01-07T20:24:15+09:00
【Swift】【Xcode】詳しいTableViewの使い方。Part.2 コード編
●まえがき
この記事はPart.1からの続きです。
まだみてない方は、Part.1へどうぞ!
【Swift】【Xcode】詳しいTableViewの使い方。Part.1 準備編●解説
目次
Part.1
1.プロジェクトの立ち上げ
2.Main.Storyboardの設定
3.各種接続Part.2
4.TableViewの設定
5.多次元配列について
6.残りのコードについて
7.起動してみよう
8.チャレンジStep.4 TableViewの設定
➀tableViewとクリアボタンを接続し,変数の宣言とメソッドの追加をします
ViewController.swiftimport UIKit //2次元配列 セルの内容が入る var cellList:[[String]] = [] //セクションの名前が入る var sectionTitles:[String] = [] //セクションの数 var sectionNum = 0 //セクションの高さ var sectionHight:Int = 50 //セルの数 var cellNum:Int = 0 //セルの高さ var cellHight:Int = 100 //セクション番号 var indexPath_section:Int = 0 //セル番号 var indexPath_row:Int = 0 class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{ @IBOutlet weak var TableView: UITableView! @IBAction func Clear(_ sender: Any) { }ClassにUITableViewDelegate, UITableViewDataSourceを追加してください。
➁次にセクション内のセル数を決めるメソッドと、表示するcellの中身を決めるメソッドを追加します
ViewController.swift/* セクション内のセル数を決めるメソッド(**必須**) */ func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return cellList[Int(section)].count } /* セルのインスタンスを生成するメソッド「表示するcellの中身を決める」(**必須**) */ func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let TableCell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "TableCell", for: indexPath) TableCell.textLabel!.text = cellList[indexPath.section][Int(indexPath.item)] return TableCell }➂セクション数を決めるメソッドとセクションの高さを決めるメソッド、セルの高さを決めるメソッドを追加
ViewController.swift/* テーブル内のセクション数を決めるメソッド */ func numberOfSections(in tableView: UITableView) -> Int { return sectionNum } /* セクションの高さを設定 */ func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return CGFloat(sectionHight) } /* セルの高さを決めるメソッド */ func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return CGFloat(cellHight) }➃セクションのラベルを設定する
セクションラベルはstoryboardで設定できないのでコードで記述します。
イメージとして、Viewの上にLabelとButtonを乗っける
func buttonTappedはセクションラベルに追加されたボタンをタップされるとこれが呼ばれます。ViewController.swift/* セクションのラベルを設定する */ func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { let view = UIView(frame: CGRect.zero) //zeroは目一杯に広げる //tableView.bounds.width はスマホの横幅を取得するメソッド let label = UILabel(frame: CGRect(x:0, y:0, width: tableView.bounds.width, height: 50)) label.text = "\(sectionTitles[section])" //追加の際入力した文字を表示 label.textAlignment = NSTextAlignment.center //文字位置変更[.right][.center][.left] label.font = UIFont.italicSystemFont(ofSize: 21) //文字サイズ変更 label.backgroundColor = UIColor.gray //背景色変更 label.textColor = UIColor.black //文字色変更 //self.view.frame.maxX は横幅の最大値を取得 基本的には左上が座標(0,0) let button = UIButton(frame: CGRect(x:self.view.frame.maxX - 50, y:0, width:50, height: 50)) button.backgroundColor = UIColor.black button.setTitle("追加", for: .normal) button.tag = section //ボタンにタグをつける //追加ボタンがタップされた際に画面遷移をする(buttonTappedはタップされた際に呼び出される関数) button.addTarget(self, action: #selector(ViewController.buttonTapped(sender:)), for: .touchUpInside) //セクションバーの上にあるviewに加える 階層として sectionBar > view > label = button view.addSubview(label) view.addSubview(button) return view } @objc func buttonTapped(sender:UIButton){ indexPath_section = sender.tag //同じstororyboard内であることをここで定義 let storyboard: UIStoryboard = self.storyboard! //移動先のstoryboardを選択(Identifierを指定する) let second = storyboard.instantiateViewController(withIdentifier: "cellAdd") //実際に移動するコード self.present(second, animated: true, completion: nil) }⑤セルを選択した時のイベントを追加
ViewController.swift/* セルを選択した時のイベントを追加 */ func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { indexPath_section = indexPath.section //記憶させてCellTapped.swiftで使う indexPath_row = indexPath.row //同じstororyboard内であることをここで定義 let storyboard: UIStoryboard = self.storyboard! //移動先のstoryboardを選択(Identifierを指定する) let second = storyboard.instantiateViewController(withIdentifier: "cellTapped") //実際に移動するコード self.present(second, animated: true, completion: nil) } //元からあるコード override func viewDidLoad() { super.viewDidLoad() } }以上がViewController.swiftのコードです。
Step.5 多次元配列について
多次元配列とは配列の中に配列が入ってるような物です。
詳しくは、この記事へどうぞ
Swift 多次元配列の宣言、追加、削除について
今回はセルの内容を記憶するためにこの多次元配列を使用します。
わかりやすく例をあげると、下のような画像の時
cellList = [["Tokyo"],["NewYork","Texas"],["Paris"]]
となります。
Step.6 残りのコードについて
内容は簡単なのでさらっと紹介します。
ちなみにfunc viewDidLoad()は画面が切り替わるたびに呼び出される関数です。■SectionAdd.swift
SectionAdd.swiftimport UIKit class SectionAdd: UIViewController { @IBOutlet weak var SectionTextField: UITextField! @IBAction func SectionAddButton(_ sender: Any) { //配列に入力内容を入れる sectionTitles.append(SectionTextField.text!) //追加ボタンを押したらフィールドを空にする SectionTextField.text = "" //セクションの数を1つ増やす。 sectionNum += 1 cellList.append([]) } override func viewDidLoad() { super.viewDidLoad() } }■CellAdd.swift
CellAdd.swiftimport UIKit class CellAdd: UIViewController { @IBOutlet weak var CellTextField: UITextField! @IBAction func CellAddButton(_ sender: Any) { //cellList.append([]) cellList[indexPath_section].append(CellTextField.text!) //追加ボタンを押したらフィールドを空にする CellTextField.text = "" //セルの数を1つ増やす。 cellNum += 1 } override func viewDidLoad() { super.viewDidLoad() } }■CellTapped.swift
CellAdd.swiftimport UIKit class CellTappedController: UIViewController { @IBOutlet weak var CellName: UILabel! @IBOutlet weak var CellNumber: UILabel! override func viewDidLoad() { super.viewDidLoad() CellName.text = cellList[indexPath_section][Int(indexPath_row)] CellNumber.text = "第\(indexPath_section)セクションの\(indexPath_row)個目のセル" } }Step.7 起動してみよう
うまく動かすことができましたね。お疲れ様でした!
Step.8 チャレンジ!
やり足りないよという方はこちらを実装してみてください!!
・クリアの実装(*ヒント*.reloadData()を使うといいよ!)
・セクションバーについて
・xibを使ってカスタムセルを作る
・セルを再利用する
・画像を挿入してみる
・ボタンを追加してみる
・Disclosure Indicator や Checkmarkを実装してみる(*ヒント*storyboardからできるよ)
・アプリを閉じても内容が記憶されたままにしておく(*UserDefaults*を使用する)●あとがき
正月家にこもってずっとTableViewと格闘していました。この記事をみた方の役に立つことを願っています!
今後の励みになりますので、ぜひ高評価とコメントお願いします!!
次は何について書こうかな。。。
Part.1はこちらから↓
【Swift】【Xcode】詳しいTableViewの使い方。Part.1 準備編
- 投稿日:2020-01-07T20:22:34+09:00
【Swift】【Xcode】詳しいTableViewの使い方。Part.1 準備編
●まえがき
こんにちは。今回は初心者がよくつまずくであろうTableViewについて紹介します。
私自身もつまずき合計で50時間ほど理解するのに時間がかかりました。
一番つまずいた、何個もセクションやセルを追加する方法も丁寧に説明しますよ〜この記事を見れば大幅に理解するための時間短縮になると思います!
今回作る物
今回はJapan,America,Franceの3つのセクションを作ってみます。
次にそれぞれの国の都市をセルに追加していきます。
このようになります
セルをタップするとタップされたセルの情報を取得します
これを作っていきます!!
それではLet`s Go!!**●解説
目次
Part.1
1.プロジェクトの立ち上げ
2.Main.Storyboardの設定
3.各種接続Part.2
4.TableViewの設定
5.多次元配列について
6.残りのコードについて
7.起動してみよう
8.チャレンジStep.1 プロジェクトの立ち上げ
基本的な操作は理解しているものとして進めていきます。
今回のプロジェクト名は"TableViewStudy"としましたStep.2 Main.Storyboardの設定
まえがきで説明した物を作るために、新しいViewControllerを(SectionAdd,CellAdd,CellTapped)3つ作ります。
まずは
ViewController,NavigationBar,BarButtonItem,TextField,Buttonの設置をしましょう。
次にTableViewとTableViewCellの設置をしましょう。
TableViewを先にView目一杯に広げ、その後にTableViewCellを設置します。Step.3各種接続
■SectionAdd,CellAdd,CellTappedの3つのClassを追加する。
NewFileからCocoaTouchClassを選択、SubclassOf:はUIViewControllerにしてください。
■storyboardの接続。
下の画像の手順にしたがって、上から順にSectionAdd,CellAdd,CellTappedと実行してください。
■画面遷移の設定。
controlキーを押しながらドラッグアンドドロップしましょう。ActionSegueはShowにしてください。
ios13から画面遷移の設定が変わったそうなので(私はあまりよく知らない)
下の画像の手順にしたがって、PresentationをFullScreenにしてください。
セル追加画面と、表示画面はのちに出てくるボタンから遷移します
■セルのIdentifierをTableCellとする。
■ViewControllerのIdentifierをcellAdd,cellTappedとする。
■TableViewのdataSourceとdelegateを接続する。
■セルの高さを可変にするためにAutomaticにチェックを入れる。
おわりに
Part.1はここまでです! お疲れ様でした!!
次回のPart.2からコードを書いていきます!
- 投稿日:2020-01-07T18:18:27+09:00
Swiftで連絡先アプリに連絡先を追加する
はじめに
連絡先アプリから読み込みを行う記事はそこそこありましたが、連絡先を追加する記事はそんなになかったのでメモとして残しておきます。
環境
以下の環境で動作確認を行いました。
- Xcodeのバージョン: Xcode11.3
- Swiftのバージョン: Swift5
下準備
連絡先にアクセスするためにinfo.plistに以下の項目を追加します。
Key Value Privacy - Contacts Usage Description連絡先にアクセスする理由等を記述 これがないとエラーになるので忘れずに追加します。
連絡先へのアクセス許可を確認
アプリが連絡先にアクセスすることを許可してもらう必要があるので、現在の認証状態を確認してそれに応じた対応を行います。
初回の場合はいつものアクセス許可を求めるアラートを表示させたりするのが一般的だと思います。
その際にアラートを出したり、後の連絡先を保存する際に必要になるCNContactStoreをインスタンス化させておきます。import Contacts let contactStore = CNContactStore() switch CNContactStore.authorizationStatus(for: .contacts) { case .notDetermined, .restricted: contactStore.requestAccess(for: .contacts) { granted, error in if let error = error { print(error) } if granted { // アクセスを許可してもらえた時 } else { // アラートからアクセスの許可をしてもらえなかった時 } } case .denied: // 拒否されている場合 case .authorized: // すでにアクセスが許可されている場合 default: // それ以外の場合 }連絡先を追加
それでは本題です。
まずは連絡先のデータベースに保存を行うためのCNSaveRequestと保存する連絡先の情報となるCNMutableContact用意します。import Contacts let request = CNSaveRequest() let contact = CNMutableContact()名前や電話番号、メールアドレスなどの情報を
CNMutableContactに入れていって、最後にCNSaveRequestで保存を行うような流れになります。名前
名字、名前、それぞれのふりがなを指定することができます。
contact.givenName = "名前" contact.familyName = "名字" contact.phoneticGivenName = "なまえ" contact.phoneticFamilyName = "みょうじ"組織
組織名とふりがなを指定できます。
contact.organizationName = "組織名" contact.phoneticOrganizationName = "そしきめい"電話番号
電話番号は複数指定可能です。
CNLabeledValue<CNPhoneNumber>の配列となっていて、初期化に若干苦戦しました。引数
labelにCNLabelPhoneNumberMainのようなCNLabelPhoneNumber...となっている定数を指定すると、デフォルトで用意されている電話番号の項目用のタイトルが上につきます。String?なので任意の文字列を入れることも可能です。contact.phoneNumbers = [ CNLabeledValue<CNPhoneNumber>(label: CNLabelPhoneNumberMain, value: CNPhoneNumber(stringValue: "123-4567-8910")), CNLabeledValue<CNPhoneNumber>(label: "カスタムのラベル", value: CNPhoneNumber(stringValue: "123-4567-8910")) ]メールアドレス
メールアドレスも電話番号と同じく複数指定可能です。
CNLabelEmailAddress...みたいなのはなかったので、CNLabelHomeとCNLabelWorkを指定しています。この辺はなんでもいいのかもしれません。contact.emailAddresses = [ CNLabeledValue<NSString>(label: CNLabelHome, value: NSString(string: "sample@sample.com")), CNLabeledValue<NSString>(label: CNLabelWork, value: NSString(string: "sample@sample.com")) ]URL
これも同じく複数指定可能です。
contact.urlAddresses = [CNLabeledValue<NSString>(label: CNLabelURLAddressHomePage, value: NSString(string: "https://www.apple.com/jp/"))]住所
同じく複数指定可能です。
CNMutablePostalAddressが引数に必要になるのでインスタンス化して住所の情報を代入します。let address = CNMutablePostalAddress() address.country = "日本" address.postalCode = "460-8508" address.state = "愛知県" address.city = "名古屋市" address.street = "中区三の丸3丁目1−1" contact.postalAddresses = [CNLabeledValue<CNPostalAddress>(label: CNLabelHome, value: address)]保存
最後に保存を行います。
CNSaveRequestに保存するCNMutableContactを追加してから保存を行います。
例外を投げるのでtry catchが必要です。request.add(contact, toContainerWithIdentifier: contactStore.defaultContainerIdentifier()) do { try contactStore.execute(request) } catch { print(error) }追加された連絡先はこんな感じに表示されます。
さいごに
他にも
birthdayやrelation、socialProfilesなどを指定することができますが、とりあえずよく使いそうな項目だけ抜粋しました。何か間違っている点などがありましたら指摘してください?♂️
参考にさせていただいた記事
- 投稿日:2020-01-07T18:02:06+09:00
【解決済み】SWIFT_VERSION '5.0' is unsupported, supported versions are: 3.0, 4.0, 4.2の対処法
解決方法
まず最初に書いておきます。
ライブラリを選択して、Swift Language Versionを4.2に選択
説明
pod inittarget "aaa" do use_frameworks! pod "Alamofire" pod "SwiftyJSON" pod "NVActivityIndicatorView" endpod installここでインストールしたライブラリの数だけエラーが出ます。エラー内容が
SWIFT_VERSION '5.0' is unsupported, supported versions are: 3.0, 4.0, 4.2こんな感じ。
書いてあることは5.0はサポートされてないよー。3.0, 4.0, 4.2のどれかにしてねーって言ってます。
swift --versionでswiftのバージョンを調べてみても
swift --version Apple Swift version 4.2.1 (swiftlang-1000.11.42 clang-1000.11.45.1) Target: x86_64-apple-darwin17.7.0と出てきます。
?
XcodeとSwiftのバージョンが悪いのかなと考えていたのですが、答え見つけました。
英語読めてよかったーーー!
まとめ
初心者はエラーに弱いですが、頑張ってググれば出てきます。頑張りましょう!
- 投稿日:2020-01-07T17:44:44+09:00
【swift5】現在地を取得した天気アプリ
ソースはこちら
https://github.com/sventouz/swift_weatherApp
全体像
①は固定。。。
これは位置情報を取得して今いる場所の地名を取得したかったができなかった。
Google Map APIの逆ジオコーディングを使用することで実現可能なので近日中に仕上げたい。②こちらは今日の日付を取得して表示するだけなのだが、時間がなく割愛。ここもあとで実装する。
③ここは変わる。笑
現在地の天気によって画像が変わる仕様になっている。参考のYOUTUBEの動画のかたが親切にもまとめてくださっているので使うと簡単。④どんな天気かが表示される。
⑤現在地の気温。
アプリの詳細
このアプリは
- Alamofire
- SwiftyJSON
- NVActivityIndicatorView
の3つのライブラリを使用している。
現在地から天気や気温などを取得しているのはOpenWeatherAPIである。
ソース
ソースは以下の通りです。説明が長くなってしまうのでページ下部にあるYoutubeをみていただきたい。
import UIKit import Alamofire import SwiftyJSON import NVActivityIndicatorView import CoreLocation class ViewController: UIViewController, CLLocationManagerDelegate { @IBOutlet weak var locationLabel: UILabel! @IBOutlet weak var dayLabel: UILabel! @IBOutlet weak var conditionImageView: UIImageView! @IBOutlet weak var conditionLabel: UILabel! @IBOutlet weak var temperatureLabel: UILabel! let apiKey = "ここはあなたのAPIkey" var lat = 26.8205 var lon = 30.8024 // loading var activityIndicator: NVActivityIndicatorView! // user location let locationManager = CLLocationManager() override func viewDidLoad() { super.viewDidLoad() let indicatorSize: CGFloat = 70 let indicatorFrame = CGRect(x: (view.frame.width-indicatorSize)/2, y: (view.frame.height-indicatorSize)/2, width: indicatorSize, height: indicatorSize) activityIndicator = NVActivityIndicatorView(frame: indicatorFrame, type: .lineScale, color: UIColor.white, padding: 20.0) activityIndicator.backgroundColor = UIColor.black view.addSubview(activityIndicator) // use popup to check and get location locationManager.requestWhenInUseAuthorization() if (CLLocationManager.locationServicesEnabled()) { locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.startUpdatingLocation() } } func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { let location = locations[0] print(location) lat = location.coordinate.latitude lon = location.coordinate.longitude Alamofire.request("http://api.openweathermap.org/data/2.5/weather?lat=\(lat)&lon=\(lon)&appid=\(apiKey)&units=metric").responseJSON { response in self.activityIndicator.stopAnimating() if let responseStr = response.result.value { let jsonResponse = JSON(responseStr) let jsonWeather = jsonResponse["weather"].array![0] let jsonTemp = jsonResponse["main"] let iconName = jsonWeather["icon"].stringValue self.locationLabel.text = jsonResponse["name"].stringValue self.conditionImageView.image = UIImage(named: iconName) self.conditionLabel.text = jsonWeather["main"].stringValue self.temperatureLabel.text = "\(Int(round(jsonTemp["temp"].doubleValue)))" } } } }まとめ
今回初めてきちんとAPIから天気を取得して表示することができた。
これで自分好みの天気アプリを作りたい。
天気アプリを作った感じだが、実際には
- APIの使い方
- 位置情報の使い方
- JSONの扱い方
などとその周りも扱えたのがよかった。
参考
今回はこちらの方のものを参考にさせていただきました。
- 投稿日:2020-01-07T17:40:37+09:00
ubuntu16.04LTSでSwiftの環境構築!
はじめに
私はiOSアプリ開発によく使われるSwiftを勉強しようとSwiftのツール「Xcode」をやってみようとしましたが、
私のパソコンはWindowsなのでXcodeは使えないと知りました。
「MacのパソコンじゃないとXcodeは動かない(´;ω;`)」
と嘆く私でしたが、WindowsでもSwiftを扱いたい!と思ったため、今回私がwindowsでSwiftの環境構築を
行った手順を紹介しようと思います。windowsといっても ubuntu16.04LTS を使っていますが(笑)
手順
Ubuntu16.04LTSを開いて以下のコマンドを入力していきます。
パッケージのアップデート
$ sudo apt-get update $ sudo apt-get upgradeclang libicu-devのインストール
swiftを使用するうえで必要なパッケージをインストールします。
$ sudo apt-get install clang libicu-devSwiftを入れるディレクトリを作成
$ mkdir /mnt/c/SwiftSwiftのダウンロード
$ cd /mnt/c/Swift $ wget https://swift.org/builds/swift-5.1.3-release/ubuntu1604/swift-5.1.3-RELEASE/swift-5.1.3-RELEASE-ubuntu16.04.tar.gzSwiftの最新バージョンは以下で確認できます。(バージョン5.1.3は2020/01/07現在最新)
https://swift.org/download/ダウンロードしたファイルを解凍
$ tar xzf swift-5.1.3-RELEASE-ubuntu16.04.tar.gzパスを通す
$ vi ~/.bashrc最終行に以下を追加
PATH="/mnt/c/Swift/swift-3.0.2-RELEASE-ubuntu14.04/usr/bin:$PATH"$ source ~/.bashrcSwift確認
以下のコマンドでSwiftのバージョンが確認できれば構築完了です。
おつかれさまでした!$ swift -versionSwiftのコンパイル&実行
任意の場所にhelloWorld.swiftを置く。(中身は print("hello world!") )
$ swiftc helloWorld.swift ←コンパイル $ ./helloWorld ←実行プロジェクト作成
プロジェクトディレクトリを作成
$ mkdir swiftProject $ cd swiftProjectプロジェクト作成
$ swift package init --type executableプロジェクトのビルド
$ swift buildプロジェクトの実行
$ ./.build//x86_64-unknown-linux/debug/(プロジェクト名)
- 投稿日:2020-01-07T17:40:37+09:00
windows10でSwiftの環境構築!
はじめに
私はiOSアプリ開発によく使われるSwiftを勉強しようとSwiftのツール「Xcode」をやってみようとしましたが、
私のパソコンはWindowsなのでXcodeは使えないと知りました。
「MacのパソコンじゃないとXcodeは動かない(´;ω;`)」
と嘆く私でしたが、WindowsでもSwiftを扱いたい!と思ったため、今回私がwindowsでSwiftの環境構築を
行った手順を紹介しようと思います。windowsといっても ubuntu16.04LTS を使っていますが(笑)
手順
Ubuntu16.04LTSを開いて以下のコマンドを入力していきます。
パッケージのアップデート
$ sudo apt-get update $ sudo apt-get upgradeclang libicu-devのインストール
swiftを使用するうえで必要なパッケージをインストールします。
$ sudo apt-get install clang libicu-devSwiftを入れるディレクトリを作成
$ mkdir /mnt/c/SwiftSwiftのダウンロード
$ cd /mnt/c/Swift $ wget https://swift.org/builds/swift-5.1.3-release/ubuntu1604/swift-5.1.3-RELEASE/swift-5.1.3-RELEASE-ubuntu16.04.tar.gzSwiftの最新バージョンは以下で確認できます。(バージョン5.1.3は2020/01/07現在最新)
https://swift.org/download/ダウンロードしたファイルを解凍
$ tar xzf swift-5.1.3-RELEASE-ubuntu16.04.tar.gzパスを通す
$ vi ~/.bashrc最終行に以下を追加
PATH="/mnt/c/Swift/swift-3.0.2-RELEASE-ubuntu14.04/usr/bin:$PATH"$ source ~/.bashrcSwift確認
以下のコマンドでSwiftのバージョンが確認できれば構築完了です。
おつかれさまでした!$ swift -versionSwiftのコンパイル&実行
任意の場所にhelloWorld.swiftを置く。(中身は print("hello world!") )
$ swiftc helloWorld.swift ←コンパイル $ ./helloWorld ←実行プロジェクト作成
プロジェクトディレクトリを作成
$ mkdir swiftProject $ cd swiftProjectプロジェクト作成
$ swift package init --type executableプロジェクトのビルド
$ swift buildプロジェクトの実行
$ ./.build//x86_64-unknown-linux/debug/(プロジェクト名)
- 投稿日:2020-01-07T11:14:47+09:00
スプレッドシートを使って、iOS開発の効率を上げる
はじめに
アプリ開発の中でデバッグは時間がかかる作業である。
動的に変わる処理のデバッグはさらに時間がかかる。
AndroidとiOS両プラットフォームで展開しているサービスであるとさらに時間がかかる。
その解決手段としてスプレッドシートを使った方法を紹介しますなぜスプレッドシートを使うのか
- 誰でも容易に変更、閲覧ができる
- 簡単なAPIとして扱える
- プラットホーム関係なしに扱える
実践してみる
準備
- シートを作成
- ファイル → ウェブに公開を選択
文言の管理をまとめる xvrh/localize-with-spreadsheet
- スプレッドシートで定義した文言をStringファイルに変換するツール
- Android、iOSの文言の共通管理できる
スプレッドシートの値をアプリで呼び出す t-osawa-009/SpreadsheetClient
- スプレッドシートの値をアプリで呼び出すためのライブラリを自作した物を活用します。
カスタムスキームの定義とデバッグ
- スプレッドシートをカスタムスキームのドキュメントとして使える
- 定義したURLをアプリで取得し、UniversalLink, カスタムスキームを処理するメソッドを呼び出すことでデバッグを簡単に行える
- シートを更新することでUniversalLink, カスタムスキームのパラメータも動的に変更可能
デバッグ用のアカウント管理
- アカウント情報のAPIとして活用できる
注意点
- スプレッドシートをURLを知っていると誰でも情報取得可能。公開されても問題ないもののみ扱う方がいいと思われる
まとめ
- スプレッドシートを使うことで簡単に開発効率を上げられる
- ドキュメントのデータとアプリで実行するデータを一致した値で扱える
環境
Xcode 11.3 Apple Swift version 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15) Target: x86_64-apple-darwin19.0.0
- 投稿日:2020-01-07T06:05:38+09:00
「リファクタリング 第2版」Swiftでコーディング その20
37-38頁 第1章 型による計算処理の再編成「関数をPerformanceCalculatorに移動」「関数の移動(p.206)」
Swift版 createStatementData.swift
import Foundation struct PerormanceMapPlay { let performance: Performance let play: Play var amount: Int = 0 var volumeCredits: Int = 0 } struct StatementData { let customer: String let performances: [PerormanceMapPlay] var totalAmount: Int = 0 var totalVolumeCredits: Int = 0 } public class PerformanceCalculator { let aPerformance: Performance let play: Play init(aPerformance: Performance, play: Play) { self.aPerformance = aPerformance self.play = play } func amount() -> Int { var result = 0 switch self.play.type { case "tragedy": result = 40000 if self.aPerformance.audience > 30 { result += 1000 * (self.aPerformance.audience - 30) } case "comedy": result = 30000 if self.aPerformance.audience > 20 { result += 10000 + 500 * (self.aPerformance.audience - 20) } result += 300 * self.aPerformance.audience default: print("error") } return result } func volumeCredits() -> Int { var result = 0 result += max(self.aPerformance.audience - 30, 0) if "comedy" == self.play.type { result += Int(self.aPerformance.audience / 5) } return result } } func playFor(aPerformance:Performance) -> Play { return plays[aPerformance.playID]! } func usd(aNumber:Int) -> String { let format = NumberFormatter() format.numberStyle = .currency format.locale = Locale(identifier: "en_US") return format.string(from: NSNumber(value: aNumber / 100))! } func totalVolumeCredits(data:StatementData) -> Int { return data.performances.reduce(0) { (num: Int, perf: PerormanceMapPlay) -> Int in num + perf.volumeCredits } } func totalAmount(data:StatementData) -> Int { return data.performances.reduce(0) { (num: Int, perf: PerormanceMapPlay) -> Int in num + perf.amount } } func enrichPerfoemance(aPerformance:[Performance], plays:Dictionary<String, Play>) -> [PerormanceMapPlay] { var result: [PerormanceMapPlay] = [] for perf in aPerformance { let calculator = PerformanceCalculator(aPerformance: perf, play: playFor(aPerformance: perf)) var perormanceMapPlay = PerormanceMapPlay(performance: perf, play: calculator.play) perormanceMapPlay.amount = calculator.amount() perormanceMapPlay.volumeCredits = calculator.volumeCredits() result.append(perormanceMapPlay) } return result } func createStatementData(invoice:Invoice, plays:Dictionary<String, Play>) -> StatementData { var result = StatementData(customer: invoice.customer, performances: enrichPerfoemance(aPerformance: invoice.performances, plays: plays)) result.totalAmount = totalAmount(data: result) result.totalVolumeCredits = totalVolumeCredits(data: result) return result }
- 投稿日:2020-01-07T06:04:53+09:00
「リファクタリング 第2版」Swiftでコーディング その19
34-36頁 第1章 型による計算処理の再編成「ポリモーフィズムによる条件記述の置き換え(p.279)」「PerformanceCalculatorの作成」「関数宣言の変更(p.130)」
Swift版 createStatementData.swift
import Foundation struct PerormanceMapPlay { let performance: Performance let play: Play var amount: Int = 0 var volumeCredits: Int = 0 } struct StatementData { let customer: String let performances: [PerormanceMapPlay] var totalAmount: Int = 0 var totalVolumeCredits: Int = 0 } class PerformanceCalculator { let aPerformance: Performance let play: Play init(aPerformance: Performance, play: Play) { self.aPerformance = aPerformance self.play = play } } func playFor(aPerformance:Performance) -> Play { return plays[aPerformance.playID]! } func volumeCreditsFor(aPerformance:Performance) -> Int { var result = 0 result += max(aPerformance.audience - 30, 0) if "comedy" == playFor(aPerformance: aPerformance).type { result += Int(aPerformance.audience / 5) } return result } func usd(aNumber:Int) -> String { let format = NumberFormatter() format.numberStyle = .currency format.locale = Locale(identifier: "en_US") return format.string(from: NSNumber(value: aNumber / 100))! } func totalVolumeCredits(data:StatementData) -> Int { return data.performances.reduce(0) { (num: Int, perf: PerormanceMapPlay) -> Int in num + perf.volumeCredits } } func totalAmount(data:StatementData) -> Int { return data.performances.reduce(0) { (num: Int, perf: PerormanceMapPlay) -> Int in num + perf.amount } } func amountFor(aPerformance:PerormanceMapPlay) -> Int { var result = 0 switch aPerformance.play.type { case "tragedy": result = 40000 if aPerformance.performance.audience > 30 { result += 1000 * (aPerformance.performance.audience - 30) } case "comedy": result = 30000 if aPerformance.performance.audience > 20 { result += 10000 + 500 * (aPerformance.performance.audience - 20) } result += 300 * aPerformance.performance.audience default: print("error") } return result } func enrichPerfoemance(aPerformance:[Performance], plays:Dictionary<String, Play>) -> [PerormanceMapPlay] { var result: [PerormanceMapPlay] = [] for perf in aPerformance { let calculator = PerformanceCalculator(aPerformance: perf, play: playFor(aPerformance: perf)) var perormanceMapPlay = PerormanceMapPlay(performance: perf, play: calculator.play) perormanceMapPlay.amount = amountFor(aPerformance: perormanceMapPlay) perormanceMapPlay.volumeCredits = volumeCreditsFor(aPerformance: perf) result.append(perormanceMapPlay) } return result } func createStatementData(invoice:Invoice, plays:Dictionary<String, Play>) -> StatementData { var result = StatementData(customer: invoice.customer, performances: enrichPerfoemance(aPerformance: invoice.performances, plays: plays)) result.totalAmount = totalAmount(data: result) result.totalVolumeCredits = totalVolumeCredits(data: result) return result }
- 投稿日:2020-01-07T01:01:00+09:00
iOS&Firebase 初めの一歩:iOSアプリのプロジェクトでFirebaseを使ってみる
対象読者
iOSアプリの作成経験はあるが、Firebaseはまだ使った事がない。
ざっくりとした使用感を掴みたい人。
実プロジェクトで使いそうな要素のみを抽出したサンプルアプリを作成してみる。準備
まずはFirebaseにプロジェクトを登録し設定する。
プロジェクト作成
開発用のGoogleアカウントがあれば(無ければ個人のものでも良い)それでFirebase Consoleにログインする。
Firebaseコンソールプロジェクトを作成(プラスマークのタイル)から新規にプロジェクトを作成する。
ちなみにプロジェクトは異なるクライアント環境ごとに作る必要はない。
モバイルアプリで言えばサーバーサイドの環境に当たるので、大抵はAndroidでもiOSでも共通だろう。以下を入力
- プロジェクト名:任意のもの。
- 地域/ロケーション:デフォルトではアメリカ合衆国になっているが、日本でサービスするならばJapanに変更した方
- アナリティクスの地域:日本(リスト最後の方にある)
- Cloud Firestoreのロケーション:asia-northeast1 →これは異なるリージョン間通信が発生すると課金対象となるので、予め最もユーザーが多い地域にしておくのが有利。 →またCloud Firestoreを利用しない場合も予め上記に設定しておいた方が良い。この値は後から変更できないからだ。
- Firebase 向け Google アナリティクスのデータ共有にデフォルトの設定を使用する →基本的にアナリティクスのデータをgoogleに共有するかどうか、という内容。 →特に個人情報を渡すわけではない(あくまでアナリティクスデータのみ)ので、 理由がない限りはチェックしておく。
- 測定管理者間のデータ保護条例に同意。 →上記アナリティクスの共有に同意した場合、こちらにもチェックを付けないとプロジェクトが作成できないようになっている。
少しするとプロジェクトが作成され、コンソールが表示される。
アプリの追加
このプロジェクトを利用するクライアント(=アプリ)を追加する。
用意されているのは、iOS・Android・Web・Unity
iOSを選択する。
- コンソール上部の「アプリにFirebaseを追加しましよう」の下にある「iOS」アイコンをクリック
iOSアプリにFirebaseを追加ダイアログを入力していく
- Xcodeでプロジェクトを作成する。
- アプリの登録
- バンドルID:Xcodeからコピぺ
- ニックネーム:Firebaseコンソール上で識別しやすい名前が良い。アプリ名やiOSが入っていると良いのかな。
- AppStore ID:App Store Connectにある。入れておく。
- 設定ファイルのダウンロード Firebase接続に必要な情報が書かれた設定ファイルを自動生成してくれるので、ダウンロードしてそのままプロジェクトに追加する。
GoogleService-Info- Firebase SDKの追加 cocoa podsでSDKをインストールする。
- Cocoa podsをインストール(既にあればこの手順は不要)
$ cd プロジェクトディレクトリ $ pod init
- Podファイルを編集して保存
$ vi Podfile target 'FirebaseSample2' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for FirebaseSample2 pod 'Firebase/Core' //←この1行を追加 end
インストール実行
$ pod installこれでxcworkspaceファイルができるはずなので、そこからプロジェクトを起動し、ビルドしてみる。
初期化コードの追加
AppDelegateに以下のコードを追加する。import Firebase func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // 追記する FirebaseApp.configure() return true }
- Firebaseをimport
- application(application,didfinishLaunchingWithOptions)メソッドに
FirebaseApp.configure()(Firebaseの初期化処理)を追記
- アプリを実行して確認 初回起動時にSDKからの通信が正しく行われているかをコンソールから確認できるようになっている。 コンソール側でアプリを認識できたらOK
認証機能の追加
ユーザーを識別しないアプリの方が少ないと思うので、大抵は何はともあれ認証しないと始まらない。
Firebaseの認証(Authentication)の方式としては以下が用意されている。
* メール/パスワード
* 電話番号
* Playゲーム
* Game Center
* GitHub
* 匿名この中でも多く使われそうなのは、メール/パスワード、SNS、匿名あたりだろうか。
アプリによっては電話番号も良いソリューションになるだろう。
また、自前システムでの認証などにも対応できるカスタム認証という手法も取れる(ここでは扱わない)準備
準備といっても先ほどの認証方式を設定するだけ。
1. FirebaseコンソールのAuthenticationをクリック
2. ログイン方法タブをクリック
3. 認証方式(ログインプロバイダという)のうち、使うものを有効にする。なお、FacebookやTiwtterを利用する場合、それぞれで開発者登録したうえでアプリの登録が必要となる。
この辺のことは情報も多いので各自探し、以下ドキュメントも参照してほしい。
https://firebase.google.com/docs/auth/?authuser=0また、より詳細な情報を得るにはガイドの参照もお勧めする。
https://firebase.google.com/docs/ios/setupSDKの導入
Firebaseはサービスドメインごとにポッドが別れている。
既に導入しているCoreは必須ライブラリだがそれ以外は個別に指定する必要がある。認証に関して言えば基本はAuhtなので、以下を追記することになる。
pod 'Firebase/Auth'また、今回は扱わないがSNSなど複数のフェデレーションを利用する場合などに便利なUI(ViewControllerサブクラス)を自動生成してくれる仕組みも用意されている。その場合には、さらに
pod 'FirebaseUIを追記する。































