20200713のSwiftに関する記事は13件です。

練習のためにSwiftをUdemyで勉強してみた(5日目)

今日やったこと

  • ARアプリを色々作って試した
  • Githubに作った作品を公開した

ARアプリについて

テキストに沿って、いくつかのアプリを作りながら、AR機能を色々と遊んでおります。
しばらくはAR機能で遊べそうですが、じっくりとアプリを作っていきたくなる今日このごろです。

作ったアプリ

MyFirstARapp

タイトルにひねりのないやつ。
単純にXcodeでNew Projectしたときに最初にでてくるやつ。
AR機能をビルドする練習として
https://github.com/blumemond10/MyFirstARapp

BoxAR

単純に直方体を置くアプリ。
簡単なオブジェクト配置、テクスチャの設定について学習しました。
https://github.com/blumemond10/BoxAR

TextAR

単純に文字を置くアプリ。
簡単なテキスト配置、大きさ、色変更について学習しました。
https://github.com/blumemond10/TextAR

MultiObject

複数のObjectを設置するアプリ。
上記で実践した、直方体、テキストに加え、球も配置しました。
位置が被るということに直面したので特に奥行きですが、配置の調整の必要性を学びました。
https://github.com/blumemond10/MultiObject

BoxTouch

上記のBoxARを改造して、Objectをタップするとテクスチャが変わるようにしました。
ここでは、一度のタップで色がかわるのですが、ちょっと応用で「タップしている間色が変わる」「再タップで色が戻る」などの実験もこれからしてみたいな、と思ってます。
https://github.com/blumemond10/BoxARTouch

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

PHPickerViewControllerを使ってみた

はじめに

PHPickerViewControllerはUIImagePickerControllerのカメラ以外の機能に複数選択など新機能が追加されました。
そのPHPickerViewControllerを使ったサンプルコードを作ってみました。
Xcode12 beta2で作成しています。
サンプルコードはgithubで公開しています。

コードの解説

呼び出し

    func makeUIViewController(context: Context) -> some UIViewController {
        var configuration = PHPickerConfiguration()
        // 静止画を選択
        configuration.filter = .images
        // 複数選択可能(上限枚数なし)
        configuration.selectionLimit = 0
        let picker = PHPickerViewController(configuration: configuration)
        picker.delegate = context.coordinator
        return picker
    }

filterで選択できる画像形式を指定できます。複数選択もできるうようです。
selectionLimitで複数選択する時の上限を指定できます。0の場合は上限なく複数選択できます。

delegate

        func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {

            // Pickerを閉じる
            parent.picker.toggle()

            for result in results {
                if result.itemProvider.canLoadObject(ofClass: UIImage.self) {
                    result.itemProvider.loadObject(ofClass: UIImage.self) { (image, error) in

                        if let image = image as? UIImage {
                            // 選択された画像を追加する
                            self.parent.images.append(image)
                        } else {
                            print("Error(1)")
                        }

                    }
                } else {
                    print("Error(2)")
                }
            }
        }

画像を選択後Addを押すとdelegateが呼ばれます。
UIImageに変換し、クロージャーが呼ばれます。シュミレーターで選択した一部の画像はUIImageに変換できないことがありました。
これは、まだBetaなので今後正式リリースに向けて改善されるのだと期待しています。

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

tableViewのstaticCell使用下においてのdidSelectedRowAt使用法

section使用時のtableViewの応用例

任意のCellのタップ時に動作を条件分岐させたい

swift
import UIKit

class TableViewController: UITableViewController {
    override func viewDidLoad() {
        super.viewDidLoad() 
    }
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("selected Section is: \(indexPath.section)")
        print("selected Row is: \(indexPath.row)")

