20200208のiOSに関する記事は6件です。

#24 チュートリアル的な初回起動時のみ表示されるViewController1例

はじめに

個人のメモ程度の出来なのであまり参考にしないで下さい.

環境

Xcode:11.2.1
Swift:5.1.2
2019/11

part1

Main.storyboardsegueを選択する.

スクリーンショット 2020-02-08 午後9.17.16.png

part2

segueを選択した状態で,Attributes inspectorIdentifierに任意の文字列を入れる.

スクリーンショット 2020-02-08 午後9.17.25.png

part3

AppDelegate.swiftapplication(_:didFinishLaunchingWithOptions:)に4行追加する.

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

        //この下の4行を追加
        let ud = UserDefaults.standard
        let firstLunchKey = "firstLunch"
        let firstLunch = [firstLunchKey: true]
        ud.register(defaults: firstLunch)

        return true
    }

part4

ホーム画面のViewControllerStartViewController.swiftviewDidAppear(_)に7行追加する.

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        //この下の7行を追加
        let ud = UserDefaults.standard
        let firstLunchKey = "firstLunch"
        if ud.bool(forKey: firstLunchKey) {
            ud.set(false, forKey: firstLunchKey)
            ud.synchronize()
            self.performSegue(withIdentifier: "toAboutApp", sender: nil)
        }
}

part5

チュートリアル画面のViewControllerAboutAppViewController.swiftの「閉じる」ボタンのアクションにに1行追加する.

