20190707のiOSに関する記事は4件です。

【Swift】 RealmSwiftのマイグレーション(データ引き継ぎ有)方法

前置き

ネットの情報を色々見て、公式の記述サンプルも見たんですが、Realmのマイグレーションがうまく行かない。より正確には、schemaVersionを更新して、レコードフォーマットも更新されて、アプリが稼働することは確認できるのに、マイグレーション前のデータが引き継がれない。
とにかく「これでマイグレーションできました」っていうブログ等の書き方を片っ端から試していって、結果、下記の書き方でうまく行ったので備忘録として残します。

開発環境

端末:MacBook Pro/MacOS 10.14.5(Mojave)
Xcode:10.2.1
Swift:5

実装内容

ソースサンプル

RealmSwift.swift
import Foundation
import RealmSwift

class myRoutine: Object {
    @objc dynamic var column1 = String()
    @objc dynamic var column2 = String()
    @objc dynamic var column3 = String()
    @objc dynamic var column4 = String()
    @objc dynamic var column5 = String()
}

func realmMigration() {
    // Realmマイグレーションバージョン
    // レコードフォーマットを変更する場合、このバージョンも上げていく。
    let migSchemaVersion: UInt64 = 1

    // マイグレーション設定
    let config = Realm.Configuration(
        schemaVersion: migSchemaVersion,
        migrationBlock: { migration, oldSchemaVersion in
            if (oldSchemaVersion < migSchemaVersion) {
    }})
    Realm.Configuration.defaultConfiguration = config
}

ライフサイクルの早い段階でマイグレーション処理 realmMigration() を呼び出す

ViewController.swift
class ViewController: UIViewController {
    ...

    // 自分の場合は、作ってるアプリのライフサイクルで一番早いのが
    // awakeFromNib()だったのでここに書いています。
    override func awakeFromNib() {
        realmMigration()

        // realmを使う時のおまじない
        realm = try! Realm()

        ...
    }
    ...
}

感想、その他メモ

いろいろ試して、(そもそも何でいろいろな書き方があるのか、という話ですが)なんとかデータ引き継ぎできる書き方が見つかってよかったです。
もっと余裕と知識があれば、ダメだった書き方のダメな理由も調べたいんですが、それはまた別の機会に。

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

【Swift】 UITextFieldに文字数制限を設ける方法

前置き

少し久しぶりのQiita投稿。
(自作アプリ公開に向けて日々いろいろやってたんですが、細かいトコの修正やら稼動確認してると、今までの反復に当たる部分でぶつかる壁が多くて、あまり目新しい収穫がなかったので。)

さて表題の件。
ブログ等を参考にしても、エントリ時期から時間が経ってSwiftの書き方にギャップが出たり、(日本語入力もカバーするか、という点で)実装の仕方に違いがあったりで、マネしてみても自分の想定する実装にならないコトが多い。
最終的に、いささか記事のエントリ時期としては古くて、少しSwift5に対応するために修正は必要だったけれど、下記「参考」記載のURLのブログエントリが自分なりに一番取り込みやすい内容でした。

開発環境

端末:MacBook Pro/MacOS 10.14.5(Mojave)
Xcode:10.2.1
Swift:5

実装内容

「こういうコトやりたい」だけ前置きに書いていても、
実際、下記のソースで何ができるようになったかが伝わりづらいかもしれないので、
どんなコトがどんな風にできるのか、できるだけ書くようにしようかな、と。
(今回は画面イメージもないので)

・UITextFieldに対する、入力文字数の制限。
・日本語入力(入力→変換→確定、というプロセス)でもOK。
 入力確定時に制限文字数以上であれば切り捨てる。
・あくまで制限文字数のある項目1つに対する入力制限の実装。
 (複数の項目で、それぞれ制限文字数が異なるような場合、別途カスタマイズは必要。)

ソースサンプル

ViewController.swift
class ViewController: UIViewController, UITextFieldDelegate {