        if indexPath.section == 1 && indexPath.row == 2 {
        // do something...
     }        

これで任意のCellのタップ時に動作の条件分岐ができるようになりました。

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

private extensionのすゝめ

private extension ?

private メソッドをまとめておきたい場合は以下のように記述することができます。

private extension Model {
    func foo() {}
    func bar() {}
}

private func はダメなの?

もちろんダメではないです。
しかし private メソッドが複数ある場合は private extension 内にまとめて記述する方が良いと考えています。

私自身、以前はコメントを記述してセクション分けをしていました。
しかしコメントを記述しただけなので当然 private メソッド以外も記述可能です。

// MARK: - Private methods
extension Model {
    private func foo() {}
    private func bar() {}
    func baz() {}    // private でないメソッドも記述できる :(
}

コメントは一般的に保守されないことが多いので、コメントと実装は乖離していく傾向にあります。
また個人のプロダクトであっても、過去の自分が決めた「自分ルール」は 必ず 破られます。

コメントに頼らないコードを書きたいと考えたときに private extension が効果を発揮します。
(もちろん先人が残した丁寧なコメント達にはいつも大変お世話になっております)

注意事項

struct ContentView: View {
    ...
    func qux() {
        let model = Model()
        model.foo()    // ❗️'foo' is inaccessible due to 'fileprivate' protection level
    }
}

外部からアクセスしようとすると確かに Xcode がエラーを出してくれます。
しかし、デフォルトでは 'fileprivate' protection level となるので注意が必要です。
fileprivate を許容できない場合は、 extension 内で private として宣言すれば private が適用されます。

private extension Model {
    private func foo() {}
}

正直微妙かなと思います。
私は fileprivate を許容して 1クラス1ファイル で運用していくことにします。

過去の自分が決めた「自分ルール」は 必ず 破られます。

おわり

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

private extension のすゝめ

private extension ?

private メソッドをまとめておきたい場合は以下のように記述することができます。

private extension Model {
    func foo() {}
    func bar() {}
}

private func はダメなの?

もちろんダメではないです。
しかし private メソッドが複数ある場合は private extension 内にまとめて記述する方が良いと考えています。

私自身、以前はコメントを記述してセクション分けしていました。
しかしコメントを記述しただけなので当然 private メソッド以外も記述可能です。

// MARK: - Private methods
extension Model {
    private func foo() {}
    private func bar() {}
    func baz() {}    // private でないメソッドも記述できる :(
}

コメントは保守されないことが多いので、一般的にコメントと実装は乖離していく傾向にあります。
また個人のプロダクトであっても、過去の自分が決めた「自分ルール」は 必ず 破られます。

コメントに頼らないコードを書きたいと考えたときに private extension が効果を発揮します。
(もちろん先人が残した丁寧なコメント達にはいつも大変お世話になっております)

注意事項

struct ContentView: View {
    ...
    func qux() {
        let model = Model()
        model.foo()    // ❗️'foo' is inaccessible due to 'fileprivate' protection level
    }
}

外部からアクセスしようとすると確かに Xcode がエラーを出してくれます。
しかし、デフォルトでは 'fileprivate' protection level となるので注意が必要です。
fileprivate を許容できない場合は、 extension 内で private として宣言すれば private が適用されます。

private extension Model {
    private func foo() {}
}

正直微妙かなと思います。
私は fileprivate を許容して 1クラス1ファイル で運用していくことにします。

過去の自分が決めた「自分ルール」は 必ず 破られます。

おわり

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

DIコンテナのテスト以外での利点について

概要

Martin Fowler氏によってDependency Injection (以下DI) と DIコンテナについての概念が2004年に発表されて約16年。
Java だけでなく JS や Swift、C# と言った様々な言語に実装されてきて基本的な設計概念として定着してきた。
だが、DIコンテナの利点、なぜDIコンテナを使うのかという話になってくると
テスト容易性をあげる、という話ばかりが多くそれ以外のメリットについて説明されることが少ないと感じてる。
Java開発を変える最新の設計思想「Dependency Injection(DI)」とは
DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる

そこでこの記事ではテスト容易性の向上以外のDIコンテナのメリットについて書いていきたいと思う。

前提 - DI と DIコンテナは違う-

まず、本題に入る前にDIとはなんのか、ということを軽く触れる。
依存性の注入とはある ModuleA の実行が違う ModuleB に依存していた時、
外部から ModuleA に ModuleB をセット(注入)できることをいう。
したがって以下のようなコンストラクトインジェクションも立派なDIである

public struct ServerSetting {}
public protocol APIClient { /*略*/}
public protocol UserRepository { /*略*/}

public class HTTPClient: APIClient {
  public init(_ setting: ServerSetting) { /*略*/ }
}

public class UserRepoImpl: UserRepository {
  public init(_ apiClient: APIClient) { /*略*/ }
}

public class Service {
  public init(_ repository: UserRepository) { /*略*/ }
}

let setting: ServerSetting = ServerSetting()
let apiClient: APIClient = HTTPClient(setting) // ServerSettingを注入してる
let userRepository: UserRepository = UserRepoImpl(apiClient) // APIClient を注入してる
let service = Service(userRepository) // UserRepositoryを注入してる
service.execute()

そしてそのDIを効率的に行うための方法の一つがDIコンテナである。
DIを行う方法はDIコンテナだけではないのだが
そこにも触れると話が大きくなりすぎるのでここではDIコンテナを使ったDIについてのみ述べる。

また、DIコンテナの中にはアノテーションやリフレクションを使い記述量を減らすための DSL 的な書き方ができる物もあるが
今回はDIコンテナとは何かということを説明するために敢えてプリミティブなDIコンテナとしてSwift製のSwinjectを用いる。
(Swiftを使うのはここ最近で自分にとって一番手慣れた言語だから)

DIコンテナは何をしているのか

まず、DIコンテナが内部でどのようなことをしてるのか、というのを軽く説明する。
SwinjectでDIコンテナの設定と利用を行うと以下のようなコードになる。
(リフレクションやアノテーションを使っていないため若干冗長なコードになる)

let container = Container()
container.register(ServerSetting.self) { _ in
  return ServerSetting()
}

container.register(APIClient.self) { r in
  return HTTPClient(r.resolve(ServerSetting.self)!)
}

container.register(UserRepository.self) { _ in
  return UserRepoImpl(r.resolve(APIClient.self)!)
}

func main() {
  let repo = container.resolve(UserRepository.self)!
  let service =  Service(repo)
  service.execute()
}

さて、やっていることを説明しよう。

  1. Interface と実際の実装モジュールの紐付けを設定する
  2. Interface が利用側から要求されると実装モジュールを組み立てる
  3. 組み立てられたモジュールを要求元に提供する
  4. 要求元は受け取った依存物を使い処理を行う

この中でDIコンテナがやっていることは仕様と実装の紐付けと解決である。
つまり語弊を恐れず乱暴に言ってしまえば
DIコンテナはクラスオブジェクトをキーとしたハッシュテーブルを拡張したものでしかなく、汎用的なものだ。

DIコンテナはテスト容易性をあげるのか

DIコンテナはテスト容易性をあげることに寄与する、とよく言われる。
しかし、正しくは依存モジュールを切り離すことができるようになるとクラスの責務が明確になり
その結果単体テストがしやすくなる、というのが本当のところだ。

ただ、依存を切り離せるようにつくるとどうしても以下のようなマトリョーシカ的なコードになってしまい、
インスタンス生成の実装コストがあがる。

let service = Service(
                UserRepository(
                  HTTPClient(ServerSetting())
                )
              )

その実装コストの省力化にDIコンテナは有効である。
一度依存関係を設定してしまえば次からは以下のようにかけるし

let service = Service(container.resolve(UserRepository.self)!)

テスト側で以下のようにモックオブジェクトに切り替えることもできる

container.register(UserRepository.self) { _ in
  return MockRepository()
}

let service = Service(container.resolve(UserRepository.self)!)

このような機能をもってDIコンテナはテスト容易性をあげるのに有効である、という説自体は別に間違えてはいない。

だが、DIコンテナはテストを容易にするためのものである、というところまで言ってしまうのはいささか言い過ぎである。
というのも依存モジュールの組み立て自体は Factory でもできるし、何よりDIコンテナの他の重要な利点を無視したものになるからである。
次の項で Factory を使った依存モジュール生成について述べよう。

Factory を使った依存物生成について

試しに Factory を使った生成を書いてみよう。

public class UserRepoFactory {
  public static func make() -> UserRepository {
    return UserRepository(
             HTTPClient(ServerSetting())
           )
  }
}

なるほど、確かに毎回設定クラスや APIClient を生成してて冗長だ。
であるのであれば APIClientSetting のFactoryを用意してしまおう。
そして、UserRepoFactory もクラスから関数にしてしまおう。
となると、以下のようになる。

func makeSetting() -> ServerSetting {
  return ServerSetting()
}
func makeAPIClient(_ setting: ServerSetting = makeSetting()) -> APIClient {
  return HTTPClient(setting)
}
func makeUserRepo(_ client: APIClient = makeAPIClient()) -> UserRepository {
  return UserRepoImpl(client)
}

func main() {
  let repo = makeUserRepo()
  let service =  Service(repo)
  service.execute()
}

それぞれの関数のデフォルト引数にデフォルトインスタンスを生成するFactoryメソッドを設定することによって
基本的な利用に関してはデフォルトのインスタンスが注入され、
引数に渡ってるインスタンスを以下のようにモックインスタンスに変えてやればテストも可能である

let repo = makeUserRepo(MockAPIClient())

上記のFactoryがやっていることを整理すると以下のようになる