@IBAction func toback(_ sender: UIButton) {

        //この下の1行を追加
        self.dismiss(animated: true, completion: nil)
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JenkinsとfastlaneでiOSビルド環境を構築@Mac mini

前置き

AndroidをEC2インスタンス(Linux)のJenkinsでビルドしていたので、同じようにiOSもビルドしたい。
しかしAndroidはソース書いてビルドもしているのでなんとかJenkinsの設定もできるが、iOS開発はほとんどわからない。自分の開発環境にiOS開発環境をセットアップしたぐらいだ。

そんな状態のほとんどAndroid開発しかしたことない自分でもfastlaneを使えばiOSも簡単にビルド自動化ができたので、Jenkins × fastlane × Mac miniの構成をご紹介します。

構成

iOSのビルドにはMacが必要なので、そのままLinuxのインスタンス上のJenkinsでは出来ない。
そのため、JNLPというJavaのサービスを利用してEC2のJenkinsとローカルの机の上のMac miniちゃんを連携してビルドできるようにする。

jenkins_ios_build_mod_for_qiita.png

  • iOSをビルドするMac miniにはJenkinsはセットアップする必要は無い。fastlaneを使ってiOSのビルドに詳しくなくても楽チン
  • ビルド自体の他に以下も行ってCI/CD(CDだけかな?)を回して、社内でipaの検証サイクルを効率化できた。
    • ビルドしたらipaをS3にアップロードしてリンクをBacklogにコメントする
    • ビルド中に何かエラーになったらslackのチャンネルに通知してすぐ気づけるように

Macへfastlaneのセットアップ

Rubyのセットアップ

  • fastlaneはRubyのgemで提供されているのでRubyが必要
  • rbenvでRuby2.5.1をシステム全体にセット (バージョンは適当)

fastlaneのセットアップ

上記ドキュメントを参照してプロジェクトのルート(xxxxx.xcodeprojが存在するディレクトリ)で

$ fastfile init

によりFastfile, Appfileを作成してソース管理する

fastlaneのgemインストール

$ bundle install —path vendor/bundle

でGemfileに記述してプロジェクト毎にセットしていると毎回のビルドでfastlaneをインストールしないといけないので、以下でシステムに直接インストールする

$ gem install fastlane

ビルド時のタイムアウトとリトライを調整する

デフォルトの設定だと以下ビルド情報を表示するコマンドでタイムアウトとリトライの上限を超えてたまにエラーになってしまう場合がある。

xcodebuild -showBuildSettings -workspace ./xxxxx.xcworkspace -scheme xxxxx

デフォルトだと10秒と4回の上限のようなので、Fastfileに以下のように設定しエラーとならないように

ENV["FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"] = "180"
ENV["FASTLANE_XCODEBUILD_SETTINGS_RETRIES"] = "10"

JenkinsへのMac miniのslave登録

EC2インスタンスで起動しているJenkinsへローカルの机の上にあるMac miniをslaveとして登録する。

  • Jenkinsの管理 -> ノードの管理 -> 新規ノード作成 -> Permanent Agentを選択してOK
  • 以下必要な項目を記入して保存
メモ
同時ビルド数 1 2にすると並行して2つ同時にビルドできる?
リモートFSルート Users/jenkins/jobs/ Mac側のルートディレクトになる場所を指定する
ラベル mac-mini(識別するラベル) ビルドするジョブを作った時にこのラベル名を指定してビルドするマシンを指定する
用途 「このマシンを特定ジョブ専用にする」を選択 iOSのビルドしかしないのでこの設定にする
起動方法 「 Launch agent via Java Web Start 」を選択 この方法がJavaのJNLPという通信で↓のjarファイルを実行して通信を開始する
可用性 「keep this agent online as much as possible 」を選択

登録後にagent.jarがダウンロードできるのでMac miniにセットして、
表示される以下コマンド(Jenkinsが作成してくれる)を実行し、Jenkinsと同期状態(ビルド実行状態が待機中)となれば成功

java -jar /Users/jenkins/jobs/agent.jar -jnlpUrl http://xxx.xxx.xxx/computer/mac-mini/slave-agent.jnlp -secret [シークレットコード] -workDir "/Users/jenkins/jobs" -failIfWorkDirIsMissing

※ 設定で上記の項目を変更した場合、agent.jarとコマンドも変更になるので都度ダウンロードとコマンドの変更を行う

Jenkinsマスター(EC2インスタンスで起動している)へのポート設定

このJNLPのサービスがJenkinsと通信を行うため、JenkinsのJNLPに使用するポートの固定とEC2インスタンスに設定しているセキュリティグループのポート設定を行う

  • ポート固定
    • Jenkinsの管理 -> グローバルセキュリティの設定 -> Agents
      • -> TCP port for JNLP agents を固定、40790 を指定
      • -> Agent protocols で Java Web Start Agent Protocol/4 (TLS encryption) を指定
  • セキュリティグループ Mac miniのIPアドレス(インターネット向け)のJNLPのポート(40790)を通すようにする。
タイプ プロトコル ポート範囲 ソース 説明
カスタム TCP ルール TCP 40790 xxx.xxx.xxx.xxx/32 port for iOS build MacMini to Master Jenkins

MacMiniの自動起動設定

MacMini起動時にJNLP起動コマンドを自動で実行するようにlaunchctlに登録する
Fastlaneやaws s3コマンドへパスが通っていないと実行できないので、plistへPATHもセットしておく
- 設定ファイル

/Users/jenkins/Library/LaunchAgents/com.xxxx.com.ci.plist

システム環境設定 -> 省エネルギー -> スケジュールでMacの自動起動、終了を行うようにする
平日の09:30起動、21:00終了としている

メモ

JenkinsでMacMiniの状態がオフラインになることがある。

Jenkinsのビルド実行状態 -> mac-mini がオフラインになり以下のエラーが表示される

コネクションが切断されました。java.nio.channels.ClosedChannelException

回避策として
Macのシステム環境設定 -> 省エネルギー -> ディスプレイがオフのときにコンピュータを自動でスリープさせないのチェックを入れているが、またオフラインになる場合があるかもしれないので、
その場合はターミナルで以下、launchctlを再起動する

  • launchctlを止める方法
launchctl unload  /Users/jenkins/Library/LaunchAgents/com.xxxx.com.ci.plist
  • launchctlを起動する方法
launchctl load  /Users/jenkins/Library/LaunchAgents/com.xxxx.com.ci.plist
  • Jenkins本体が落ちた場合
    • Jenkins本体のEC2がスポットインスタンスの価格高騰などで落ちた場合、Jenkins本体が再び上がったら自動的に接続するのでMac mini側は何もしなくてよい
    • Jenkins本体が上がってしばらくしてもオフラインの場合は、Mac mini側を再起動してみる。

aws S3へのアップロード

  • python3とawscliのインストール aws s3 のコマンドでビルドしたipaをs3へアップロードする awsアカウントのcredentialをセットしてawsコマンドを使えるようにする(省略)

backlogへのリンクのポスト

BacklogのAPIを実行できるRubyのgemをシステムにインストールしてbacklogのチケットへコメントするスクリプトを実装
https://github.com/emsk/backlog_kit

$ gem install backlog_kit

slack通知

ビルドエラーの際、slackのチャンネルへ通知する
Fastfileのslackアクションにエラー時に通知するように設定できる

プロビジョニングが更新されたら

Mac miniのプロビジョニングファイルはfastfileに記載した自動更新オプションにより自動で更新されるはずだが、念の為以下のプロビジョニングファイルを削除しておく。(ビルド時に新しく取得しなおすように)

~/Library/MobileDevice/Provisioning Profiles/
  • fastfileのプロビジョニング自動更新オプション
xcargs: "-allowProvisioningUpdates",
  • キャッシュが原因でビルドが失敗していると思ったら 以下パス直下のファイルを全て消してみる。
/Users/jenkins/Library/Developer/Xcode/DerivedData

fastfileで以下のようにしてクリアしていれば、毎回のビルドで消えているはずだが、ビルド失敗するようなら自分で消してみる

  # delete Xcode Derrived Data
  clear_derived_data

※↑のプロビジョニングファイルも含めて毎回ビルド毎に rm するようにしたらいいかもしれない

fastlaneのアップデート

ひんぱんにアップデートされ、iOSの新バージョンリリース後にビルドがうまくいかなくなった場合は、リリースノートを見ると対応されていることがあったので、バージョンアップ手順は確認しておきたい。

$ gem list | grep 'fastlane'
commander-fastlane (4.4.6)
fastlane (2.126.0)
  • 以下コマンドでアップデート
$ sudo gem install fastlane
  • アップデート後のバージョン確認 -> 2.134.0になった
$ gem list | grep 'fastlane'
commander-fastlane (4.4.6)
fastlane (2.134.0, 2.126.0)
  • 元のバージョンに戻したい場合、以下でいけるはず
$ gem install fastlane -v “2.126.0”
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SwiftPMで�Quickを入れたら"No such module 'Quick'" というエラーが出た話

iOSテスト全書を読んでいると、第5章に「BDDによるアプリ開発」というQuick/Nimbleを利用したチュートリアルがあったので、早速やろうとしたらライブラリ導入の時点でつまづきました。

初歩的すぎて呆れる方もいるかもしれませんが、初心者の自分はこれで3時間以上溶かしてしまったので、同じ様な初心者の方にお役に立てれば幸いです。Qiita初投稿のため、内容に誤り等あればコメントでご指摘いただけると助かります。

ちなみに、簡単にSwiftPMの話もしています。

環境

MacOS Mojave 10.14.6
Xcode11.2.1
Swift5

まずライブラリを入れる

「iOSテスト全書」内ではCocoaPodsとCarthageでの導入手順が記載されていますが、私はSwiftPMで入れてみました。Xcode11からはGUIでパッケージ管理できるので便利ですね。
まずはXcodeのメニューから[File]→[Swift Packages]→[Add Package Dependency...]を選びます。
スクリーンショット 2020-02-08 17.22.00.png

すると、githubのリポジトリを検索するウインドウが出てくるので、お目当の「Quick」を検索します。ちなみに、github以外にあるものでもURLを指定すればパッケージを入れることができるそうです(私は未検証)。
スクリーンショット 2020-02-08 17.16.29.png

バージョンやブランチを聞かれます。今回は特にこだわらないのでそのままNextを押します。
スクリーンショット 2020-02-08 17.17.39.png

次は、ライブラリをどのターゲットに対して追加するかを聞かれます。デフォルトでは実際のソース(今回はqiitasample)が選ばれていますが、Quickはテストコードで使いたいライブラリなので、忘れずにテストコードの方(qiitasampleTests)を選びましょう。
スクリーンショット 2020-02-08 17.27.19.png

すると、Xcodeの左側に追加したパッケージと、その依存関係にあるパッケージが追加されます。Quickを入れただけでSwiftPMがNimbleも落としてきてくれたみたいですね。
スクリーンショット 2020-02-08 17.29.33.png

いきなりエラー

早速Quick/Nimbleを使ってテストコードを書いていきましょう!まずはQuickをimportして...
スクリーンショット 2020-02-08 17.36.45.png
それらしいものがサジェストされません。もう不穏ですね。
とりあえずQuickと手打ちしてビルド(Controll+B)してみると...
スクリーンショット 2020-02-08 17.36.59.png
No such module Quick
と言われてしまいました。いや、あるじゃん。
この後プロジェクトをクリーンしてみたり、エラー名でググってみたりしましたが、それらしい解決法に出会えずハマってしまいました...

解決法

どうにかできないかと思いXcodeのメニューを見ていると、[Product]の中に[Build for...]という項目を見つけました。よく見ると...
スクリーンショット 2020-02-08 17.41.38.png
Build For Testingがある!ここでようやく、Controll+Bではテストコードに対するビルドが行われていないことに気付きました。確かにXcode上部のバーにQuickがビルドされているような表示は出ていなかった気がする。
Build For Testingを実施すると、QuickやNimbleにもビルドがかかり、無事エラーも解消されました。

まとめ

  • テストコードに対してパッケージを追加した時は、Build For Testingを実行する。
  • ググってもわからないからといって諦めず、メニューをよく見てIDEの機能をよく確認する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dyld: Library not loaded: @rpath/Realm.framework/Realm

Xcode 11.3.1 になってからなのか、iOS 13.3.1 になってからなのか不明だが、実機の場合になぜか下記のエラーが出るようになった。(シミュレーターはOK)

dyld: Library not loaded: @rpath/Realm.framework/Realm

下記を参考に Podfile を変更して動くようになりましたが、あまり理解できていないのでやる方は自己責任でお願いします。バグなのかは不明ですが、Apple Developer Program に登録していない場合になるみたいです。

https://github.com/Alamofire/Alamofire/issues/3051

  • use_frameworks! をコメントアウトして変わりに use_modular_headers! にして update。
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'MyApp' do
  # Comment the next line if you don't want to use dynamic frameworks
  # use_frameworks!
  use_modular_headers!

  # Pods for MyApp
  pod 'RealmSwift'
end
  • プロジェクトファイルの Frameworks, Libraries, and Embedded Content にあるフレームワークを変更。
Pods_MyApp.framework
↓↓↓
libPods-MyApp.a
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CombineLatest Streamのテスト手法

はじめに

テスト書いてますか?
テストコードが0のアプリにようやくテストコードを入れるようになり、RxTest/RxBlockingを使いはじめました。
CombineLatestで複数のObservabelをまとめて観測する値がtuple型のときに、テストで詰まったので解決策を残します。

環境

  • macOS Catalina 10.15.3
  • Xcode 11.3.1 (11C504)
  • RxSwift 5.0.1

Rxのテスト

Model

テストしたいModelを用意します。
今回はuserStream: Observable<(String, Int)>の値をテストします。

import RxSwift
import RxCocoa

class UserModel {

    let name: BehaviorRelay<String> = BehaviorRelay(value: "")
    let age: BehaviorRelay<Int> = BehaviorRelay(value: 0)

    // outputStream
    let userStream: Observable<(String, Int)>

    init(name: String, age: Int) {
        self.name.accept(name)
        self.age.accept(age)

        userStream = Observable.combineLatest(self.name, self.age)
    }
}

テストコード

それではUserModelのuserStreamのテストを書いてみます。

import XCTest
import RxSwift
import RxCocoa
import RxBlocking
import RxTest
@testable import TestApp

class TestAppTests: XCTestCase {

    var userModel: UserModel!
    var scheduler: TestScheduler!
    var disposeBag: DisposeBag!

    override func setUp() {
        userModel = UserModel(name: "takutoki", age: 5)
        scheduler = TestScheduler(initialClock: 0)
        disposeBag = DisposeBag()
        super.setUp()
    }

    func testObservableWithTuple() {
        // assertする型を定義
        let testObserver = scheduler.createObserver((String, Int).self)

        // テスト対象のbind先をtestObserverにする
        userModel.userStream
            .bind(to: testObserver)
            .disposed(by: disposeBag)

        // name に流すテストObservable
        let nameTestObservable = scheduler.createColdObservable([
            Recorded.next(10, "ShirasakaKoume"),
            Recorded.next(20, "KoshimizuSachiko"),
            Recorded.next(30, "HoshiSyoko")
            ])

        // age に流すテストObservable
        let ageTestObservable = scheduler.createColdObservable([
            Recorded.next(15, 13),
            Recorded.next(25, 14),
            Recorded.next(35, 15)
            ])

        nameTestObservable
            .bind(to: userModel.name)
            .disposed(by: disposeBag)

        ageTestObservable
            .bind(to: userModel.age)
            .disposed(by: disposeBag)

        // expect
        // 0: 初期値
        // 10,20,30: nameが変更されたときに流れる
        // 15,25,35: ageが変更されたときに流れる

        let expected: [Recorded<Event<(String, Int)>>] = [
            Recorded.next(0, ("takutoki", 5)),
            Recorded.next(10, ("ShirasakaKoume", 5)),
            Recorded.next(15, ("ShirasakaKoume", 13)),
            Recorded.next(20, ("KoshimizuSachiko", 13)),
            Recorded.next(25, ("KoshimizuSachiko", 14)),
            Recorded.next(30, ("HoshiSyoko", 14)),
            Recorded.next(35, ("HoshiSyoko", 15))
        ]

        scheduler.start()

        XCTAssertEqual(testObserver.events, expected) // Global function 'XCTAssertEqual(_:_:file:line:)' requires that '(String, Int)' conform to 'Equatable'
    }
}

XCTAssertEqualの行でコンパイル前にエラーが起きます。

余談ですが、Xcode10.3ではビルド時に以下のエラーが発生します。
これだと原因がいまいちはっきりしませんね。
実際の直面したときは以下のエラーだったので、解決法がなかなか見つかりませんでした。

Expression type '()' is ambiguous without more context

どうするか

エラーメッセージにある通りEquatableに準拠した型同士でassertするようにすれば良いです。
そこでstruct同士でassertを行うようにします。

struct AssertStruct: Equatable {

    var name: String
    var age: Int

    public static func == (lhs: AssertStruct, rhs: AssertStruct) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
    }
}

