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

Swift Postman 導入(つまづいたところ全部)

Postman

APIとは
https://www.internetacademy.jp/it/programming/javascript/how-to-use-web-api.html
APIの導入はかなりめんどくさい・・らしい。
というのも自分はまだサンプルプログラム等でしかAPIを用いたことがない。
コードを書いていたときは、これ自分が欲しいAPI合っても導入で時間かかりそうだなぁとざっくり思ってました。

本題
PostmanはそのAPIの構築をサポートするツールとのこと。
どれどれやってみよう。
(ちなみに一度は上記のめんどくさいやり方でやってみることが重要だとは思ってますhttps://qiita.com/dddisk/items/09b46c65434c117f1287)

で表示される画面がこちら

とてもピンときませんね。

ということでまずはAPIについて学んでいきます
APIはHTTPのリクエスト、レスポンスとかを使う。ふむ。
https://qiita.com/r_fukuma/items/a9e8d18467fe3e04068e

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

【swift5】カウントダウンタイマーの実装

まずUIPickerViewで「何時間」「何分」「何秒」を選択してストップウォッチ作成。

そこからカウントダウンするのですが、セットした時間が全部秒になってしまっております。。。

60秒以内なら問題ないのですが1分30秒だと90秒のような感じ。

カップラーメンに使ったり筋トレのタイマーなんかにはいいのではないでしょうか。

2020年1月18現在ではこんな感じですが、修正したいと思います。

デザイン

スクリーンショット 2020-01-18 20.38.04.png

ソース全体

import UIKit

class TimerViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

    @IBOutlet weak var pickerLabel: UILabel!
    @IBOutlet weak var pickerView: UIPickerView!
    @IBOutlet weak var start: UIButton!

    var timer:Timer = Timer()
    var count:Int = 0

    let dataList = [[Int](0...24), [Int](0...60), [Int](0...60)]

    override func viewDidLoad() {
        super.viewDidLoad()
        //「時間」のラベルを追加
        let hStr = UILabel()
        hStr.text = "時間"
        hStr.sizeToFit()
        hStr.frame = CGRect(x: pickerView.bounds.width/4 - hStr.bounds.width/2, y: pickerView.bounds.height/2 - (hStr.bounds.height/2), width: hStr.bounds.width, height:hStr.bounds.height)
        pickerView.addSubview(hStr)
        //「分」のラベルを追加
        let mStr = UILabel()
        mStr.text = "分"
        mStr.sizeToFit()
        mStr.frame = CGRect(x: pickerView.bounds.width/2 - mStr.bounds.width/2, y: pickerView.bounds.height/2 - (mStr.bounds.height/2), width: mStr.bounds.width, height: mStr.bounds.height)
        pickerView.addSubview(mStr)
        //「秒」のラベルを追加
        let sStr = UILabel()
        sStr.text = "秒"
        sStr.sizeToFit()
        sStr.frame = CGRect(x: pickerView.bounds.width*3/4 - sStr.bounds.width/2, y: pickerView.bounds.height/2 - (sStr.bounds.height/2), width: sStr.bounds.width, height: sStr.bounds.height)
        pickerView.addSubview(sStr)
    }
    //コンポーネントの個数を返すメソッド
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return dataList.count
    }
    //コンポーネントに含まれるデータの個数を返すメソッド
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return dataList[component].count
    }
    //サイズを返すメソッド
    func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
        return pickerView.bounds.width * 1/4
    }
    //データを返すメソッド
    func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
        let pickerLabel = UILabel()
        pickerLabel.textAlignment = NSTextAlignment.left
        pickerLabel.text = String(dataList[component][row])
        return pickerLabel
    }

    @IBAction func startCountButton(_ sender: Any) {
        timer.invalidate()
        count = dataList[0][pickerView.selectedRow(inComponent: 0)] * 60 * 60
            +  dataList[0][pickerView.selectedRow(inComponent: 1)] * 60
            +  dataList[0][pickerView.selectedRow(inComponent: 2)]
        timer = Timer.scheduledTimer(timeInterval: 1, target:self, selector:#selector(countDown), userInfo:nil, repeats:true)
    }

    @IBAction func reset(_ sender: Any) {
        count = 0
        pickerLabel.text = "カウントダウン終了"
    }

    //タイマーから呼び出されるメソッド
    @objc func countDown(){
        count -= 1
        if(count > 0) {
            pickerLabel.text = "残り\(count)秒です。"
        } else {
            pickerLabel.text = "カウントダウン終了"
            timer.invalidate()
        }
    }
}