  1. Interface と実際の実装モジュールの紐付けを定義する
  2. Interface が利用側から要求されると実装モジュールを組み立てる
  3. 組み立てられたモジュールを要求元に提供する
  4. 要求元は受け取った依存物を使い処理を行う

そう、DIコンテナがやってることとほとんど変わらないのである。
つまり依存を切り離すことによるテスト容易性をあげるのにはFactoryでもいいということだ。

では DIコンテナとFactoryの違い、そしてDIコンテナを使う理由、すなわちメリットとはなんだろうか?

DIコンテナとFactoryの違い

前項でFactoryのやっていることを整理し、DIコンテナと比較しやっていることはほとんど変わらないと書いたが
実はというと微妙に違う。
DIコンテナは
「Interface と実際の実装モジュールの紐付けを設定する」なのに対し
Factoryは
「Interface と実際の実装モジュールの紐付けを定義する」になっている。

もったいぶった言い方になっているが簡単に言ってしまえば
Factoryの方はメソッド定義によって実装との紐付けが静的に行われているのに対し、DIコンテナは動的に行われている。

つまり、ある Interface の実装がリクエストされた時、Factoryはメソッドで定義されている以上
確実にインスタンスを返してくれるが
DIコンテナの方は設定が漏れている場合、何が返ってくるかはDIコンテナの実装仕様次第ということになる
(Google Guiceはよろしくインスタンスを生成して返してくれるが Swinjectはnil(null)を返す)

このように書くとDIコンテナよりFactoryを使った方が良さそうな感じがするが、
実は静的であるが故に 依存仕様の隠蔽 ができないという弊害がある。

依存仕様の隠蔽とは

Factoryの 依存仕様の隠蔽 ができないという弊害はどういうことであろうか?
実はその弊害は Clean Architecture だと決定的である。
(Clean Architecture についてはこの記事のスコープ外のため説明しない)

Clean Architecture ではDomainレイヤを実装レイヤが参照し、Domainレイヤに置かれたServiceの実行に必要なモジュールを実装レイヤが実装する、という構造になっている
di1.png

Factoryは UserRepoImpl を参照する必要があるため実装レイヤにおかれる。
故に Service を初期化するコードはFactoryを使った場合以下のようになる。

controlle.swift
let repo = makeUserRepo()
let service = Service(repo)
service.execute()

一方、DIコンテナを使った場合は前述されたように以下のように書くことが可能である

controller.swift
let repo = container.resolve(UserRepository.self)!
let service = Service(repo)
service.execute()

あまり変わらないようだが、DIコンテナは前述したように最終的にはただのハッシュテーブルであり、DIコンテナのI/Fはレイヤによって変わるということはない。
したがって Service のコンストラクタを拡張したら(Javaなどではオーバーロードになると思う)
DIコンテナそのものを渡すことが可能になり以下のように書くことができる

Service+Container.swift
extension Service {
  public init(container: Container) {
    self.userRepository = container.resolve(UserRepository.self)!
  }
}
controller.swift
let service =  Service(container: container)
service.execute()

つまり、Service 内部からDIコンテナを操作することによって何に依存してるかをcontroller側(利用側)から隠蔽することができる。
これを私は個人的に依存仕様の隠蔽と呼んでいる。
一方、Factoryは実装レイヤにあるため Service 内部から参照することはできずcontroller側(利用側)から隠蔽することはできない。

依存仕様を隠蔽できる事による利点

では、依存仕様を隠すことの利点はなんだろうか?
それは Service の依存仕様に利用側が左右されないということである。
たとえば Service が現状 UserRepository に依存しているわけなのだがそれに加え Google Analytics 等に利用状況を送ることになったとしよう。

Google Analyticsが呼ばれるかテストするために抽象化し以下のように Service にDIする手法をとるはずだ。

controller.swift
let repo = makeUserRepo()
let analytics = makeAnalytics()
let service = Service(repository: repo, analytics: analytics)
service.execute()

なるほど。問題ないように見える。だが、こうも思わないだろうか。「Analyticsを呼ぶかどうかなんて Controller 側からは知る必要のない情報なのにControllerの実装を変更するのは面倒だな」と。

そう、インスタンス化するモジュールが何に依存しているか、なんてことは利用する側にとっては本来気にすることではないし気にしたくもない。
この例は機能を追加した話だがリファクタリングによってモジュールを切り離した場合も同じである。
しかしDIコンテナを使うことにより以下のように変更範囲をDIコンテナの設定とそのインスタンスの実装に抑えることができるのだ。

DI.Swift
container.register(Analytics.self) {
  return GoogleAnalytics()
}
Service+Container.swift
extension Service {
  public init(container: Container) {
     self.analytics = container.resolve(Analytics.self)!
     self.userRepository = container.resolve(UserRepository.self)!
  }
}
controller.swift
// controller 側の実装は変わらない
let service = Service(container: container)
service.execute()

これこそが依存仕様の隠蔽によるメリット、つまりDIコンテナを使う理由でもある。
つまり、DIコンテナによって ServiceUserRepositoryAnalytics に依存してるというところから
Service にはなんらかの依存を注入する必要がある、というレベルまで依存の仕様が抽象化され変更に強くなるのである。

結論

以上、DIの方法についてFactoryを使ったやり方とDIコンテナを使ったやり方を比較し、それぞれのメリットとデメリットについて書いた。
これでDIコンテナがテストに有効というだけで無く、依存を隠蔽することによって変更に強くなるということが説明できたかと思う。

DIコンテナはテストのために必要、や、DIコンテナは疎結合による生成の冗長さを解消するもの、という認識だと
生成が冗長でないものはDIコンテナを使わなくてもいいという事になってしまうが
実際は冗長でないものでもDIコンテナを使う意味はある。

ただ、DIコンテナは便利なものではあるが残念ながら万能ではない。
DIコンテナは動的に設定するため、設定を間違えた場合は実行時エラーになるしかないしそれをテストで防ぐのは難しい。
(てか、DIコンテナの設定ってみなさんどのようにテストしてるんですかね。いい方法があれば教えて欲しい)

またDIコンテナ周りの記事はサーバサイドが多く、あまりDIコンテナをアプリケーションコード内で生成する記事はほとんどないが
(というかDIコンテナの設定をコロコロ切り替えるのはサーバサイドではアンチパターンである)
クライアントサイドにおいてはDIコンテナを動的に生成せざるえないことは多々ある。
それに関してはまたどこかで記事を書こうと思う。

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

DIコンテナのテスト以外での利点について (7/15修正)

概要

Martin Fowler氏によってDependency Injection (以下DI) と DIコンテナについての概念が2004年に発表されて約16年。
Java だけでなく JS や Swift、C# と言った様々な言語に実装されてきて基本的な設計概念として定着してきた。
だが、DIコンテナの利点、なぜDIコンテナを使うのかという話になってくると
テスト容易性をあげる、という話ばかりが多くそれ以外のメリットについて説明されることが少ないと感じてる。
Java開発を変える最新の設計思想「Dependency Injection(DI)」とは
DI (依存性注入) って何のためにするのかわからない人向けに頑張って説明してみる

そこでこの記事ではテスト容易性の向上以外のDIコンテナのメリットについて書いていきたいと思う。

まぁまぁ長いので面倒だったら結論を先に読むでいいと思う

当初、DI コンテナを直接モジュールのコンストラクタに渡す実装をしていました & 依存仕様の隠蔽という表現をしているところがありましたがコメントでの指摘を受け修正しました

前提 - DI と DIコンテナは違う-

まず、本題に入る前にDIとはなんのか、ということを軽く触れる。
依存性の注入とはある ModuleA の実行が違う ModuleB に依存していた時、
外部から ModuleA に ModuleB をセット(注入)できることをいう。
したがって以下のようなコンストラクトインジェクションも立派なDIである

public struct ServerSetting {}
public protocol APIClient { /*略*/}
public protocol UserRepository { /*略*/}

public class HTTPClient: APIClient {
  public init(_ setting: ServerSetting) { /*略*/ }
}

public class UserRepoImpl: UserRepository {
  public init(_ apiClient: APIClient) { /*略*/ }
}

public class Service {
  public init(_ repository: UserRepository) { /*略*/ }
}

let setting: ServerSetting = ServerSetting()
let apiClient: APIClient = HTTPClient(setting) // ServerSettingを注入してる
let userRepository: UserRepository = UserRepoImpl(apiClient) // APIClient を注入してる
let service = Service(userRepository) // UserRepositoryを注入してる
service.execute()

そしてそのDIを効率的に行うための方法の一つがDIコンテナである。
DIを行う方法はDIコンテナだけではないのだが
そこにも触れると話が大きくなりすぎるのでここではDIコンテナを使ったDIについてのみ述べる。

また、DIコンテナの中にはアノテーションやリフレクションを使い記述量を減らすための DSL 的な書き方ができる物もあるが
今回はDIコンテナとは何かということを説明するために敢えてプリミティブなDIコンテナとしてSwift製のSwinjectを用いる。
(Swiftを使うのはここ最近で自分にとって一番手慣れた言語だから)

DIコンテナは何をしているのか

まず、DIコンテナが内部でどのようなことをしてるのか、というのを軽く説明する。
SwinjectでDIコンテナの設定と利用を行うと以下のようなコードになる。
(リフレクションやアノテーションを使っていないため若干冗長なコードになる)

Container.swift
let container = Container()
container.register(ServerSetting.self) { _ in
  return ServerSetting()
}

container.register(APIClient.self) { r in
  return HTTPClient(r.resolve(ServerSetting.self)!)
}

container.register(UserRepository.self) { _ in
  return UserRepoImpl(r.resolve(APIClient.self)!)
}

func main() {
  let repo = container.resolve(UserRepository.self)!
  let service =  Service(repo)
  service.execute()
}

さて、やっていることを説明しよう。