Equatableを準拠したAssertStructを作成して ==メソッドを実装します。
テストも書き直します。

func testObservableWithTuple() {
        // assertする型をAssertStructに変更する
        let testObserver = scheduler.createObserver(AssertStruct.self)

        // userStreamをAssertStructに変換するoperatorを追加する
        userModel.userStream
            .map{ AssertStruct(name: $0.0, age: $0.1) }
            .bind(to: testObserver)
            .disposed(by: disposeBag)

        let nameTestObservable = scheduler.createColdObservable([
            Recorded.next(10, "ShirasakaKoume"),
            Recorded.next(20, "KoshimizuSachiko"),
            Recorded.next(30, "HoshiSyoko")
            ])

        let ageTestObservable = scheduler.createColdObservable([
            Recorded.next(15, 13),
            Recorded.next(25, 14),
            Recorded.next(35, 15)
            ])

        nameTestObservable
            .bind(to: userModel.name)
            .disposed(by: disposeBag)

        ageTestObservable
            .bind(to: userModel.age)
            .disposed(by: disposeBag)

        // 期待する値はAssertStructの型に変更する
        let expected: [Recorded<Event<AssertStruct>>] = [
            Recorded.next(0, AssertStruct(name: "takutoki", age: 5)),
            Recorded.next(10, AssertStruct(name: "ShirasakaKoume", age: 5)),
            Recorded.next(15, AssertStruct(name: "ShirasakaKoume", age: 13)),
            Recorded.next(20, AssertStruct(name: "KoshimizuSachiko", age: 13)),
            Recorded.next(25, AssertStruct(name: "KoshimizuSachiko", age: 14)),
            Recorded.next(30, AssertStruct(name: "HoshiSyoko", age: 14)),
            Recorded.next(35, AssertStruct(name: "HoshiSyoko", age: 15))
        ]

        scheduler.start()

        XCTAssertEqual(testObserver.events, expected)
    }