    // 入力チェックを行うUITextField
    @IBOutlet weak var myTextField: UITextField!
    // 入力可能な最大文字数
    let maxLength: Int = 5

    override func viewDidLoad() {
        super.viewDidLoad()

        myTextField.delegate = self        

        // myTextFieldの入力チェック(文字数チェック)をオブザーバ登録
        NotificationCenter.default.addObserver(self,
                                               selector: #selector(textFieldDidChange(notification:)),
                                               name: UITextField.textDidChangeNotification,
                                               object: myTextField)
    }

    // オブザーバの破棄
    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    // 入力チェック(文字数チェック)処理
    @objc func textFieldDidChange(notification: NSNotification) {
        let textField = notification.object as! UITextField

        if let text = textField.text {
            if textField.markedTextRange == nil && text.count > maxLength {
                textField.text = text.prefix(maxLength).description
            }
        }
    }

        ...    
}

参考

・雑食プログラミング備忘録
 [iOS] UITextFieldの最大文字数を設定する
 http://chicketen.blog.jp/archives/50002171.html

感想

会社での担当業務としてHTMLを触るコトが多いのもあって、UITextFieldの文字数制限くらいStoryboardでチャチャっと設定できると思っていたトコロから、気づいたら実装・稼動確認、そしてここへアウトプットするのも合わせるとフツーに2、3時間経ってました。
英語入力だけの文字数制限だけなら割とすぐにできたトコロから、日本語に対応したモノへ発展させるのにけっこう悩まされました。

Swift2,3の頃の記事が多くて、Swift4,5だと書き方が全然違って来てる(もしくは同じ挙動にならない)っていうコトに割と出くわすので、自分自身がSwift5で色々試して、そのギャップを埋めるコトにささやかながら貢献できれば嬉しいです。

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

iOS13時代のSVG表示設定方法

デザイナーからアイコン素材としてSVGが納入される機会が出てきたこの頃。
WWDC2019のIntroducing SF Symbolsの中でAVOID rasterizing!と言ってるくらいなので、
SS00003.png
iOS13時代のアプリでSVGを扱う方法は何が最適か試行錯誤してみました。
(Xcode 11 beta3, Deployment Target = iOS 12.0)

前提: SVGKitなどのライブラリは使いません。

SVGのサイズ

SVGは拡大しても大丈夫なはずなのですが、
:o: SVGは3x画像のサイズに設定してください。
:x: 1x画像サイズにすると、2x画像、3x画像がギザギザになります。 (iOS12以下にて)

デザイナーへ発注するときにお願いするのが理想的ですが、
「SVGは拡大してもギザギザにならないんだからサイズ指定するとかおかしいでしょ!」と些細な事で揉めがちなので:cloud_rain:
検索すればフリーのツールやサイトを見つけることができます。

SVGのファイル形式

:small_red_triangle:従来通りPDFに変換してください。
デザイナーへ発注するときにお願いするのが理想的ですが、
(略)
検索すればフリーのツールやサイトを見つけることができます。

SVGの設定

  1. Asset CatalogにImage Setを作成する。
  2. Resizing Preserve Vector Dataにチェックする。
  3. Scalesは Individual Scalesにセットする。
  4. SVGを1xにセットする。(2x、3xは空欄)

:o: SVGはコードで表示すること。

imageView.image = UIImage(named: "svgSample")

:x: Storyboardで画像を設定しただけではギザギザになります。

S_SVG_IMAGE_SET.png

比較

左からiOS13、iOS12(3xサイズSVGをセット)、iOS12(1xサイズSVGをセット)
SS0001.png

iOS13以降しかTargetにしないとき

:o: SVGサイズは気にしなくていいです。
:small_red_triangle:PDFに変換してください。
:small_red_triangle:コードで表示してください。
1. Asset CatalogにImage Setを作成する。
2. Resizing Preserve Vector Dataにチェックする。
3. Scalesは Single Scaleにセットする。
4. SVGをAllにセットする。
SS0002.png
:o: 2x、3xは空欄でいいの?とか3xサイズなのに1xにセットするの?とか気にしなくていい。