  1. Interface と実際の実装モジュールの紐付けを設定する
  2. Interface が利用側から要求されると実装モジュールを組み立てる
  3. 組み立てられたモジュールを要求元に提供する
  4. 要求元は受け取った依存物を使い処理を行う

この中でDIコンテナがやっていることは仕様と実装の紐付けと解決である。
つまり語弊を恐れず乱暴に言ってしまえば
DIコンテナはクラスオブジェクトをキーとしたハッシュテーブルを拡張したものでしかなく、汎用的なものだ。

DIコンテナはテスト容易性をあげるのか

DIコンテナはテスト容易性をあげることに寄与する、とよく言われる。
しかし、正しくは依存モジュールを切り離すことができるようになるとクラスの責務が明確になり
その結果単体テストがしやすくなる、というのが本当のところだ。

ただ、依存を切り離せるようにつくるとどうしても以下のようなマトリョーシカ的なコードになってしまい、
インスタンス生成の実装コストがあがる。

let service = Service(
                UserRepository(
                  HTTPClient(ServerSetting())
                )
              )

その実装コストの省力化にDIコンテナは有効である。
一度依存関係を設定してしまえば次からは以下のようにかけるし

let service = Service(container.resolve(UserRepository.self)!)

テスト側で以下のようにモックオブジェクトに切り替えることもできる

container.register(UserRepository.self) { _ in
  return MockRepository()
}

let service = Service(container.resolve(UserRepository.self)!)

このような機能をもってDIコンテナはテスト容易性をあげるのに有効である、という説自体は別に間違えてはいない。

だが、DIコンテナはテストを容易にするためのものである、というところまで言ってしまうのはいささか言い過ぎである。
というのも依存モジュールの組み立て自体は Factory でもできるし、何よりDIコンテナの他の重要な利点を無視したものになるからである。
次の項で Factory を使った依存モジュール生成について述べよう。

Factory を使った依存物生成について

試しに Factory を使った生成を書いてみよう。

public class UserRepoFactory {
  public static func make() -> UserRepository {
    return UserRepository(
             HTTPClient(ServerSetting())
           )
  }
}

なるほど、確かに毎回設定クラスや APIClient を生成してて冗長だ。
であるのであれば APIClientSetting のFactoryを用意してしまおう。
そして、UserRepoFactory もクラスから関数にしてしまおう。
となると、以下のようになる。

func makeSetting() -> ServerSetting {
  return ServerSetting()
}
func makeAPIClient(_ setting: ServerSetting = makeSetting()) -> APIClient {
  return HTTPClient(setting)
}
func makeUserRepo(_ client: APIClient = makeAPIClient()) -> UserRepository {
  return UserRepoImpl(client)
}

func main() {
  let repo = makeUserRepo()
  let service =  Service(repo)
  service.execute()
}

それぞれの関数のデフォルト引数にデフォルトインスタンスを生成するFactoryメソッドを設定することによって
基本的な利用に関してはデフォルトのインスタンスが注入され、
引数に渡ってるインスタンスを以下のようにモックインスタンスに変えてやればテストも可能である

let repo = makeUserRepo(MockAPIClient())

上記のFactoryがやっていることを整理すると以下のようになる

