20200118のiOSに関する記事は3件です。

【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で続きを読む

素人がiPhoneアプリを作った話<SNS機能>

やりたかったこと

  • アカウント作成(ユーザー登録)
  • 承認制のクローズドSNS
  • ユーザーによるアイコン選択
  • タイムライン表示
  • 書き込み
  • 編集
  • 削除
  • 何種類かの「いいね」機能
  • アカウント削除
  • 通報
  • ログイン
  • 自動ログイン

できたこと

  • アカウント作成(ユーザー登録)
    • ユーザー登録はAPIでWordPressにメールを登録する形になっています
    • 登録も含めてユーザー管理はプラグインのUltimate Menberで行っています
      1. メールアドレスを入力してSign Upボタンを押すとメールが送信されます
      2. 折り返しメールが届きます
      3. メールのURLからWordPressの新規登録画面へ行って登録します
  • 承認制のクローズドSNS
    • Ultimate Menberの承認制なので、管理者にメールが届き、承認作業をします(アプリ上では承認前でもログインできてしまいますが)
  • タイムライン表示
  • 書き込み
  • 削除
  • いいね機能(一部)
    • 1種類のみ
    • タップすると泣き顔のアイコンが笑顔アイコンになる
    • いいねをカウントする(ニフクラで管理)
  • アカウント削除
    • APIで削除ができなかったので、削除依頼のメールを送信してもらうようになっている
    • 削除依頼のリンクをタップするとメーラーが立ち上がる
    • メールにアカウント名と内容が自動で入る
  • 通報
    • 不適切な投稿があったときに通報できるようにアカウント削除と同じ方法でメーラーとメール本文が作られている
  • ログイン
    • ログイン画面から行うようになっている

できなかったこと

  • 承認制のクローズドSNS
    • 承認前にログインできてしまっている
  • ユーザーによるアイコン選択
    • 自分で何種類かのアイコンから選べるようにしたかったけれど、わたしの技術ではできなかった
  • 編集
    • 編集できるようにできず、上書きにしようと考えたが時間切れで断念した
  • 何種類かの「いいね」機能
    • Facebookのいいねと同じような何種類かの顔の表情のいいねができるようにしたかったができなかった
  • アカウント削除
    • APIでできるようにしたかったが、Ultimate Memberのせいかできなかった
    • 原因を突き止めて改善する時間がなかった
  • 自動ログイン
    • 一度ログインしてログイン状態を保持できるオプションを付けたかったができなかった

全体的に、技術不足と時間不足により、やりたいことができていません。でも、素人でこのSNS機能を3週間で作ったのは自分でも褒めたい!

1回目に申請した時のアプリにはSNS機能はなく、却下を受けてWordPressの画面をアプリ内で表示してSNSができるようにしました。
ここまでは却下されてから3日間で、WordPressをUltimate MemberとBuddyFormsを使ってメンバー限定のSNSにできるように、いいね機能もプラグインを使って何種類かできるようになどしていました。

2回目の却下を受け、REST APIでSNS機能をアプリとして付け、WordPressをバックエンドにするしかないと判断して、必死に調べて現在の状態にしました。知り合いのエンジニアに、もう一度却下されたらアドバイスをして欲しいと頼んでありました。

3回目の却下はSNS機能ではなく、メインの機能(メッセージ表示)がちゃちいということのようだったので、着想を得た元のアプリを指摘してこれと同じものなので却下の正当性がないと抗議し、契約に至りました。

現在は、SNSのREST APIの部分の何かの変更と、ニフクラのURL変更のために、以下の機能が使えません。

  • 新規登録
  • いいね
  • 投稿削除

早くなんとかしたいのですが、今は気力がなく放置しています。MonacaやOAuth Server、WordPressをインストールしたサーバー、ドメイン、などなど自動的に支出はあるので、どなたか作業の見積もりしていくらでならできるか教えてください。
何よりも、このアプリを使ってくださる方々の役に立つ状態にしたいと思います。

データは前回の投稿にて全て公開してあります。

よろしくお願いいたしますm(_ _)m

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