20200107のSwiftに関する記事は11件です。

【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.swift
import 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"]]
となります。
スクリーンショット 2020-01-07 14.42.06.png

Step.6 残りのコードについて

内容は簡単なのでさらっと紹介します。
ちなみにfunc viewDidLoad()は画面が切り替わるたびに呼び出される関数です。

■SectionAdd.swift

SectionAdd.swift
import 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.swift
import 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.swift
import 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 起動してみよう

スクリーンショット 2020-01-07 20.04.50.png

うまく動かすことができましたね。お疲れ様でした!

Step.8 チャレンジ!

やり足りないよという方はこちらを実装してみてください!!

・クリアの実装(*ヒント*.reloadData()を使うといいよ!)
・セクションバーについて
    ・xibを使ってカスタムセルを作る
    ・セルを再利用する
    ・画像を挿入してみる
    ・ボタンを追加してみる
    ・Disclosure Indicator や Checkmarkを実装してみる(*ヒント*storyboardからできるよ)
・アプリを閉じても内容が記憶されたままにしておく(*UserDefaults*を使用する)

●あとがき

正月家にこもってずっとTableViewと格闘していました。この記事をみた方の役に立つことを願っています!
今後の励みになりますので、ぜひ高評価とコメントお願いします!!
次は何について書こうかな。。。
Part.1はこちらから↓
【Swift】【Xcode】詳しいTableViewの使い方。Part.1 準備編

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】【Xcode】詳しいTableViewの使い方。Part.1 準備編

●まえがき

こんにちは。今回は初心者がよくつまずくであろうTableViewについて紹介します。
私自身もつまずき合計で50時間ほど理解するのに時間がかかりました。
一番つまずいた、何個もセクションやセルを追加する方法も丁寧に説明しますよ〜

この記事を見れば大幅に理解するための時間短縮になると思います!

今回作る物

スクリーンショット 2020-01-07 14.34.56.png
追加ボタンを押すとセクション追加画面へ。

スクリーンショット 2020-01-07 14.38.19.png
今回はJapan,America,Franceの3つのセクションを作ってみます。
スクリーンショット 2020-01-07 14.40.19.png
次にそれぞれの国の都市をセルに追加していきます。
スクリーンショット 2020-01-07 14.41.20.png
このようになります
スクリーンショット 2020-01-07 14.42.06.png
セルをタップするとタップされたセルの情報を取得します
スクリーンショット 2020-01-07 14.42.24.png

これを作っていきます!!
それでは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の設置をしましょう。
スクリーンショット 2020-01-07 13.51.04.png
次にTableViewとTableViewCellの設置をしましょう。
TableViewを先にView目一杯に広げ、その後にTableViewCellを設置します。

スクリーンショット 2020-01-07 13.56.44.png

Step.3各種接続

SectionAdd,CellAdd,CellTappedの3つのClassを追加する。

NewFileからCocoaTouchClassを選択、SubclassOf:はUIViewControllerにしてください。

スクリーンショット 2020-01-07 14.03.38.png
スクリーンショット 2020-01-07 14.04.24.png
スクリーンショット 2020-01-07 14.04.55.png

■storyboardの接続。

下の画像の手順にしたがって、上から順にSectionAdd,CellAdd,CellTappedと実行してください。
スクリーンショット 2020-01-07 14.12.47.png

■画面遷移の設定。

controlキーを押しながらドラッグアンドドロップしましょう。ActionSegueはShowにしてください。
スクリーンショット 2020-01-07 14.18.45.png

ios13から画面遷移の設定が変わったそうなので(私はあまりよく知らない)
下の画像の手順にしたがって、PresentationをFullScreenにしてください。
スクリーンショット 2020-01-07 14.19.35.png

セル追加画面と、表示画面はのちに出てくるボタンから遷移します
スクリーンショット 2020-01-07 14.26.08.png

セルのIdentifierをTableCellとする。

スクリーンショット 2020-01-07 16.23.16.png

ViewControllerのIdentifierをcellAdd,cellTappedとする。

スクリーンショット 2020-01-07 16.39.43.png
スクリーンショット 2020-01-07 16.42.18.png

■TableViewのdataSourceとdelegateを接続する。