  1. Interface と実際の実装モジュールの紐付けを定義する
  2. Interface が利用側から要求されると実装モジュールを組み立てる
  3. 組み立てられたモジュールを要求元に提供する
  4. 要求元は受け取った依存物を使い処理を行う

そう、DIコンテナがやってることとほとんど変わらないのである。
つまり依存を切り離すことによるテスト容易性をあげるのにはFactoryでもいいということだ。

では DIコンテナとFactoryの違い、そしてDIコンテナを使う理由、すなわちメリットとはなんだろうか?

DIコンテナとFactoryの違い

前項でFactoryのやっていることを整理し、DIコンテナと比較しやっていることはほとんど変わらないと書いたが
実はというと微妙に違う。
DIコンテナは
「Interface と実際の実装モジュールの紐付けを設定する」なのに対し
Factoryは
「Interface と実際の実装モジュールの紐付けを定義する」になっている。

もったいぶった言い方になっているが簡単に言ってしまえば
Factoryの方はメソッド定義によって実装との紐付けが静的に行われているのに対し、DIコンテナは動的に行われている。

つまり、ある Interface の実装がリクエストされた時、Factoryはメソッドで定義されている以上
確実にインスタンスを返してくれるが
DIコンテナの方は設定が漏れている場合、何が返ってくるかはDIコンテナの実装仕様次第ということになる
(Google Guiceはよろしくインスタンスを生成して返してくれるが Swinjectはnil(null)を返す)

実際に Clean Architecture のケースをみてみよう。
(Clean Architecture についてはこの記事のスコープ外のため説明しない)

Clean Architecture ではDomainレイヤを実装レイヤが参照し、Domainレイヤに置かれたServiceの実行に必要なモジュールを実装レイヤが実装する、という構造になっている
di1.png

Factoryは UserRepoImpl を参照する必要があるため実装レイヤにおかれる。
故に Service を初期化するコードはFactoryを使った場合以下のようになる。

factory.swift
func makeService(repo: UserRepository = makeUserRepo()) -> Service {
  return Service(repo)
}
controller.swift
let service = makeService()
service.execute()

一方、DIコンテナを使った場合は前述されたように以下のように書くことが可能である

Container+Domain.swift
container.register(Service.self) { r in
  return Service(r.resolve(UserRepository.self)!)
}
controller.swift
let service = container.resolve(Service.self)!
service.execute()

あまり変わらないようだが、DIコンテナは依存関係を動的に解決できるため Container+Domain.swift を実装レイヤにも Domain レイヤにもおくことができる
一方、Factory は利用時には依存が全て解決されている必要があるため実装レイヤにしかおけない。

つまり、DIコンテナの方が Factory より依存解決の抽象度が高いと言える。

依存解決の抽象度が高いことの利点

では、依存解決の抽象度が高いことの利点はなんだろうか?

一旦 Factory を使って
Service を機能拡張し Google Analytics 等に利用状況を送ることになった例を書いてみよう。

Google Analyticsが呼ばれるかテストするために抽象化し以下のように Service にDIする手法をとるはずだ。

factory.swift
func makeAnalytics() -> Analytics { 
  return GoogleAnalytics()
}
func makeService(repo: UserRepository = makeUserRepo(), 
                 analytics: Analytics = makeAnalytics()) -> Service {
  return Service(repo, analytics)
}
controller.swift
let service = makeService()
service.execute()

なるほど、Factory を使えば Service の依存仕様の変更は隠蔽され controller 側の実装に影響を及ぼさないように見える。
ただ、内部的には Analytics を呼び出しているため、Factory の利用するタイミングで Analytics の実装が「定義」されている必要がある。

では次にリファクタリングによって internal なモジュールを切り離した場合を考えてみよう。
例えばこの Analytics を操作する際、タグの優先準備などやユーザーID等を紐付けるビジネスロジックを他のクラスからも使えるように Helper として切り出し
Analytics をラップしたとする。

Helper.swift
class Helper { 
  init(_ analytics: Analytics) { /* 略 */}  
}

依存関係は以下のような図になる

DI2.png

すると Factory は以下のようになるはずだ

factory.swift
func makeHelper(analytics: Analytics = makeAnalytics()) -> Helper { 
  return Helper(analytics)
}
func makeService(repo: UserRepository = makeUserRepo(), 
                 helper: helper = makeHelper()) -> Service {
  return Service(repo, helper)
}

良さそうに見えるが実は上記のコードには重大な欠陥がある。
Helper が Domainレイヤの internal なクラスであるのに対し、Factory は実装レイヤに置かざるえないため参照できずコンパイルが通らないのである。
つまり、 Factory を使って生成する場合は本来公開する必要のない Helper を public にする必要が出てくる。
微妙である。

一方、DI コンテナは実装との配線を後に回すことができるため、以下のように Domain レイヤ内の DI コンテナ設定に生成ロジックを書くことができる。

Container+Domain.Swift
// Domain レイヤに DIコンテナの設定をおく

public func configDomainDI(with container: Container) -> Container {
  container.register(Helper.self) { r in
    return Helper(r.resolve(Analytics.self)!)
  }

  container.register(Service.self) { r in
    return Service(r.resolve(UserRepository.self)!,
                   r.resolve(Helper.self)!)
  }
  return container
}

そして controller 側で依存関係の配線をつなげることができる

controller.swift
let combined = configDomainDI(with: container) // container は UserRepository等を実装と紐付けたDIコンテナ
let service = combined.resolve(Service.self)!
service.execute()

これこそがDIコンテナのメリットである。
configureDomainDI を定義した時点では UserRepositoryAnalytics の実装は決定していないが
生成ロジックを書くことができる。

つまり、Factory と比べより生成/依存解決のロジックを抽象化してると言える。

これはクリーンアーキテクチャーのように抽象が実装を参照してるような場合や
アプリケーションレイヤから動的にインフラレイヤに設定オブジェクトを後からセットする様な依存方向を逆転させるパターンの際非常に有効である。
Factory だと依存方向の関係で出来ないからだ。
(無理やりやればできなくはないが、外部からDIできるように private なパラメータを public にしたりなど歪な実装になるだろう)

また、先述した様に生成ロジックが抽象化することが可能であるため
Domain レイヤのオブジェクトの生成ロジックが変更されたとしてもその変更を吸収する
DI コンテナの設定にオブジェクトの生成ロジックを集中させてレイヤ間での生成ロジックに関する腐敗防止層を作ることができ、
変更に強くなるとも言える。

結論

以上、DIの方法についてFactoryを使ったやり方とDIコンテナを使ったやり方を比較し、それぞれのメリットとデメリットについて書いた。
まとめると