参考サイト

ちょいと記事が古かった

https://hajihaji-lemon.com/smartphone/swift/uipickerview_countdown/

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

【swift】強制アップデート機能用ライブラリsirenの解説と実装

環境

xcode 11.3
swift 5.1.3
CocoaPods 1.8.4

手順

完成形

スクリーンショット 2020-01-18 16.16.48.png
(github : sirenページより引用)

CocoaPodsによるライブラリのインストール

以下を追加後、ターミナルでpod installを実行

Podfile
pod 'Siren'

コードの記述

AppDeledate.swift
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

        // sirenの強制アップデート設定用関数
        forceUpdate()

        return true
    }

    // 途中省略
}

// 以下、追記
private extension AppDelegate {

    func forceUpdate() {
        let siren = Siren.shared
        // 言語を日本語に設定
        siren.presentationManager = PresentationManager(forceLanguageLocalization: .japanese)

        // ruleを設定
        siren.rulesManager = RulesManager(globalRules: .critical)

        // sirenの実行関数
        siren.wail { results in
            switch results {
            case .success(let updateResults):
                print("AlertAction ", updateResults.alertAction)
                print("Localization ", updateResults.localization)
                print("Model ", updateResults.model)
                print("UpdateType ", updateResults.updateType)
            case .failure(let error):
                print(error.localizedDescription)
            }
        }

        // 以下のように、完了時の処理を無視して記述することも可能
        // siren.wail()
    }
}

sirenの解説

使用例

以下の使用例まとめをみると、どうやって使えばいいのかがわかりやすい。

github : Siren/Example/Example/AppDelegate.swift

PresentationManagerとは

表示されるアラートの設定を行うものという認識。
呼び出し時に引数として渡すことで設定できるのは、以下のプロパティたち。

  • appName: アプリ名
  • alertTitle: アラートのタイトル
  • alertMessage: アラートのメッセージ
  • updateButtonTitle: 更新ボタンのタイトル
  • nextTimeButtonTitle: 次回更新ボタンのタイトル
  • skipButtonTitle: スキップボタンのタイトル
  • forceLanguageLocalization forceLanguage: 言語

基本的に、言語を日本語に設定すれば、他は特にいじらなくても大丈夫そう。
何も指定しない場合は、iPhoneの本体の設定言語で表示されるため、日本人以外にもターゲティングしてる場合はそちらで。

コードは以下。

github : Siren/Sources/Managers/PresentationManager.swift

RuleManagerとは

強制アップデートのルールを設定するものという認識。

ruleに関して

更新頻度の種類

.immediately .daily .weekly
毎アプリ起動時 1日1回 1週間に1回

アラートタイプの種類

.force .optional .skip .none
更新ボタンのみ 更新ボタン・次回起動時ボタン 更新ボタン・次回起動時ボタン・このアップデートをスキップするボタン アラートを表示しない 

更新頻度・アラートタイプの組み合わせ表現

annoying critical default hinting persistent relaxed
更新頻度 .immediately .immediately .daily .weekly .daily .weekly
アラートタイプ .option .force .skip .option .option .skip

一括で指定する場合

RulesManager(globalRules: .critical)

それぞれ指定する場合

RulesManager(
    majorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .force), // A.b.c.d
    minorUpdateRules: Rules(promptFrequency: .immediately, forAlertType: .optional), // a.B.c.d
    patchUpdateRules: Rules(promptFrequency: .daily, forAlertType: .skip), // a.b.C.d
    revisionUpdateRules: Rules(promptFrequency: .weekly, forAlertType: .skip) // a.b.c.D
)

RuleManagerのコードは以下。

github : Siren/Sources/Managers/RulesManager.swift

参考資料

強制アップデートで検索すると検索するとここら辺の記事が出てくる