スクリーンショット 2020-01-07 19.47.45.png

■セルの高さを可変にするためにAutomaticにチェックを入れる。

スクリーンショット 2020-01-07 19.49.31.png
スクリーンショット 2020-01-07 19.49.57.png

おわりに

Part.1はここまでです! お疲れ様でした!!
次回のPart.2からコードを書いていきます!

【Swift】【Xcode】詳しいTableViewの使い方。Part.2 コード編

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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>の配列となっていて、初期化に若干苦戦しました。

引数labelCNLabelPhoneNumberMainのようなCNLabelPhoneNumber...となっている定数を指定すると、デフォルトで用意されている電話番号の項目用のタイトルが上につきます。String?なので任意の文字列を入れることも可能です。

contact.phoneNumbers = [
    CNLabeledValue<CNPhoneNumber>(label: CNLabelPhoneNumberMain, value: CNPhoneNumber(stringValue: "123-4567-8910")),
    CNLabeledValue<CNPhoneNumber>(label: "カスタムのラベル", value: CNPhoneNumber(stringValue: "123-4567-8910"))
]

メールアドレス

メールアドレスも電話番号と同じく複数指定可能です。

CNLabelEmailAddress...みたいなのはなかったので、CNLabelHomeCNLabelWorkを指定しています。この辺はなんでもいいのかもしれません。

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)
}

追加された連絡先はこんな感じに表示されます。

スクリーンショット 2020-01-07 17.39.18.png

さいごに

他にもbirthdayrelationsocialProfilesなどを指定することができますが、とりあえずよく使いそうな項目だけ抜粋しました。

何か間違っている点などがありましたら指摘してください?‍♂️

参考にさせていただいた記事

swift(iOS10)で連絡先アプリから連絡先データを取得する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【解決済み】SWIFT_VERSION '5.0' is unsupported, supported versions are: 3.0, 4.0, 4.2の対処法

解決方法

まず最初に書いておきます。

podを選択
スクリーンショット 2020-01-07 17.56.16.png

ライブラリを選択して、Swift Language Versionを4.2に選択
スクリーンショット 2020-01-07 17.56.34.png

完了
スクリーンショット 2020-01-07 17.56.47.png

説明

pod init
target "aaa" do

  use_frameworks!

  pod "Alamofire"
  pod "SwiftyJSON"
  pod "NVActivityIndicatorView"

end
pod 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のバージョンが悪いのかなと考えていたのですが、答え見つけました。

https://stackoverflow.com/questions/55585062/xcode-keeps-showing-1-swift-version-5-0-is-unsupported-supported-versions-a

英語読めてよかったーーー!

まとめ

初心者はエラーに弱いですが、頑張ってググれば出てきます。頑張りましょう!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【swift5】現在地を取得した天気アプリ

ソースはこちら

https://github.com/sventouz/swift_weatherApp

全体像

スクリーンショット 2020-01-07 17.32.25.png

①は固定。。。
これは位置情報を取得して今いる場所の地名を取得したかったができなかった。
Google Map APIの逆ジオコーディングを使用することで実現可能なので近日中に仕上げたい。

②こちらは今日の日付を取得して表示するだけなのだが、時間がなく割愛。ここもあとで実装する。 

③ここは変わる。笑
現在地の天気によって画像が変わる仕様になっている。参考のYOUTUBEの動画のかたが親切にもまとめてくださっているので使うと簡単。

④どんな天気かが表示される。

⑤現在地の気温。

アプリの詳細

このアプリは

  • Alamofire
  • SwiftyJSON
  • NVActivityIndicatorView

の3つのライブラリを使用している。

現在地から天気や気温などを取得しているのはOpenWeatherAPIである。

https://openweathermap.org/

ソース

ソースは以下の通りです。説明が長くなってしまうのでページ下部にある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の扱い方

などとその周りも扱えたのがよかった。

参考

今回はこちらの方のものを参考にさせていただきました。

https://www.youtube.com/watch?v=WHRntPeAOo4

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 upgrade

clang libicu-devのインストール

swiftを使用するうえで必要なパッケージをインストールします。

$ sudo apt-get install clang libicu-dev