  1. Factory でも DI でも依存を切り離し疎結合に書くことができる
  2. Factory は静的に依存を解決するため依存方向が逆転するような場合、歪なコードになってしまうことがある
  3. DI は動的に依存を解決するため実装が決定していないモジュールに依存したとしても生成ロジックを記述することができる
    • 生成ロジックが疎結合となり中間層を挟み込む余地ができ、取り回しが利くようになる(変更に強くなる)
      • この生成ロジックの付け替えはテスト時のモッキングに有効

これでDIコンテナがテストに有効というだけで無く、依存関係を動的に切り替えられることのメリットを説明できたと思う。

DIコンテナはテストのために必要、や、DIコンテナは疎結合による生成の冗長さを解消するもの、という認識だと
生成が冗長でないものはDIコンテナを使わなくてもいいという事になってしまうが
実際は冗長でないものでもDIコンテナを使う意味はある。

ただ、DIコンテナは便利なものではあるが残念ながら万能ではない。
DIコンテナは動的に設定するため、設定を間違えた場合は実行時エラーになるしかないしそれをテストで防ぐのは難しい。
configDomainDIUserRepository を呼んでいるが実装レイヤできちんと実装が紐付けされているかはコンパイル時にチェックできない。
(てか、DIコンテナの設定ってみなさんどのようにテストしてるんですかね。いい方法があれば教えて欲しい)

したがって依存方向が逆転しなかったり、下位レイヤの設定を上位レイヤが動的に実装を切り替える、ということがないのであれば無理に DI コンテナを使う必要はなく
むしろ静的に依存解決される Factory の方がコンパイルチェックが走る分安全ではあるだろう。

ちなみにDIコンテナ周りの記事はサーバサイドが多く、あまりDIコンテナをアプリケーションコード内で生成する記事はほとんどないが
(というかDIコンテナの設定をコロコロ切り替えるのはサーバサイドではアンチパターンである)
クライアントサイドにおいてはDIコンテナを動的に生成せざるえないことは多々ある。
それに関してはまたどこかで記事を書こうと思う。

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

クイズのアプリでボタンに回答を保存して集計していくプログラム

Xcode10.2.1を利用しています

この処理を10個のViewControllerで行い,aD.scoreにどんどん値を足していって,最後の画面で,合計の計算結果を表示するプログラムを作っているのですが,シュミレーターで起動時に,値を入れたボタンをクリックするとアプリが落ちてしまいます
解決方法分かりますでしょうか?
import UIKit

class Question2ViewController: UIViewController {

var score = 0
@IBAction func q21(){
    score += 0
}
@IBAction func q22(){
    score += 0
}
@IBAction func q23(){
    score += 0
}
@IBAction func q24(){
    score += 1
}


override func viewDidLoad() {
    super.viewDidLoad()



    // Do any additional setup after loading the view.
    func prepareForSegue(segue: UIStoryboardSegue, sender:AnyObject?){
        let aD:  AppDelegate = UIApplication.shared.delegate as!
        AppDelegate

        aD.score = aD.score + score
    }
}


/*
// MARK: - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    // Get the new view controller using segue.destination.
    // Pass the selected object to the new view controller.
}
*/

}

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

UIAlertController(ActionSheet)のタイトルの枠を削除する

日本語の記事がなかったので、書いておきます。

困っている内容

タイトルとサブタイトルの枠が残ってしまう。
image.png

実装したいもの

タイトルの枠を削除する
image.png

実装方法

titleとmessageをnilにする

ViewController.swift
let alertController:UIAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) 

参考ソース

https://stackoverflow.com/questions/29777342/how-to-hide-title-message-frame-in-a-uialertcontroller

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

SwiftでFontAwesomeのアイコンを使う(2020年7月)

はじめに

CocoaPodsを使って、FontAwesomeのアイコンを利用する方法です。

「FontAwesomeとは?」の辺りはここでは省略させてもらいます。
Webアプリの方が馴染みがあるかもしれませんが、フリーで利用できるアイコンフォントです。

Swiftでは思っていたより使えるアイコンが少なかったので、たまに重宝しています。

CocoaPodsについて

以前はCocoaPodsに苦手意識があって、CocoaPodsを使わない方法でやっていました。
ずっとこちら↓↓の記事を参考にさせていただいてました。大変感謝しております。
iOSでFont Awesomeを使う

ただ、フォントファイルの設置やInfo.plistの記述、フォントの呼び出し方を毎回忘れるのでw、
徐々に面倒くさくなってしまい、最近ではCocoaPodsを利用することが多いです。

CocoaPodsの導入方法もここでは省略させていただきます。
私はこちら↓↓の記事を参考に導入させていただきました。大変お世話になりました。
CocoaPodsを導入してみた

環境

Xcode 11.2.1
Swift 5.1.2

※私の個人的な好みにより、画面レイアウトにはStoryBoardを使わず、コードのみで記述しています。
StoryBoardを使われる方は色々置き換えていただかなければならない箇所があると思いますので、ご注意ください!!

作るのはこんな感じのもの

新規プロジェクトを作って、ラベル、ボタン、ナビゲーションバーボタンの3つにFontAwesomeのアイコンを設定してみたいと思います。
スクリーンショット 2020-07-13 3.48.21.png

手順

事前準備

1. 新規プロジェクトで Single View App を作成します。
スクリーンショット 2020-07-12 16.50.33.png

2. 名前は適当に IconTest と付けておきます。
スクリーンショット 2020-07-12 16.50.49.png

3. プロジェクトができたら、プロジェクトファイルは一旦閉じておきます。

4. 続いて、ターミナルを起動し、プロジェクト名のディレクトリに移動して「pod init」を実行します。
スクリーンショット 2020-07-12 16.54.17.png

5. ディレクトリに Podfile ができるので、そのまま「vi Podfile」を実行してターミナル上で編集しても良いですし、テキストエディタで開いて編集してもOKです。
スクリーンショット 2020-07-12 16.54.41.png

6. Podfile に「pod 'FontAwesome.swift'」の1行を追加し、保存して閉じます。

Podfile
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

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

  # Pods for IconTest

  pod 'FontAwesome.swift'

end

7. ターミナルに戻って、そのままのディレクトリで「pod install」を実行します。

8. コマンドの実行が終わると、ディレクトリ内に「プロジェクト名.xcworkspace」ファイルができているはずなので、それをXcodeで開きます。
スクリーンショット 2020-07-13 4.24.01.png

画面の作成

1. まず SceneDelegate を開き、「func scene( ...」の部分を以下のように修正、追記します。

SceneDelegate.swift
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
//        guard let _ = (scene as? UIWindowScene) else { return }

        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        self.window = window
        let rootView = ViewController(nibName: nil, bundle: nil)
        window.rootViewController = UINavigationController(rootViewController: rootView)
        window.makeKeyAndVisible()
    }

    === 以下省略 ===

}

これは、今回StoryBoardを使わないので、このような記述しています。
起動時に開く View を ViewController に指定し、ナビゲーションバーを付けて開く、といった意味合いです。

2. 続いて、ViewController を開いて、以下のように追記します。解説はコメントで入れておきます。