感想

iOS13のベクター画像の扱いが素晴らしくなっている。:clap:
早く変換なしでSVGをセットできるようにしてほしい。
iOS12以下を切ってしまいたい誘惑が〜:fire::jack_o_lantern::fire:


Xcode 11 beta3を元に作成しています。後日変更される可能性があります。

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

TDDでじゃんけんアプリ作った!

初めまして!きゅうすけと申します!
iOSエンジニアの私が、自社の定例イベント 「TokyoUppersBoost」 で学んだ基礎の部分をまとめたものです!
メモ感覚ではありますが、これ違うぞってことがあったらぜひ教えてくださいー!(● ˃̶͈̀ロ˂̶͈́)੭ꠥ⁾⁾

今回は、「iOSにおけるunit testハンズオン」という企画でした。

概要

  • TDDについての座学まとめ
  • Unit Testハンズオンまとめ

早速座学のまとめからしていきます!

TDD とは

テスト駆動開発 (Test-Driben Deberopment)のことです。

普通の開発は
実装 → テストコードを書く → テスト
で進めていきます。

それに対して TDD
テストコードを書く → テストコードが通る実装とリファクタリング
普通の開発と逆の流れで進めていきます。

メリットとデメリット

メリット

  • テストコードがドキュメントとして残せる
  • 仕様を確認しやすい
  • リファクタリング時の心理的な負担がへる
  • ペアプロにもってこい(1人がテストコード、1人が実装)

デメリット

ほぼ二倍のコードを書くため、
2倍コードをかけるように、または1/2の量でコードをかけるようになる必要がある

実装サイクル

Red → Green → Refactor のサイクルで行う

・Red
絶対に失敗するテストコードを書く
テストが通らないものを確認する
└ 「このテストコードを書いて通ったらおかしい」を知る

・Green
テストの通る最低限の実装をする

・Refactor
テストをオールグリーンにした状態で、ソースコードを綺麗な状態にして見やすくする

Unit Testハンズオン開始!

今回のハンズオンで作ったのは、じゃんけんアプリです!
↓こちらのgithubをクローンして作成していきます!
https://github.com/mht-mikiya-okugawa/SampleJanken1.git

ハンズオンの流れ

テストかく (Red)

コンパイルエラーを解消する

テストが通る処理をかく (Green)

RedとGreenを繰り返す

Refactor

ViewControllerでViewの調整

Red / Greenテスト

以下テストのコードになります!オールグリーンの状態です!

SampleJanken1Tests.swift
import XCTest
@testable import SampleJanken1

class SampleJanken1Tests: XCTestCase {

    override func setUp() {
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }

    //パターンscissors
    //あいこ
    func testYouScissorsDrow() {
        let yourHandScissors: Int = 1
        let pcScissors: Int = 1
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandScissors)
        XCTAssertEqual(rps, "あいこ")
    }
    //かち
    func testYouScissorsWin() {
        let yourHandScissors: Int = 1
        let pcPaper = 2
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandScissors)
        XCTAssertEqual(rps, "あなたの勝ち")
    }
    //負け
    func testYouScissorsLoose() {
        let yourHandScissors: Int = 1
        let pcRock = 0
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandScissors)
        XCTAssertEqual(rps, "あなたの負け")
    }

    //パターンRock
    func testYouRockDrow() {
        let yourHandRock: Int = 0
        let pcRock: Int = 0
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandRock)
        XCTAssertEqual(rps, "あいこ")
    }
    //かち
    func testYouRockWin() {
        let yourHandRock: Int = 0
        let pcPaper: Int = 1
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandRock)
        XCTAssertEqual(rps, "あなたの勝ち")
    }

    //負け
    func testYouRockLoose() {
        let yourHandRock: Int = 0
        let pcScissors: Int = -1
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandRock)
        XCTAssertEqual(rps, "あなたの負け")
    }


    //パターンぱー
    //あいこ
    func testYouPaperDrow() {
        let yourHandPaper: Int = 2
        let pcPaper: Int = 2
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcPaper, you: yourHandPaper)
        XCTAssertEqual(rps, "あいこ")
    }
    //かち
    func testYouPaperWin() {
        let yourHandPaper: Int = 2
        let pcRock: Int = 3
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcRock, you: yourHandPaper)
        XCTAssertEqual(rps, "あなたの勝ち")
    }
    //負け
    func testYouPaperLoose() {
        let yourHandPaper: Int = 2
        let pcScissors: Int = 1
        let janken = Janken()
        let rps = janken.rockPeparScissors(pc: pcScissors, you: yourHandPaper)
        XCTAssertEqual(rps, "あなたの負け")
    }

    //ランダムにCPに手を出してもらう-1~3の間が帰ってきてほしい
    func testRandomCpHand() {
        let janken = Janken()
        // 100回テストしてくれる → コンソールで結果確認
        for num in 0...100 {
            let result: Int = janken.randomHand()
            print ("Result" + String(num) + "回目" + String(result))
            XCTAssertEqualWithAccuracy(1.0, Float(result), accuracy: 2.0,"成功")
        }
    }
}
Jsnken.swift
import Foundation

open class Janken {
    public func rockPeparScissors(pc: Int, you: Int) -> String {
        let paper1: Int = -1
        let rock1: Int = 0
        let scissors1: Int = 1
        let paper2: Int = 2
        let rock2: Int = 3

        if you == scissors1 {
            if pc == scissors1 {
                return "あいこ"
            } else if pc == paper1 || pc == paper2 {
                return "あなたの勝ち"
            } else if pc == rock1 || pc == rock2 {
                return "あなたの負け"
            } else {
                return ""
            }
        } else if you == rock1 {
            if pc == rock1 || pc == rock2 {
                return "あいこ"
            } else if pc == scissors1 {
                return "あなたの勝ち"
            } else if pc == paper1 || pc == paper2 {
                return "あなたの負け"
            } else {
                return ""
            }
        } else if you == paper2 {
            if pc == paper2 || pc == paper1 {
                return "あいこ"
            } else if pc == rock1 || pc == rock2 {
                return "あなたの勝ち"
            } else if pc == scissors1 {
                return "あなたの負け"
            } else {
                return ""
            }
        } else {
            return ""
        }
    }

    public func randomHand() -> Int {
        // -1〜4よりも小さいメソッドを適当に返してくれる
        return Int.random(in: -1..<4)
    }
}

Refactor

次がJsnken.swiftを、じゃんけんの法則にそってリファクタしたコードです!
public func rockPeparScissors(pc: Int, you: Int) -> String {}の中身を書き換えます!

import Foundation

open class Janken {
    // リファクタリング
    public func rockPeparScissors(pc: Int, you: Int) -> String {
        let jadge: Int = (you - pc + 3) % 3

        switch jadge {
        case 0:
            return "あいこ"
        case 1:
            return "あなたの負け"
        case 2:
            return "あなたの勝ち"
        default:
            return ""
        }
    }

    public func randomHand() -> Int {
        // -1〜4よりも小さいメソッドを適当に返してくれる
        return Int.random(in: -1..<4)
    }
}

View調整

import UIKit

class ViewController: UIViewController {

    @IBOutlet var pcHandImgView: UIImageView!
    @IBOutlet var resultLabel: UILabel!
    @IBOutlet var rockBtn: UIButton!

    @IBOutlet var scissorsBtn: UIButton!
    @IBOutlet var paperBtn: UIButton!
    let rock: Int = 0
    let scissors: Int = 1
    let paper: Int = 2
    let janken = Janken()