Swiftを入れるディレクトリを作成

$ mkdir /mnt/c/Swift

Swiftのダウンロード

$ 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.gz

Swiftの最新バージョンは以下で確認できます。(バージョン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 ~/.bashrc

Swift確認

以下のコマンドでSwiftのバージョンが確認できれば構築完了です。
おつかれさまでした!

$ swift -version

Swiftのコンパイル&実行

任意の場所に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/(プロジェクト名)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 upgrade

clang libicu-devのインストール

swiftを使用するうえで必要なパッケージをインストールします。

$ sudo apt-get install clang libicu-dev

Swiftを入れるディレクトリを作成

$ mkdir /mnt/c/Swift

Swiftのダウンロード

$ 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.gz

Swiftの最新バージョンは以下で確認できます。(バージョン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 ~/.bashrc

Swift確認

以下のコマンドでSwiftのバージョンが確認できれば構築完了です。
おつかれさまでした!

$ swift -version

Swiftのコンパイル&実行

任意の場所に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/(プロジェクト名)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スプレッドシートを使って、iOS開発の効率を上げる

はじめに

アプリ開発の中でデバッグは時間がかかる作業である。
動的に変わる処理のデバッグはさらに時間がかかる。
AndroidとiOS両プラットフォームで展開しているサービスであるとさらに時間がかかる。
その解決手段としてスプレッドシートを使った方法を紹介します

なぜスプレッドシートを使うのか

  • 誰でも容易に変更、閲覧ができる
  • 簡単なAPIとして扱える
  • プラットホーム関係なしに扱える

実践してみる

準備

  • シートを作成
  • ファイル → ウェブに公開を選択

Screenshot 2020-01-08 at 12.15.17.png

  • 公開範囲を指定する Screenshot 2020-01-08 at 12.19.27.png

文言の管理をまとめる xvrh/localize-with-spreadsheet

  • スプレッドシートで定義した文言をStringファイルに変換するツール
  • Android、iOSの文言の共通管理できる

スプレッドシートの値をアプリで呼び出す t-osawa-009/SpreadsheetClient

  • スプレッドシートの値をアプリで呼び出すためのライブラリを自作した物を活用します。

カスタムスキームの定義とデバッグ

  • スプレッドシートをカスタムスキームのドキュメントとして使える
  • 定義したURLをアプリで取得し、UniversalLink, カスタムスキームを処理するメソッドを呼び出すことでデバッグを簡単に行える
  • シートを更新することでUniversalLink, カスタムスキームのパラメータも動的に変更可能

Screenshot 2020-01-07 at 10.51.52.png

デバッグ用のアカウント管理

  • アカウント情報のAPIとして活用できる

Screenshot 2020-01-07 at 11.16.28.png

注意点

  • スプレッドシートを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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「リファクタリング 第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
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

「リファクタリング 第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
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS&Firebase 初めの一歩:iOSアプリのプロジェクトでFirebaseを使ってみる

対象読者

iOSアプリの作成経験はあるが、Firebaseはまだ使った事がない。
ざっくりとした使用感を掴みたい人。
実プロジェクトで使いそうな要素のみを抽出したサンプルアプリを作成してみる。

準備

まずはFirebaseにプロジェクトを登録し設定する。

プロジェクト作成

  1. 開発用のGoogleアカウントがあれば(無ければ個人のものでも良い)それでFirebase Consoleにログインする。
    Firebaseコンソール

  2. プロジェクトを作成(プラスマークのタイル)から新規にプロジェクトを作成する。
    ちなみにプロジェクトは異なるクライアント環境ごとに作る必要はない
    モバイルアプリで言えばサーバーサイドの環境に当たるので、大抵はAndroidでもiOSでも共通だろう。

  3. 以下を入力

    • プロジェクト名:任意のもの。
    • 地域/ロケーション:デフォルトではアメリカ合衆国になっているが、日本でサービスするならばJapanに変更した方
    • アナリティクスの地域:日本(リスト最後の方にある)
    • Cloud Firestoreのロケーション:asia-northeast1 →これは異なるリージョン間通信が発生すると課金対象となるので、予め最もユーザーが多い地域にしておくのが有利。 →またCloud Firestoreを利用しない場合も予め上記に設定しておいた方が良い。この値は後から変更できないからだ。
    • Firebase 向け Google アナリティクスのデータ共有にデフォルトの設定を使用する →基本的にアナリティクスのデータをgoogleに共有するかどうか、という内容。 →特に個人情報を渡すわけではない(あくまでアナリティクスデータのみ)ので、 理由がない限りはチェックしておく。
    • 測定管理者間のデータ保護条例に同意。 →上記アナリティクスの共有に同意した場合、こちらにもチェックを付けないとプロジェクトが作成できないようになっている。
  4. 少しするとプロジェクトが作成され、コンソールが表示される。

アプリの追加

このプロジェクトを利用するクライアント(=アプリ)を追加する。
用意されているのは、iOS・Android・Web・Unity
iOSを選択する。

  1. コンソール上部の「アプリにFirebaseを追加しましよう」の下にある「iOS」アイコンをクリック
  2. iOSアプリにFirebaseを追加ダイアログを入力していく

    1. Xcodeでプロジェクトを作成する。
    2. アプリの登録
    3. バンドルID:Xcodeからコピぺ
    4. ニックネーム:Firebaseコンソール上で識別しやすい名前が良い。アプリ名やiOSが入っていると良いのかな。
    5. AppStore ID:App Store Connectにある。入れておく。
    6. 設定ファイルのダウンロード Firebase接続に必要な情報が書かれた設定ファイルを自動生成してくれるので、ダウンロードしてそのままプロジェクトに追加する。 GoogleService-Info
    7. Firebase SDKの追加 cocoa podsでSDKをインストールする。
      1. Cocoa podsをインストール(既にあればこの手順は不要)
$ cd プロジェクトディレクトリ
$ pod init
  1. 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
  1. インストール実行
    $ pod install

    これでxcworkspaceファイルができるはずなので、そこからプロジェクトを起動し、ビルドしてみる。

  2. 初期化コードの追加
    AppDelegateに以下のコードを追加する。

import Firebase

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // 追記する        
        FirebaseApp.configure()

        return true
    }
  • Firebaseをimport
  • application(application,didfinishLaunchingWithOptions)メソッドに FirebaseApp.configure()(Firebaseの初期化処理)を追記
  1. アプリを実行して確認 初回起動時にSDKからの通信が正しく行われているかをコンソールから確認できるようになっている。 コンソール側でアプリを認識できたらOK

認証機能の追加

ユーザーを識別しないアプリの方が少ないと思うので、大抵は何はともあれ認証しないと始まらない。
Firebaseの認証(Authentication)の方式としては以下が用意されている。
* メール/パスワード
* 電話番号
* Google
* Playゲーム
* Game Center
* Facebook
* Twitter
* GitHub
* 匿名

この中でも多く使われそうなのは、メール/パスワード、SNS、匿名あたりだろうか。
アプリによっては電話番号も良いソリューションになるだろう。
また、自前システムでの認証などにも対応できるカスタム認証という手法も取れる(ここでは扱わない)

準備

準備といっても先ほどの認証方式を設定するだけ。
1. FirebaseコンソールのAuthenticationをクリック
2. ログイン方法タブをクリック
3. 認証方式(ログインプロバイダという)のうち、使うものを有効にする。

なお、FacebookやTiwtterを利用する場合、それぞれで開発者登録したうえでアプリの登録が必要となる。
この辺のことは情報も多いので各自探し、以下ドキュメントも参照してほしい。
https://firebase.google.com/docs/auth/?authuser=0

また、より詳細な情報を得るにはガイドの参照もお勧めする。
https://firebase.google.com/docs/ios/setup

SDKの導入

Firebaseはサービスドメインごとにポッドが別れている。
既に導入しているCoreは必須ライブラリだがそれ以外は個別に指定する必要がある。

認証に関して言えば基本はAuhtなので、以下を追記することになる。

pod 'Firebase/Auth'

また、今回は扱わないがSNSなど複数のフェデレーションを利用する場合などに便利なUI(ViewControllerサブクラス)を自動生成してくれる仕組みも用意されている。その場合には、さらにpod 'FirebaseUIを追記する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む