スクリーンショット 2020-02-08 15.24.36.png
All Greeeeen!!!

最後に

上記の例ではこんなんテストする必要あるんか?と思いますが、
実際の業務ではoperatorを複数噛ませたりdistinctUntilChangedなど入れたりと複雑な要件になりがちです。
テストでoutputを素早く確認できるのは安心ですね。

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

XCTestのAssertion一覧、テストファイルの使い方について

XCTestのAssertion一覧と使い方について、頭に入りきっていないので自身のためのカンペを兼ねてまとめました。
筆者が初学者のため、噛み砕いた記事になります。誤字脱字、補足、ここおかしいよ!等歓迎します。

テストファイルの記入

import XCTest //XCTestのフレームワークをインポート

@testable import モジュール名 //モジュールをテスト用にインポート。モジュールが異なる場合、アクセス識別子がpublic,openでないとアクセスできないが、@testableとすることでinternal(明示的にアクセス識別子が書かれてない場合のデフォルト値)へアクセスできる。

class クラス名: XCTestCase //XCTestCaseを継承

XCTestのAssertion一覧

Assertion名の引数の最後にあるstringは試験失敗時のメッセージとして、文字列を入れることができる。試験の結果が確認しやすいよう状況に応じて使用する。

Boolean Assertions

Assertion名 説明
XCTAssert(expression,string) expressionがtrueであることを確認する
XCTAssertTrue(expression,string) expressionがtrueであることを確認する(XCTAssertと同じ)
XCTAssertFalse(expression,string) expressionがfalseであることを確認する

