- 投稿日:2020-06-24T23:53:26+09:00
iOS app の習得日記
udemy の講座をみながら、
firebase Authを使って、facebookログインの二週目ViewControllerに
import UIKit
import FBSDKCoreKit
import FBSDKLoginKit
import FacebookCore
import FacebookLogin
import Firebase
なぜか?
https://developers.facebook.com/docs/facebook-login/ios
https://firebase.google.com/docs/auth/ios/facebook-login?authuser=0
公式ドキュメントにはこう書いてないのに??SDKの種類
CoreKit とCoreの違いは??公式のドキュメントの理解が追いついていない
- 投稿日:2020-06-24T21:00:02+09:00
WWDC2020で追加された LazyVGrid / LazyHGrid について考察してみる
※注意
- Xcode 12 beta の環境で動作確認したものです。正式版では動作が変わる可能性もあります。
- 今 Swift や iOS 8 について書くのは NDA 違反か調べてみたと同じ考えのもと、Appleが公開する画像・動画・コードおよびその拡張コードの添付は、問題ないと考えています。
- 本記事に出てくる画面スクリーンショットは、全てXcode11.5およびiOS13で再現した画面です。(Xcode12 betaおよびiOS14のものは1つもありません)はじめに
WWDC2020が初まり、興奮さめやらぬ人も多いのではないでしょうか。
Xcode12 Betaの配布も始まり、公式サイトではいろんなAPIリファレンスが一気に展開されました。前回のWWDC2019で注目を集めたSwiftUIですが、まだ課題も多くアップデートに期待がかかっていましたが。。
無事に、今回の発表で多くのAPIが追加されたようです!その中でも、LazyVGrid / LazyHGrid についてフォーカスしてみました。
LazyVGrid / LazyHGrid
WWDC2020の映像でも公開されていましたが、SwiftUI上でのグリッドデザインを、下記のように簡単に作成できるようになりました。
(Quote: WWDC2020 - What's new in SwiftUI / 10:11~)
1. Documentation
まずは公式のドキュメントから覗いてみます。
(Quote: Documentation - LazyVGrid)
名前で予想はついたかと思いますが、それぞれ縦方向・横方向にグリッドを組める仕組みが用意されています。
また、それぞれに同じ記載のあるこの部分The grid is “lazy,” in that the grid view does not create items until they are needed.
必要になるまでViewの生成がなされないようです。
こちらに関しては後で説明していきます。2. Code Example
まずは簡単な例をみてもらうのが、一番わかりやすいかと思います。
今回はLazyVGrid
で例を作成しました。
(※スクリーンショットはXcode11.5で同じ画面を再現したものです)
- Xcode11.5で同じ画面を再現したコード
ScrollView { ForEach((0...24), id: \.self) { row in HStack { ForEach((1...4), id: \.self) { column in Text("\(row*4+column)") .frame(width: 80, height: 60) // widthは目視で同じになるように任意の値を設定 } }.frame(maxWidth: .infinity) } }
- LazyVGridを使用したコード
ScrollView { LazyVGrid(columns: Array(repeating: GridItem(), count: 4)) { // カラム数の指定 ForEach((1...100), id: \.self) { index in Text("\(index)") .frame(width: 60, height: 60) } } }columnsにある
GridItem
の数だけ、カラムが生成されます。以前は
ForEach
やStack
を用いて、再現したいグリッド部分を入れ子にすることで画面を構成する必要がありました。
LazyVGrid
では指定したcolumns数で区切ってくれるようになり、コードもすっきりしてだいぶ明示的になったのではないでしょうか?3. GridItem
Documentation
先ほどのコードにも記載ありましたが、
LazyVGrid
/LazyHGrid
で画面を構成するためには、必ずGridItem
を渡してあげる必要があります。(Quote: Documentation - GridItem)
プロパティを見ても分かる通り、レイアウトの調整に使用します。
イメージとしては、UICollectionViewのLayoutに近いと思います。Variable
この
GridItem
を使うことで、可変のグリッドも作り出すことが可能です。
GridItem
を生成する際の引数としてGridItem.Size
を指定することで可能になります。
また、サイズの最小値・最大値の設定もこちらで行います。基本的には上記の3タイプで、ざっくり説明すると
① fixed : グリッドのサイズを固定で設定 ② flexible : グリッドのサイズを最小値〜最大値で設定 ③ adaptive : グリッドのサイズを最小値〜最大値で設定し、アイテムを詰めて設置になります。
ぞれぞれ具体例を見ていきましょう。
① fixed
(※スクリーンショットはXcode11.5で同じ画面を再現したものです)
Xcode11.5でほぼ同じ画面を再現したコードはこちら
ScrollView { ForEach((0...24), id: \.self) { row in HStack { Text("\(row*4)").frame(width: 10, height: 60) Text("\(row*4+1)").frame(width: 30, height: 60) Text("\(row*4+2)").frame(width: 20, height: 60) }.frame(maxWidth: .infinity) } }ScrollView { LazyVGrid(columns: [GridItem(.fixed(10)), GridItem(.fixed(30)), GridItem(.fixed(20))]) { // GridItemが3つなので3カラム ForEach((1...100), id: \.self) { index in Text("\(index)") } } }各columnごとに固定値を設定できるようになっています。
② flexible
「① fixed」が可変になった形です。
ScrollView { LazyVGrid(columns: [GridItem(.flexible(minimum: 30, maximum: 100)), GridItem(.flexible(minimum: 0, maximum: 10))]) { // GridItemが2つなので2カラム ForEach((1...100), id: \.self) { index in Text("\(index)") } } }各columnごとに最小値〜最大値を設定できるようになっています。
③ adaptive
※こちらは既存のもので再現できなかったので、できることのイメージ画像を添付します。
Quote: UICollectionViewでタグが左寄せに並んでいるようなレイアウトを実現するScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 100, maximum: 200))]) { ForEach((1...100), id: \.self) { index in Text("\(index)") } } }1つ設定するだけで、設定した最小値〜最大値でアイテムを詰めて表示してくれるようになります。
複数のadaptive
を設定した場合はOS側がよしなに分割するようです(何回か試したがそんな感じだった)4. Lazy
「1. Documentation」で説明しなかったこの部分
The grid is “lazy,” in that the grid view does not create items until they are needed.
このViewはLazyがついていることにすごく意味があります。
それは画面の呼び出しタイミングが違うということです。下記のコードを、それぞれ新・旧のSwiftUIのコードに追加して、挙動の違いを確認していきたいと思います。
.onAppear { debugPrint("onAppear: \(/* current index */)") } .onDisappear { debugPrint("onDisappear: \(/* current index */)") }
onAppear
/onDisappear
を用いて画面の表示・非表示のタイミングでログを出すようにします。Xcode11.5で同じ画面を再現したコードのログを取得する
今までのグリッド実装の場合、画面生成と共にすべてのindexが取得されました。
つまり、画面生成と共にすべての画面(Text)が呼び出されていると言うことになります。LazyVGridを使用したコードのログを取得する
既存のListと同じ動きをします。
つまり、アイテムが表示されたタイミングonAppearが発火し、非表示になったタイミングでonDisappearが発火します。(画像はイメージ図。Xcode11.5より)
実際の開発においては、複雑なViewが膨大に並ぶことになるかと思います。
そのため、古い実装ではパフォーマンスとして良くないため実戦投入には不向きでした。
その部分において、Gridを使用してパフォーマンスの改善をすることができそうです。5. PinnedScrollableViews
名前からなんとなく想像がつきそうな、つかなそうなと言う感じですが、
セクションヘッダー・セクションフッターを固定するかどうか指定するためのプロパティになります。(Quote: Documentation - PinnedScrollableViews)
試しにヘッダーを固定したものを用意しました。
(※スクリーンショットはXcode11.5で同じ画面を再現したものです)ScrollView { LazyVGrid(columns: Array(repeating: GridItem(), count: 4), pinnedViews: .sectionHeaders) { // 固定する方を指定 Section(header: Text("header")) { // セクション ForEach((1...100), id: \.self) { index in Text("\(index)") .frame(width: 60, height: 60) } } } }引数に追加するだけでとても簡単です。
イメージとしては、UICollectionElementKindSection Header/Footer
に近いと思います。
この引数を指定しない場合は、セクションはそのままスクロールされてしまいます。終わりに
今までのSwiftUIでは
UICollectionView
に相当するものがなかったため、開発においてハック的なやり方で、だいぶ無理をする必要がありました。
今回フォーカスしたGridで完全に補えているかどうかは怪しいところですが。。。WWDC2020の発表から、SwiftUIのAPIがたくさん増えたことで、開発の幅が広がりました。
一部の機能はSwiftUIでしか開発できないことを鑑みても、ここ2・3年でSwiftUIへ移行はmustになってきそうです。間違いがあるかもしれないので、指摘あればお願いしますmm
その他
リポジトリ
- SwiftUI_LazyGridドキュメント
- Documentation - LazyHGrid
- Documentation - LazyVGrid
- Documentation - GridItem
- Documentation - GridItem.Size外国の方がGridのレイアウトを解説している動画
- Building Grids in SwiftUI 2.0 for iOS 14
- 投稿日:2020-06-24T21:00:02+09:00
[Swift] WWDC2020で追加された LazyVGrid / LazyHGrid について考察してみる
※注意
- Xcode 12 beta の環境で動作確認したものです。正式版では動作が変わる可能性もあります。
- 今 Swift や iOS 8 について書くのは NDA 違反か調べてみたと同じ考えのもと、Appleが公開する画像・動画・コードおよびその拡張コードの添付は、問題ないと考えています。
- 本記事に出てくる画面スクリーンショットは、全てXcode11.5およびiOS13で再現した画面です。(Xcode12 betaおよびiOS14のものは1つもありません)はじめに
WWDC2020が初まり、興奮さめやらぬ人も多いのではないでしょうか。
Xcode12 Betaの配布も始まり、公式サイトではいろんなAPIリファレンスが一気に展開されました。前回のWWDC2019で注目を集めたSwiftUIですが、まだ課題も多くアップデートに期待がかかっていましたが。。
無事に、今回の発表で多くのAPIが追加されたようです!その中でも、LazyVGrid / LazyHGrid についてフォーカスしてみました。
LazyVGrid / LazyHGrid
WWDC2020の映像でも公開されていましたが、SwiftUI上でのグリッドデザインを、下記のように簡単に作成できるようになりました。
(Quote: WWDC2020 - What's new in SwiftUI / 10:11~)
1. Documentation
まずは公式のドキュメントから覗いてみます。
(Quote: Documentation - LazyVGrid)
名前で予想はついたかと思いますが、それぞれ縦方向・横方向にグリッドを組める仕組みが用意されています。
また、それぞれに同じ記載のあるこの部分The grid is “lazy,” in that the grid view does not create items until they are needed.
必要になるまでViewの生成がなされないようです。
こちらに関しては後で説明していきます。2. Code Example
まずは簡単な例をみてもらうのが、一番わかりやすいかと思います。
今回はLazyVGrid
で例を作成しました。
(※スクリーンショットはXcode11.5で同じ画面を再現したものです)
- Xcode11.5で同じ画面を再現したコード
ScrollView { ForEach((0...24), id: \.self) { row in HStack { ForEach((1...4), id: \.self) { column in Text("\(row*4+column)") .frame(width: 80, height: 60) // widthは目視で同じになるように任意の値を設定 } }.frame(maxWidth: .infinity) } }
- LazyVGridを使用したコード
ScrollView { LazyVGrid(columns: Array(repeating: GridItem(), count: 4)) { // カラム数の指定 ForEach((1...100), id: \.self) { index in Text("\(index)") .frame(width: 60, height: 60) } } }columnsにある
GridItem
の数だけ、カラムが生成されます。以前は
ForEach
やStack
を用いて、再現したいグリッド部分を入れ子にすることで画面を構成する必要がありました。
LazyVGrid
では指定したcolumns数で区切ってくれるようになり、コードもすっきりしてだいぶ明示的になったのではないでしょうか?3. GridItem
Documentation
先ほどのコードにも記載ありましたが、
LazyVGrid
/LazyHGrid
で画面を構成するためには、必ずGridItem
を渡してあげる必要があります。(Quote: Documentation - GridItem)
プロパティを見ても分かる通り、レイアウトの調整に使用します。
イメージとしては、UICollectionViewのLayoutに近いと思います。Variable
この
GridItem
を使うことで、可変のグリッドも作り出すことが可能です。
GridItem
を生成する際の引数としてGridItem.Size
を指定することで可能になります。
また、サイズの最小値・最大値の設定もこちらで行います。基本的には上記の3タイプで、ざっくり説明すると
① fixed : グリッドのサイズを固定で設定 ② flexible : グリッドのサイズを最小値〜最大値で設定 ③ adaptive : グリッドのサイズを最小値〜最大値で設定し、アイテムを詰めて設置になります。
ぞれぞれ具体例を見ていきましょう。
① fixed
(※スクリーンショットはXcode11.5で同じ画面を再現したものです)
Xcode11.5でほぼ同じ画面を再現したコードはこちら
ScrollView { ForEach((0...24), id: \.self) { row in HStack { Text("\(row*4)").frame(width: 10, height: 60) Text("\(row*4+1)").frame(width: 30, height: 60) Text("\(row*4+2)").frame(width: 20, height: 60) }.frame(maxWidth: .infinity) } }ScrollView { LazyVGrid(columns: [GridItem(.fixed(10)), GridItem(.fixed(30)), GridItem(.fixed(20))]) { // GridItemが3つなので3カラム ForEach((1...100), id: \.self) { index in Text("\(index)") } } }各columnごとに固定値を設定できるようになっています。
② flexible
「① fixed」が可変になった形です。
ScrollView { LazyVGrid(columns: [GridItem(.flexible(minimum: 30, maximum: 100)), GridItem(.flexible(minimum: 0, maximum: 10))]) { // GridItemが2つなので2カラム ForEach((1...100), id: \.self) { index in Text("\(index)") } } }各columnごとに最小値〜最大値を設定できるようになっています。
③ adaptive
※こちらは既存のもので再現できなかったので、できることのイメージ画像を添付します。
Quote: UICollectionViewでタグが左寄せに並んでいるようなレイアウトを実現するScrollView { LazyVGrid(columns: [GridItem(.adaptive(minimum: 100, maximum: 200))]) { ForEach((1...100), id: \.self) { index in Text("\(index)") } } }1つ設定するだけで、設定した最小値〜最大値でアイテムを詰めて表示してくれるようになります。
複数のadaptive
を設定した場合はOS側がよしなに分割するようです(何回か試したがそんな感じだった)4. Lazy
「1. Documentation」で説明しなかったこの部分
The grid is “lazy,” in that the grid view does not create items until they are needed.
このViewはLazyがついていることにすごく意味があります。
それは画面の呼び出しタイミングが違うということです。下記のコードを、それぞれ新・旧のSwiftUIのコードに追加して、挙動の違いを確認していきたいと思います。
.onAppear { debugPrint("onAppear: \(/* current index */)") } .onDisappear { debugPrint("onDisappear: \(/* current index */)") }
onAppear
/onDisappear
を用いて画面の表示・非表示のタイミングでログを出すようにします。Xcode11.5で同じ画面を再現したコードのログを取得する
今までのグリッド実装の場合、画面生成と共にすべてのindexが取得されました。
つまり、画面生成と共にすべての画面(Text)が呼び出されていると言うことになります。LazyVGridを使用したコードのログを取得する
既存のListと同じ動きをします。
つまり、アイテムが表示されたタイミングonAppearが発火し、非表示になったタイミングでonDisappearが発火します。(画像はイメージ図。Xcode11.5より)
実際の開発においては、複雑なViewが膨大に並ぶことになるかと思います。
そのため、古い実装ではパフォーマンスとして良くないため実戦投入には不向きでした。
その部分において、Gridを使用してパフォーマンスの改善をすることができそうです。5. PinnedScrollableViews
名前からなんとなく想像がつきそうな、つかなそうなと言う感じですが、
セクションヘッダー・セクションフッターを固定するかどうか指定するためのプロパティになります。(Quote: Documentation - PinnedScrollableViews)
試しにヘッダーを固定したものを用意しました。
(※スクリーンショットはXcode11.5で同じ画面を再現したものです)ScrollView { LazyVGrid(columns: Array(repeating: GridItem(), count: 4), pinnedViews: .sectionHeaders) { // 固定する方を指定 Section(header: Text("header")) { // セクション ForEach((1...100), id: \.self) { index in Text("\(index)") .frame(width: 60, height: 60) } } } }引数に追加するだけでとても簡単です。
イメージとしては、UICollectionElementKindSection Header/Footer
に近いと思います。
この引数を指定しない場合は、セクションはそのままスクロールされてしまいます。終わりに
今までのSwiftUIでは
UICollectionView
に相当するものがなかったため、開発においてハック的なやり方で、だいぶ無理をする必要がありました。
今回フォーカスしたGridで完全に補えているかどうかは怪しいところですが。。。WWDC2020の発表から、SwiftUIのAPIがたくさん増えたことで、開発の幅が広がりました。
一部の機能はSwiftUIでしか開発できないことを鑑みても、ここ2・3年でSwiftUIへ移行はmustになってきそうです。間違いがあるかもしれないので、指摘あればお願いしますmm
その他
リポジトリ
- SwiftUI_LazyGridドキュメント
- Documentation - LazyHGrid
- Documentation - LazyVGrid
- Documentation - GridItem
- Documentation - GridItem.Size外国の方がGridのレイアウトを解説している動画
- Building Grids in SwiftUI 2.0 for iOS 14
- 投稿日:2020-06-24T15:57:05+09:00
Custom URL Schemeを便利に使うためのStruct
Custom URL SchemeのURL文字列をSwift上で使用できるようにパースするための構造体を考えたのでメモ。
想定URL形式:
someApp://home/articleDetail?userId=100&articleId=200&searchText=hoge
Custom URL Scheme自体の実装はこちらを参照。
Custom URL Schemeでアプリ内の任意のページを表示する// DeepLinkHierarchy.swift struct DeepLinkHierarchy { enum TabType: String { case home // ※サンプルケース case article // ※サンプルケース case setting // ※サンプルケース case myPage // ※サンプルケース case none // ※サンプルケース } enum ScreenNameType: String { case articleDetail case archive case changeAccount case none } var tabType: TabType var screenNameType: ScreenNameType var query: DeepLinkQuery init(host tabText: String, query queryText: String) { tabType = TabType(rawValue: tabText) ?? .none screenNameType = ScreenNameType(rawValue: path) ?? .none query = DeepLinkQuery(queryText) } } struct DeepLinkQuery { var userId: Int? // ※サンプルプロパティ var articleId: Int? // ※サンプルプロパティ var searchText: String? // ※サンプルプロパティ init(_ query: String) { if let userIdText = query.getValue(by: "userId") { userId = Int(userIdText) } if let articleIdText = query.getValue(by: "articleIdText") { articleId = Int(articleIdText) } searchText = query.getValue(by: "searchText") } } private extension String { func getValue(by key: String) -> String? { return components(separatedBy: "&") .map({ $0.components(separatedBy: "=") }) .first(where: { $0.first == key })?[1] } }
TabType
と命名しているのは、UITabBarを利用している前提のもと、どのタブをルートとしておくかの判定に利用する想定とした。
また、ルートタブを指定しつつ、遷移先画面をScreenNameType
で判別する。// AppDelegate.swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let deepLinkHierarchy = DeepLinkHierarchy(host: url.host ?? "", query: url.query ?? "") print("tabType: \(deepLinkHierarchy.tabType)") print("UserId: \(deepLinkHierarchy.query.userId)") print("articleId: \(deepLinkHierarchy.query.articleId)") print("searchText: \(deepLinkHierarchy.query.searchText)") return true }ログアウトプット
tabType: home UserId: Optional(100) articleId: Optional(200) searchText: Optional("hoge")
- 投稿日:2020-06-24T13:13:59+09:00
タイマーアプリをリファクタリングする
はじめに
iPhoneアプリ開発集中講座にあるタイマーアプリのリファクタリングに挑戦してみたいと思います。
楽器アプリの「Day 1 Lesson 4-4 ステップアップ リファクタリングで見通しを改善しよう」(P.165) で学びましたね。タイマーアプリが完成してからでかまいませんので、楽器アプリで学んだことを思い出して、コードの冗長性をなくしてみてください。
(iPhoneアプリ開発集中講座 P.257より引用)リファリングとは
Twitterのツイートを引用しますが、ソフトウェア開発の上で読みづらくなったコードを整理して可読性の向上と勘違いによる不具合発生の防止が主な目的となると思います。
好きな文章を引用
— アユム@1月からWebエンジニア (@ex_endeavor) June 21, 2020
> ソフトウェアの開発は、繰りかえしに次ぐ繰り返しです。読みづらくなるまでコードを書き足し、そのコードをリファクタリングする、その繰り返しです。そして、より簡潔なコードを書くために、本書が少しでもお役に立てばと思います。実際に考えてみる
まずどんな構造にするのか?
タイマアプリは、下記のような用件があると思います
- タイマーカウントダウン
- タイマーのカウントダウンする設定値を読み出し
- タイマーのカウントダウンする設定値の書き込み
- UIの表示
4つほどあると思います。
そこでファイルを分けることを考えます。下記のように分けてみました。
用件 ファイル メモ タイマーカウントダウン CountDownManager.swift(新規) タイマーのカウントダウンを検討する タイマーのカウントダウンする設定値を読み出し SettingManager.swift(新規) UserDefaultsの読み出しを行う タイマーのカウントダウンする設定値の書き込み SettingManager.swift(新規) UserDefaultsの読み出しと、取りうる値を管理する UIの表示 ViewController.swift
SettingViewController.swift従来のファイルのままとする これから紹介するコードはこちらのgithubに公開しますので参考にしてください。
タイマーカウントダウン
タイマーカウントダウンするTimerManager.swiftを作りました。
課題となるのは、1秒毎にタイムアウトしたときにViewController.swiftにどのように通知するか?です。そこで今回はdelegateを用いてUI表示更新してみることにしました。
下記のようにdelegateメソッドを定義しています。
protocol TimerManagerDelegate: class { func timerInterrupt(remainCount:Int) }また、いくつかのメソッドを定義しました。
メソッド名 概要 start タイマーカウントダウンを開始する stop タイマーカウントダウンを停止する clear タイマーの設定値などを変わった時にクリアする また、外部公開する変数(プロパティー)を1つ定義しました。
変数名 概要 timerValue タイマーの設定時間 設定値を管理する
設定値を管理するSettingManager.swiftを作りました。
UIPickerViewで表示する設定の選択肢のリストと、タイマーの設定値の取得と設定する変数(プロパティー)を定義しました。
変数名 概要 setttingArray UIPickerViewで表示する設定の選択肢のリスト timerVaue タイマーの設定時間
UserDefaultsから値を取得、設定する
設定するときは、TimerManager.swiftに設定値を更新して、クリアをする起動
アプリが起動する時にタイマーカウントダウンするTimerManager.swiftのtimerValueを初期化する処理を追加しました。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. // タイマーマネージャーのインスタンス取得 let timerManager = TimerManager.shared // 設定マネージャーのインスタンス取得 let settingManager = SettingManager.shared // 設定マネージャーで保持している設定時間をタイマーマネージャーに渡す timerManager.timerValue = settingManager.timerVaue return true }終わりに
様々なリファクタリングがあると思います。
まずは一例として捉えていただければと思います。ありがとうございました。
- 投稿日:2020-06-24T10:18:59+09:00
[Swift]複数の要素を配列から削除する方法
背景
SwiftとFirebaseを使ってマッチングアプリを作っていたところ、不正ユーザーをブロックする機能を実装する必要が出てきました。
そして、タイトルにもあるように、「配列から複数の要素を削除する方法」が必要になったのでその方法を自分なりに考えてみました。
方針
array1からarray2と重複する要素を除去します。
実際の環境では、自分以外の全ユーザーのドキュメントIDをDBから取得して配列(array1)とし、ブロックしたユーザーのドキュメントIDをDBから取得して配列(array2)として、array1からarray2の要素を除去することで、ブロックしていないユーザーのドキュメントIDからなる配列を取得します。
具体的な方法
var array1 = ["a","b","c","d","e","f"] var array2 = ["a","d","e"] var duplicateIndexs:[Int] = [] var count = 0 for i in 0..<array1.count { if array2.contains(array1[i]){ duplicateIndexs.append(i) } } print(duplicateIndexs) // [0, 3, 4] for i in 0..<duplicateIndexs.count{ array1.remove(at: duplicateIndexs[i] - count) count += 1 } print(array1) // ["b", "c", "f"]補足
duplicateIndexsとか、countとかなんかごちゃごちゃやっているなという感じですが、
単に以下のコードだとIndex out of rangeのエラーが出ます。var array1 = ["a","b","c","d","e","f"] var array2 = ["a","d","e"] for i in 0..<array1.count { if array2.contains(array1[i]){ array1.remove(at: i) } } print(array1)removeで除去しつつfor文を回しているので、array1が短くなっていき(要素が減っていき)、array1[i]の部分がIndex out of rangeになるんですね。
このエラーを避けるために、duplicateIndexsとか、countとか用意してなんとか目的の、array2の要素を除去した新たな配列を取得することに成功しました。
「もっと他にいい実装方法あるよ」という意見やアドバイスをいただけると幸いです!