- 投稿日:2020-02-08T21:50:13+09:00
#24 チュートリアル的な初回起動時のみ表示されるViewController1例
はじめに
個人のメモ程度の出来なのであまり参考にしないで下さい.
環境
Xcode:11.2.1
Swift:5.1.2
2019/11part1
Main.storyboard
でsegue
を選択する.part2
segue
を選択した状態で,Attributes inspector
のIdentifier
に任意の文字列を入れる.part3
AppDelegate.swift
のapplication(_: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
ホーム画面の
ViewController
のStartViewController.swift
のviewDidAppear(_)
に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
チュートリアル画面の
ViewController
のAboutAppViewController.swift
の「閉じる」ボタンのアクションにに1行追加する.@IBAction func toback(_ sender: UIButton) { //この下の1行を追加 self.dismiss(animated: true, completion: nil) }
- 投稿日:2020-02-08T18:21:02+09:00
Swiftの無限ループをforEachで実現する
はじめに
なんで余計なことを考えるのか。
while( true ) {}
を使いましょう。
(Swift触り始めて数日の初心者なので、間違いなどがある可能性があります)きっかけ
ネットのどこかでみかけた以下のやりとり。
cソースからswift5に移行してるのですが、ループ処理の代替で詰まりました。
for (;;) {}
上記for文をswift5で書くとするとどう書くでしょうか?
while (true) {}
が無難ですかね...それ以外ないような
やはりwhile文使うしか無さそうですね。ありがとうございました
foreachは?
無限ループforeachサンプルお願いします
ごめん勘違いしてたわ
作ってみたもの
cancelメソッドを実行するまで無限にnextできるイテレーター。
無限forEachclass Loop:Sequence,IteratorProtocol{ typealias Element = Loop var _loopVal :Loop? init() { _loopVal = self } func next() -> Loop? {return _loopVal } func cancel() { self._loopVal = nil } }forEachでの使い方var cnt = 0 Loop().forEach { loop in cnt += 1 print(cnt) if cnt > 5 { loop.cancel() } }for..inでの使い方(breakで抜けるパターン)var cnt = 0 for _ in Loop(){ cnt += 1 print(cnt) if cnt > 5 { break } }(使用例の条件が簡易すぎて用途が不明瞭すぎる…)
以下雑感(作ってる間に考えてたこととか)
- 最初は単なる無限のジェネレーター(イテレーター、シーケンス)を作ればそれで完了だと思っていた。
- for..inはいいけど、forEach{}(クロージャー)で使うと無限ループ終了できないね。うん。
- Cancelの仕組みつくろか。
cancel:bool
を条件値に持てばええな。- ……。ループするたびに判定処理とか実行コストアカンでしょ……。
self
を変数にいれて管理すれば判定いらんくなるな。(これでやっと↑のコードに)ループ毎に判定してたやつclass Loop:Sequence,IteratorProtocol{ typealias Element = Loop var _cancel = false func next() -> Loop? {return self._cancel ? nil : self } func cancel() { self._cancel = true } }
- Cancelの仕組みはスレッドセーフの観点からみたらどうなる??
- breakで抜ける分には、breakの条件になる値をスレッドセーフにして読み書きしてれば大丈夫だろうけど、Cancelでは次ループに入る可能性があるのでは?
- ……残念なことに問題がでそうなコードを書いてみたけれど、おかしな値を出すことができなかった。
(並列処理の書き方がよくない??)スレッドセーフ検証class Counter{ var count: Int = 0; init(){} } func doAsyncs() { let group = DispatchGroup() let queue = DispatchQueue(label: "queue", attributes: .concurrent) let loop = Loop(); let cnt = Counter(); // 並列処理させるタスク // 共用のカウンタをインクリメントする // (微妙な動きさせたいので、一旦共用カウンタは排他制御なし) // 共用のカウンタが規定値超えたところでループを抜ける。 let task = { (taskNo: Int) in var i = 0 loop.forEach { l in if cnt.count > 100000 { l.cancel() // ループ抜けた時のタスクNoとタスク内カウンタを表示 print("taskNo:\(taskNo) i:\(i)") return } cnt.count += 1 i += 1 } } // タスク10個作って実行 for i in 1 ... 10{ let work = DispatchWorkItem { task(i) group.leave() } group.enter() queue.async(execute: work) } // 全タスク終了で共用カウンタの値出力 group.notify(queue: .main) { print("result=\(cnt.count)") } } doAsyncs()出力結果taskNo:7 i:10589 taskNo:2 i:10333 taskNo:10 i:10380 taskNo:4 i:10383 taskNo:1 i:10516 taskNo:3 i:10508 taskNo:6 i:10419 taskNo:8 i:10400 taskNo:9 i:10479 taskNo:5 i:10557 result=100001
iの合計値>ループ終了条件値
なのは共用カウンタのインクリメントをアトミック操作してないため……。- Loopのインスタンスはタスクごとに別にしても、共用にしても結果変わらず。
- そもそも終了条件をみたしているときはループを抜るので、問題にならないのか。
- そうなると
_loopVal = nil
の操作がアトミックかを考えればいいだけかな?- Swiftがそのへんどうなのかは、また今度調べよう。
- 投稿日:2020-02-08T17:53:49+09:00
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...]を選びます。
すると、githubのリポジトリを検索するウインドウが出てくるので、お目当の「Quick」を検索します。ちなみに、github以外にあるものでもURLを指定すればパッケージを入れることができるそうです(私は未検証)。
バージョンやブランチを聞かれます。今回は特にこだわらないのでそのままNextを押します。
次は、ライブラリをどのターゲットに対して追加するかを聞かれます。デフォルトでは実際のソース(今回はqiitasample)が選ばれていますが、Quickはテストコードで使いたいライブラリなので、忘れずにテストコードの方(qiitasampleTests)を選びましょう。
すると、Xcodeの左側に追加したパッケージと、その依存関係にあるパッケージが追加されます。Quickを入れただけでSwiftPMがNimbleも落としてきてくれたみたいですね。
いきなりエラー
早速Quick/Nimbleを使ってテストコードを書いていきましょう!まずはQuickをimportして...
それらしいものがサジェストされません。もう不穏ですね。
とりあえずQuickと手打ちしてビルド(Controll+B)してみると...
No such module Quick
と言われてしまいました。いや、あるじゃん。
この後プロジェクトをクリーンしてみたり、エラー名でググってみたりしましたが、それらしい解決法に出会えずハマってしまいました...解決法
どうにかできないかと思いXcodeのメニューを見ていると、[Product]の中に[Build for...]という項目を見つけました。よく見ると...
Build For Testingがある!ここでようやく、Controll+Bではテストコードに対するビルドが行われていないことに気付きました。確かにXcode上部のバーにQuickがビルドされているような表示は出ていなかった気がする。
Build For Testingを実施すると、QuickやNimbleにもビルドがかかり、無事エラーも解消されました。まとめ
- テストコードに対してパッケージを追加した時は、Build For Testingを実行する。
- ググってもわからないからといって諦めず、メニューをよく見てIDEの機能をよく確認する。
- 投稿日:2020-02-08T15:40:30+09:00
ドットインストールの動画を観てXcodeの環境構築をしたときに詰まったところ
Xcode11とドットインストール動画内での環境の違いと対策
これからSwiftの学習およびiOSアプリの開発を行いたいと考えている初学者の方が、恐らく私含めいらっしゃると思います。
動画で学習できるドットインストールを使って、まずは環境構築をしようとしたのですが、当時の動画内での設定(最終更新は2017年)とかなり違っていて時間を喰われたので、その具体的な内容と対策について情報共有します。
Qiitaへは初の投稿となり、至らないことも多いかと思いますが、該当される方は是非ご活用ください。
SwiftUI
まずはプロジェクトを作成する場面です。下記画像の「Create a new Xcode project」を選択します。
おそらくここまでは動画内の説明のまま進めて大丈夫です。問題は次あたりに出てくるこの画面です。
User Interfaceの選択肢は、ドットインストールの動画内にはありませんでした。SwiftUIはざっと調べたところ、2019年6月ごろに発表された新しいUIのようです。
動画の通りに学習を進めていくと、「Main.storyboard」というファイル(iPhoneなどの画面にボタンなどの部品を配置できるファイル)を使うことがわかったのですが、SwiftUIを使用するとこれが見つかりません。おそらく代替ファイルのようなものはあると思いますが、慣れていないうちはStoryboardをUIとして選択することを推奨します。
アシスタントエディター
動画内では画面右上の方に○が2つ重なったようなアイコンをクリックしてアシスタントエディターを表示させていましたが、Xcode11ではそのアイコンが無くなっています。ググってみたところ、対策については割と多く情報がありましたが、設定でアイコンを表示させるというようなことはできなさそうです。
これについては「Xcode11 アシスタントエディター」で検索してみてください。アシスタントエディターの画面分割
上記内容でググったところ、どうやらMain.storyboardファイルが表示されている状態で、編集したい新たなファイルをoption+クリックすればよいとの情報が多数確認できたので実際にやってみたんですが、画面が左右ではなく上下に分割されてしまいました。
このままでも良かったんですが、どうにもお互い狭い画面でしか確認できないのがストレスに感じそうだったので、解決方法を調べてみたところ、以下画像の一番左のアイコンで設定できることがわかりました。
画面全体でいうと一番右上あたりにまとまっていると思います。四角のなかに+マークがついているようなアイコンですが、こいつにoptionキーを押しながらカーソルを持っていくと、中の縦線の向きが変わります。(上記画像ではすでに左右分割の設定になっている)
これを変更した状態で再度、アシスタントエディターを表示したところ、無事に左右分割になってくれました。他のサイト様を色々調べて回ったんですが、追加で表示したいファイルをoption+クリックで初めから左右分割できる、という記述が多く、自分の設定だけおかしいのか...と焦ってしまいました。
環境構築は私含め初学者の方が萎えやすいポイントかと思われますので、この記事が少しでもお力になれば幸いです。
- 投稿日:2020-02-08T15:38:13+09:00
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) }最後に
上記の例では
こんなんテストする必要あるんか?
と思いますが、
実際の業務ではoperatorを複数噛ませたりdistinctUntilChangedなど入れたりと複雑な要件になりがちです。
テストでoutputを素早く確認できるのは安心ですね。
- 投稿日:2020-02-08T15:35:53+09:00
MessageKit メッセージの削除
MessageKit でLineのように長押しでメッセージを削除
UICollectionViewの機能を利用する。
削除以外の他の処理も実装可能です。継承クラスにメニューオブジェクトを定義
MessageChatViewControllerでチャット画面の実装、ChatMessageCellでテキスト、ChatPhotoCellで画像を表示しているものとする。
class MessageChatViewController: MessagesViewController { // 新しくロングタップメニューを追加する let messageMenuItem = UIMenuItem(title: "削除", action: #selector(ChatMessageCell.deleteMessageData(_:))) let photoMenuItem = UIMenuItem(title: "削除", action: #selector(ChatPhotoCell.deletePhotoData(_:))) }*ここはなぜかセルのメソッドでないと動かないっぽい
長押しの許可
ここから先はUICollectionViewDelegate, UICollectionViewFlowDelegateのメソッドでMessageChatViewControllerクラスに実装する。
// 長押しを許可するか? override func collectionView(_ collectionView: UICollectionView, shouldShowMenuForItemAt indexPath: IndexPath) -> Bool { // 自分のメッセージのみ let message = self.messageForItem(at: indexPath, in: messagesCollectionView) if message.sender.senderId != currentSender().senderId { return false } // セル種別 if let _ = collectionView.cellForItem(at: indexPath) as? ChatMessageCell { return true } if let _ = collectionView.cellForItem(at: indexPath) as? ChatPhotoCell { return true } return false }メニュー選択時の処理の実行を許可
ちょっと面倒だけど編集時に編集を許可するとかそんな設計思考ににてる。
actionでメニュー選択時に実行する処理が渡されるのでdescriptionプロパティ でセレクタを判断してUIMenuItem生成時に設定したメソッドであればtrueを返す。// 長押しメニューを選択時に処理の実行を可能にするか? override func collectionView(_ collectionView: UICollectionView, canPerformAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) -> Bool { // セル種別 if let _ = collectionView.cellForItem(at: indexPath) as? ChatMessageCell { // 削除のみ if action.description == "deleteMessageData:" { return true } } if let _ = collectionView.cellForItem(at: indexPath) as? ChatPhotoCell { // 削除のみ if action.description == "deletePhotoData:" { return true } } return false }こここでよくあるように「return super.collectionView(collectionView, canPerformAction: action, forItemAt: indexPath, withSender: sender)」を入れておくとコピーとかそんなデフォルトの動きもメニューに追加される。
長押しメニュー選択時の処理
ここまできてようやく実際の処理を記述する。
便利なのはindexPathとactionが渡されること。
長押しメニューで選択したときに自力で変数を渡す処理を書かなくていい。// 長押しメニュー実行時の処理 delegeteでセルから戻ってくる override func collectionView(_ collectionView: UICollectionView, performAction action: Selector, forItemAt indexPath: IndexPath, withSender sender: Any?) { let message = self.messageForItem(at: indexPath, in: messagesCollectionView) guard let message_id = Int(message.messageId) else { return } // セル種別 if let _ = collectionView.cellForItem(at: indexPath) as? ChatMessageCell { // 削除のみ if action.description == "deleteMessageData:" { // テキストセル削除処理 } } if let _ = collectionView.cellForItem(at: indexPath) as? ChatPhotoCell { // 削除のみ if action.description == "deletePhotoData:" { //画像セル削除処理 } } }セルにメソッドを追加
長押しメニューからの処理はセル→UICollectionViewのコールバックで行われます。UIMenuItem生成時に設定するメソッドの作成と、そのメソッドからUICollectionViewの処理を呼び出す部分を定義します。
// メッセージ削除処理 delegeteでコールバックして実行する @objc func deleteMessageData(_ sender: Any?) { if let collectionView = self.superview as? UICollectionView { // indexPath取得 if let indexPath = collectionView.indexPath(for: self) { // action実行 collectionView.delegate?.collectionView?(collectionView, performAction: NSSelectorFromString("deleteMessageData:"), forItemAt: indexPath, withSender: sender) } } }セルからindexPathを取得してセレクタ指定で処理を実行。
セレクタをメソッド名と同じにしてるけど、ここはおそらく別でもいけるはず。。。
- 投稿日:2020-02-08T02:06:54+09:00
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) テストを失敗させる