ViewController.swift
import UIKit
import FontAwesome_swift    // FontAwesomeを使用することを宣言

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // ナビゲーションバーが見やすいように背景色を指定
        view.backgroundColor = UIColor.darkGray

        // ラベルの場合(ブランドというスタイルからGitHubアイコンを指定)
        let gitHubLabel = UILabel()
        gitHubLabel.font = UIFont.fontAwesome(ofSize: 100, style: .brands)
        gitHubLabel.text = String.fontAwesomeIcon(name: .github)
        gitHubLabel.textColor = UIColor.white

        // ボタンの場合(レギュラーというスタイルから閉じるボタンのようなアイコンを指定)
        let closeButton = UIButton()
        closeButton.titleLabel?.font = UIFont.fontAwesome(ofSize: 40, style: .regular)
        closeButton.setTitle(String.fontAwesomeIcon(name: .windowClose), for: .normal)
        closeButton.addTarget(self, action: #selector(didTapCloseButton(_:)), for: .touchUpInside)  // タップした時のアクションを指定

        // ナビゲーションバーボタンの場合(ソリッドスタイルから設定ボタンのような歯車マークのアイコンを指定)
        let attributes = [NSAttributedString.Key.font: UIFont.fontAwesome(ofSize: 20, style: .solid)]
        let settingButton = UIBarButtonItem(title: String.fontAwesomeIcon(name: .cog),
                                            style: .plain,
                                            target: self,
                                            action: #selector(didTapSettingButton(_:)))     // タップした時のアクションを指定
        settingButton.setTitleTextAttributes(attributes, for: .normal)                      // 通常状態のボタン
        settingButton.setTitleTextAttributes(attributes, for: .highlighted)                 // タップされてハイライトした時のボタン
        navigationItem.setRightBarButton(settingButton, animated: true)                     // ナビゲーションバーの右ボタンに設置

        // オートレイアウトで配置を指定
        view.addSubview(gitHubLabel)
        view.addSubview(closeButton)

        gitHubLabel.translatesAutoresizingMaskIntoConstraints = false
        closeButton.translatesAutoresizingMaskIntoConstraints = false

        gitHubLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -80).isActive = true
        gitHubLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true

        closeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 80).isActive = true
        closeButton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
    }

    // closeButtonがタップされた時の動作
    @objc private func didTapCloseButton(_ sender: UIButton) {
        print("Did tap close button.")
    }

    // settingButtonがタップされた時の動作
    @objc private func didTapSettingButton(_ sender: UIButton) {
        print("Did tap setting button.")
    }
}

あとはビルドすれば、最初のスクショのような画面ができあがるはずです。

最後に

Pod 提供元のGitHubがこちら↓↓です。
FontAwesome.swift

FontAwesomeの公式ではないと思っているのですが、
あまり詳しくないので、もしご存知の方がいれば、教えていただけるとありがたいです。

GitHubのReadmeにはツールバーのボタンや画像として使う方法なども載っているようですので、
そちらもご興味あれば参考にしてみてください。

最後までご覧いただき、ありがとうございました!
至らない点や無駄な記述など、お気付きの点はご指摘いただけるとありがたいです!

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

【初心者向け】SwiftでFontAwesomeのアイコンを使う

はじめに

CocoaPodsを使って、FontAwesomeのアイコンを利用する方法を書きます。

「FontAwesomeとは?」の辺りはここでは省略させてもらいます。
Webアプリの方が馴染みがあるかもしれませんが、フリーで利用できるアイコンフォントです。

Swiftでは思っていたより使えるアイコンが少なかったので、たまに重宝しています。

CocoaPodsについて

以前はCocoaPodsに苦手意識があって、CocoaPodsを使わない方法でやっていました。
ずっとこちら↓↓の記事を参考にさせていただいてました。大変感謝しております。
iOSでFont Awesomeを使う

ただ、フォントファイルの設置やInfo.plistの記述、フォントの呼び出し方を毎回忘れるのでw、
徐々に面倒くさくなってしまい、最近ではCocoaPodsを利用することが多いです。

CocoaPodsの導入方法もここでは省略させていただきます。
私はこちら↓↓の記事を参考に導入させていただきました。大変お世話になりました。
CocoaPodsを導入してみた

環境

Xcode 11.2.1
Swift 5.1.2

※私の個人的な好みにより、画面レイアウトにはStoryBoardを使わず、コードのみで記述しています。
StoryBoardを使われる方は色々置き換えていただかなければならない箇所があると思いますので、ご注意ください!!

作るのはこんな感じのもの

新規プロジェクトを作って、ラベル、ボタン、ナビゲーションバーボタンの3つにFontAwesomeのアイコンを設定してみたいと思います。
スクリーンショット 2020-07-13 3.48.21.png

手順

事前準備

1. 新規プロジェクトで Single View App を作成します。
スクリーンショット 2020-07-12 16.50.33.png

2. 名前は適当に IconTest と付けておきます。
スクリーンショット 2020-07-12 16.50.49.png

3. プロジェクトができたら、プロジェクトファイルは一旦閉じておきます。

4. 続いて、ターミナルを起動し、プロジェクト名のディレクトリに移動して「pod init」を実行します。
スクリーンショット 2020-07-12 16.54.17.png

5. ディレクトリに Podfile ができるので、そのまま「vi Podfile」を実行してターミナル上で編集しても良いですし、テキストエディタで開いて編集してもOKです。
スクリーンショット 2020-07-12 16.54.41.png

6. Podfile に「pod 'FontAwesome.swift'」の1行を追加し、保存して閉じます。

Podfile
# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

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

  # Pods for IconTest

  pod 'FontAwesome.swift'

end

7. ターミナルに戻って、そのままのディレクトリで「pod install」を実行します。

8. コマンドの実行が終わると、ディレクトリ内に「プロジェクト名.xcworkspace」ファイルができているはずなので、それをXcodeで開きます。
スクリーンショット 2020-07-13 4.24.01.png

画面の作成

1. まず SceneDelegate を開き、「func scene( ...」の部分を以下のように修正、追記します。

SceneDelegate.swift
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // guard let _ = (scene as? UIWindowScene) else { return }

        guard let windowScene = (scene as? UIWindowScene) else { return }
        let window = UIWindow(windowScene: windowScene)
        self.window = window
        let rootView = ViewController(nibName: nil, bundle: nil)
        window.rootViewController = UINavigationController(rootViewController: rootView)
        window.makeKeyAndVisible()
    }

    === 以下省略 ===

}

これは、今回StoryBoardを使わないので、このような記述しています。
起動時に開く View を ViewController に指定し、ナビゲーションバーを付けて開く、といった意味合いです。

2. 続いて、ViewController を開いて、以下のように追記します。解説はコメントで入れておきます。