    override func viewDidLoad() {
        super.viewDidLoad()

        rockBtn.addTarget(self, action: #selector(ViewController.youHandRock), for: .touchUpInside)
        scissorsBtn.addTarget(self, action: #selector(ViewController.youHandScissors), for: .touchUpInside)
        paperBtn.addTarget(self, action: #selector(ViewController.youHandPaper), for: .touchUpInside)

    }

    // グーを選んだ時
    @objc func youHandRock() {
        // テストで作ったランダムハンドのメソッドを呼ぶ
        let pcHand: Int = janken.randomHand()
        // ランダムハンドの結果を使って、画像を切り替える
        pcImageChange(pcHand: pcHand)

        // ボタンの背景色を変える
        rockBtn.backgroundColor = UIColor.blue
        scissorsBtn.backgroundColor = UIColor.white
        paperBtn.backgroundColor = UIColor.white

        // テストで作った、rockPeparScissorsけメソッドを呼んで、結果をresultLabelに入れる
        resultLabel.text = janken.rockPeparScissors(pc: pcHand, you: rock)

    }
    // チョキを選んだ時
    @objc func youHandScissors() {
        // テストで作ったランダムハンドのメソッドを呼ぶ
        let pcHand: Int = janken.randomHand()
        // ランダムハンドの結果を使って、画像を切り替える
        pcImageChange(pcHand: pcHand)

        // ボタンの背景色を変える
        rockBtn.backgroundColor = UIColor.white
        scissorsBtn.backgroundColor = UIColor.blue
        paperBtn.backgroundColor = UIColor.white

        // テストで作った、rockPeparScissorsけメソッドを呼んで、結果をresultLabelに入れる
        resultLabel.text = janken.rockPeparScissors(pc: pcHand, you: scissors)
    }

    // パーを選んだ時
    @objc func youHandPaper() {
        // テストで作ったランダムハンドのメソッドを呼ぶ
        let pcHand: Int = janken.randomHand()
        // ランダムハンドの結果を使って、画像を切り替える
        pcImageChange(pcHand: pcHand)

        // ボタンの背景色を変える
        rockBtn.backgroundColor = UIColor.white
        scissorsBtn.backgroundColor = UIColor.white
        paperBtn.backgroundColor = UIColor.blue

       // テストで作った、rockPeparScissorsけメソッドを呼んで、結果をresultLabelに入れる
        resultLabel.text = janken.rockPeparScissors(pc: pcHand, you: paper)
    }

    // グーチョキパーを変えるメソッド
    func pcImageChange(pcHand: Int) {
        if pcHand == -1 || pcHand == 2 {
            pcHandImgView.image = UIImage(named: "paper")
        } else if pcHand == 0 || pcHand == 3 {
            pcHandImgView.image = UIImage(named: "rock")
        } else {
            pcHandImgView.image = UIImage(named: "scissors")
        }
    }

}

これでアプリ完成です!!
色変えて遊んだり( ^ω^ )
スクリーンショット 2019-07-06 23.50.51.png
スクリーンショット 2019-07-06 23.50.58.png
スクリーンショット 2019-07-07 0.08.04.png



↓ 以下講師の方の記事を参考に作成しました!ぜひリポジトリをクローンしてやってみてください!
https://qiita.com/MHTcode_micky/private/bfbc80065d5e02cea849

終わりに

「このテストコードを書いて通ったらおかしい」を知るを把握することで
何かエラーが起きたときにも対処しやすいし、アプリの品質も担保できるというところがいいと思いました。
普通の開発のテストしかやったことがなかったので、今回できてよかったです!(*゚▽゚)ノ



私が参加している勉強会 「TokyoUppersBoost」への参加者さん大募集中です!!
一緒にiOSについて学びましょう・:*+.(( °ω° ))/.:+

↓私が作ったLPなのでよかったら見てください!٩( 'ω' )و
TokyoUppersBoostとは?

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