teckmemo : 簡単にアプリのアップデート通知を行う
Sirenを使って iOSアプリに強制アップデート実装するよ
狛ログ : iOSアプリのアップデートをSirenを使って実装する。

以下がsirenのソース。これを読んだ方がわかりやすい

github : ArtSabintsev/Siren

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

iOSアプリ内にAPIアクセスキーを保持するベストプラクティス(Swift)

はじめに

iOSアプリ開発において、APIアクセスキーの管理方法に疑問を持ちました。

Twitterでいろいろ教えていただき、自分の中でベストプラクティスを考えたので紹介します。

結論

先に結論を記載します。

  • CocoaPodsを使っている、かつライブラリの追加に抵抗がない → cocoapods-keysを導入する
  • CocoaPodsを使っていない、またはライブラリの追加に抵抗がある
    • リポジトリがprivate → .swift ファイルへ直接書く
    • リポジトリがpublic → CI時に .swift ファイルへ注入する

解説

結論のみではわからないので、解説します。

cocoapods-keys

CocoaPodsのプラグインで、機密情報をキーチェーンアクセスで管理するツールです。
https://github.com/orta/cocoapods-keys

私は使ったことがないのですが、 こちらの記事 を読む限り、非常に便利なツールです。
CocoaPodsを使っているなら、導入を検討してもよさそうです。

リポジトリがprivate

CocoaPodsを使っていない、またはライブラリの追加に抵抗がある場合は、手動で対応する方法が考えられます。

リポジトリがprivateの場合、 .swift ファイルへ直接書いて問題ないと思います。
理由は当たり前ですが、privateリポジトリからソースが漏れることは(基本的に)ないためです。

AccessTokens.swift
let fooAccessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" // 直接書いてOK

注意点として、 .plist ファイルなどのリソースに保持すると、エンドユーザーが読み取れてしまいます
.swift ファイルはコンパイルされますが、リソースはそのまま .ipa ファイル内に保持されます。
もしリソースで機密情報を管理していたら、すぐに対応すべきです。

リポジトリがpublic

APIアクセスキーを使っているOSSなど、リポジトリがpublicな場合も考えられます。
その場合はソースに直接書けないので、CI時に注入します。

①ローカルの .swift ファイルにアクセスキーを記述する

privateリポジトリと同様、 .swift ファイルをプロジェクト内に作成してアクセスキーを記述します。

AccessTokens.swift
let fooAccessToken = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

これでローカルでの開発は問題なく行えます。

②アクセスキーを含むファイルをGitの管理外にする

このままリモートリポジトリにプッシュすることはできないので、 .gitignore に対象ファイルを追記します。

.gitignore
+ AccessTokens.swift

③プロジェクトファイルのみプッシュする

対象ファイルを追加したプロジェクトファイルのみリモートリポジトリにプッシュします。

④CIサービスにアクセスキーを保持する

ほとんどのCIサービスには機密情報を暗号化して保持する機能があると思うので、活用します。
GitHubの場合、 Secrets という機能があるので、そこでアクセスキーをシークレットとして保持します。

スクリーンショット_2020-01-18_0_21_30.jpg

⑤CI時にアクセスキーを注入する

保持したアクセスキーをCI時に注入します。
GitHub Actionsの場合、以下のようなステップをビルド前に実行します。

main.yml
- name: Inject access token
  run: echo "let fooAccessToken = \"${{ secrets.FOO_ACCESS_TOKEN }}\"" > ./Foo/AccessTokens.swift

これでCI時にアクセスキーを .swift ファイルへ注入できます。

⑥Makefileを用意する

自分以外の開発者のために、Makefileを用意すると親切です。
以下に例を記載します。

Makefile
.PHONY: inject-tokens

inject-tokens:
    echo "let fooAccessToken = \"${FOO_ACCESS_TOKEN}\"" > ./Foo/AccessTokens.swift

コマンドをREADMEに書くことで、他の人もローカルで開発しやすくなります。

README.md
Run `make inject-tokens FOO_ACCESS_TOKEN={Foo Access token}` .

おわりに

これでiOSアプリ開発で安全にAPIアクセスキーを管理できます!

もし他にベストプラクティスを持っている方がいたら、教えていただけると嬉しいです。

参考リンク

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