Nil and Non-nil Assertions

Assertion名 説明
XCTAssertNil(expression,string) expressionがnilであることを確認する
XCTAssertNotNil(expression,string) expressionがnilではないことを確認する

Comparable Value Assertions

Assertion名 説明
XCTAssertGreaterThan(expresstion1,expression2,string) expression1 > expression2となっていることを確認する
XCTAssertGreaterThanOrEqual(expresstion1,expression2,string) expression1 ≧ expression2となっていることを確認する
XCTAssertLessThan(expresstion1,expression2,string) expression1 < expression2となっていることを確認する
XCTAssertLessThanOrEqual(expresstion1,expression2,string) expression1 ≦ expression2となっていることを確認する

Equality and Inequality Assertions

Assertion名 説明
XCTAssertEqual(c,string expression1とexpression2を比較し、一致することを確認する
XCTAssertNotEqual(expresstion1,expression2,string) expression1とexpression2を比較し、一致しないことを確認する
XCTAssertEqualObjects(c,string expression1とexpression2を比較し同一オブジェクトであることを確認する(Objective-C専用)
XCTAssertNotEqualObjects(expresstion1,expression2,string) expression1とexpression2を比較し同一オブジェクトではないことを確認する(Objective-C専用)
XCTAssertEqualWithAccuracy(expresstion1,expression2,accuracy,string) expression1とexpression2を比較しaccuracyの範囲以内に差が収まっていることを確認する
XCTAssertNotEqualWithAccuracy(expresstion1,expression2,accuracy,string) expression1とexpression2を比較しaccuracyの範囲以内に差が収まっていないことを確認する

NSException Assertions

Assertion名 説明
XCTAssertThrows(expression,string) expressionで例外が発生することを確認する
XCTAssertNoThrows(expression,string) expressionで例外が発生しないことを確認する
XCTAssertThrowsSpecific(expression,exception_class,string) expressionで、特定のクラス(exception_class)で例外が発生することを確認する
XCTAssertNoThrowsSpecific(expression,exception_class,string) expressionで、特定のクラス(exception_class)で例外が発生しないことを確認する
XCTAssertThrowsSpecificNamed(expression,exception_class,exception_name,string) expressionで、特定のクラス(exception_class)の特定の例外(exception_name)が発生することを確認する
XCTAssertNoThrowsSpecificNamed(expression,exception_class,exception_name,string) expressionで、特定のクラス(exception_class)の特定の例外(exception_name)が発生しないことを確認する

Failing Unconditionally

Assertion名 説明
XCTFail(string) テストを失敗させる

参考
Apple Developer
XCTestのAssert一覧
iOSアプリ開発自動テストの教科書

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