ViewController.swift
import UIKit
import FontAwesome_swift    // FontAwesomeを使用することを宣言

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        // ナビゲーションバーが見やすいように背景色を指定
        view.backgroundColor = UIColor.darkGray

        // ラベルの場合(ブランドというスタイルからGitHubアイコンを指定)
        let gitHubLabel = UILabel()
        gitHubLabel.font = UIFont.fontAwesome(ofSize: 100, style: .brands)
        gitHubLabel.text = String.fontAwesomeIcon(name: .github)
        gitHubLabel.textColor = UIColor.white

        // ボタンの場合(レギュラーというスタイルから閉じるボタンのようなアイコンを指定)
        let closeButton = UIButton()
        closeButton.titleLabel?.font = UIFont.fontAwesome(ofSize: 40, style: .regular)
        closeButton.setTitle(String.fontAwesomeIcon(name: .windowClose), for: .normal)
        closeButton.addTarget(self, action: #selector(didTapCloseButton(_:)), for: .touchUpInside)  // タップした時のアクションを指定

        // ナビゲーションバーボタンの場合(ソリッドスタイルから設定ボタンのような歯車マークのアイコンを指定)
        let attributes = [NSAttributedString.Key.font: UIFont.fontAwesome(ofSize: 20, style: .solid)]
        let settingButton = UIBarButtonItem(title: String.fontAwesomeIcon(name: .cog),
                                            style: .plain,
                                            target: self,
                                            action: #selector(didTapSettingButton(_:)))     // タップした時のアクションを指定
        settingButton.setTitleTextAttributes(attributes, for: .normal)                      // 通常状態のボタン
        settingButton.setTitleTextAttributes(attributes, for: .highlighted)                 // タップされてハイライトした時のボタン
        navigationItem.setRightBarButton(settingButton, animated: true)                     // ナビゲーションバーの右ボタンに設置

        // オートレイアウトで配置を指定
        view.addSubview(gitHubLabel)
        view.addSubview(closeButton)

        gitHubLabel.translatesAutoresizingMaskIntoConstraints = false
        closeButton.translatesAutoresizingMaskIntoConstraints = false

        gitHubLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: -80).isActive = true
        gitHubLabel.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true

        closeButton.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 80).isActive = true
        closeButton.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0).isActive = true
    }

    // closeButtonがタップされた時の動作
    @objc private func didTapCloseButton(_ sender: UIButton) {
        print("Did tap close button.")
    }

    // settingButtonがタップされた時の動作
    @objc private func didTapSettingButton(_ sender: UIButton) {
        print("Did tap setting button.")
    }
}

あとはビルドすれば、最初のスクショのような画面ができあがるはずです。

最後に

Pod 提供元のGitHubがこちら↓↓です。
FontAwesome.swift

FontAwesomeの公式とは違うと思っているのですが、この辺りはあまり詳しくないのでよく分かりませんw

GitHubのReadmeにツールバーのボタンや画像として使う方法なども載っていますので、
(英語ですが)そちらもご興味あれば参考にしてみてください。

最後までご覧いただき、ありがとうございました!
至らない点や無駄な記述など、お気付きの点はご指摘いただけるとありがたいです!

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

"ld: warning: Could not find or use auto-linked library 'XCTestSwiftSupport'"

「RxSwiftやってみよー!」と思い、SwiftPMでRxSwiftをインストール、適当なところでimportしてビルドしようとするも、コンパイルエラーでBuild Failedに... :cry:

エラーログ

ld: warning: Could not find or use auto-linked library 'XCTestSwiftSupport'
ld: warning: Could not find or use auto-linked framework 'XCTest'
Undefined symbols for architecture x86_64:
  "__swift_FORCE_LOAD_$_XCTestSwiftSupport", referenced from:
      __swift_FORCE_LOAD_$_XCTestSwiftSupport_$_RxTest in RxTest.o
  "XCTest.XCTFail(_: Swift.String, file: Swift.StaticString, line: Swift.UInt) -> ()", referenced from:
      RxTest.XCTAssertRecordedElements<A where A: Swift.Equatable>(_: [RxTest.Recorded<RxSwift.Event<A>>], _: [A], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
  "XCTest.XCTAssertEqual<A where A: Swift.Equatable>(_: @autoclosure () throws -> A, _: @autoclosure () throws -> A, _: @autoclosure () -> Swift.String, file: Swift.StaticString, line: Swift.UInt) -> ()", referenced from:
      RxTest.XCTAssertEqual<A where A: Swift.Equatable>(_: [RxSwift.Event<A>], _: [RxSwift.Event<A>], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
      RxTest.XCTAssertEqual<A where A: Swift.Equatable>(_: [RxSwift.SingleEvent<A>], _: [RxSwift.SingleEvent<A>], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
      RxTest.XCTAssertEqual<A where A: Swift.Equatable>(_: [RxSwift.MaybeEvent<A>], _: [RxSwift.MaybeEvent<A>], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
      RxTest.XCTAssertEqual(_: [RxSwift.CompletableEvent], _: [RxSwift.CompletableEvent], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
      RxTest.XCTAssertEqual<A where A: Swift.Equatable>(_: [RxTest.Recorded<RxSwift.Event<A>>], _: [RxTest.Recorded<RxSwift.Event<A>>], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
      RxTest.XCTAssertEqual<A where A: Swift.Equatable>(_: [RxTest.Recorded<RxSwift.Event<A?>>], _: [RxTest.Recorded<RxSwift.Event<A?>>], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
      RxTest.XCTAssertRecordedElements<A where A: Swift.Equatable>(_: [RxTest.Recorded<RxSwift.Event<A>>], _: [A], file: Swift.StaticString, line: Swift.UInt) -> () in RxTest.o
      ...
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

原因

Xcode 11.4のバグらしい。11.5で直っていないとのことですが、そこまで調べてはいません...

解決策

Swift Package Managerのターゲットを見直して、インストールし直すことで、Build Succeedになりました:smile_cat:

Package Product Target
RxSwift 作ったプロジェクト
RxCocoa 作ったプロジェクト
RxRelay 作ったプロジェクト
RxBlocking 作ったプロジェクトのテスト
RxTest 作ったプロジェクトのテスト

スクリーンショット 2020-07-12 23.52.07.png

参考

https://github.com/ReactiveX/RxSwift/issues/2127#issuecomment-592330682
https://twitter.com/freak4pc/status/1233560587176681472

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

練習のためにSwiftをUdemyで勉強してみた(4日目)

今日やったこと

  • ARKitを使ったアプリ制作のチュートリアル
  • 信頼されていないデベロッパと表示される現象の解決

ARKitを使ったアプリ制作のチュートリアル

受講している講座が古いためか、プロジェクトを新規作成時に新たに「Reality Composer」という項目が増えていることに気がついた。
調べてみると、まだまだ旧来のもののほうが細かなところまで手が届くのだが、どうやら簡単にARの導入ができるらしい。
が、そうやって訳もわからないままテキストを外れていっていいことは一つもないので、とりあえずテキストどおりに進める。

https://note.com/northsand/n/nb245a2d4ab1f

どうやら、プロジェクトを作って、早速ARのビルドができるらしい、早速実機ビルドしてみたところ、ビルドはできたのだが、なぜか実機で起動しない。。。

信頼されていないデベロッパと表示される現象の解決

どうやら許可されていないデベロッパなのが、いけないそうでiPhoneの「設定」から許可することができるらしい。
が、その設定項目が見つからず。

調べてみると、どうやら他の人も困っているらしい。

https://blog.integrityworks.co.jp/2020/01/23/ios-verify-the-developer-app-certificate-for-your-account-is-trusted-on-your-device/
https://teratail.com/questions/207230

よくわからないまま試行錯誤した結果、Xcode自体のTeamを一度ログアウトし再度ログインしなおし&実機の再起動などを繰り返していたら解消した。
なんだったんだ。

明日やること

  • ARアプリ制作を終わらせたい
  • 詳細Swift第5版を購入したので、少しでも読み進めていきたい

https://www.amazon.co.jp/dp/B081GK7636/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1

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