iOSエンジニアのつぶやき

毎朝8:30に iOS 関連の技術について1つぶやいています。まれに釣りについてつぶやく可能性があります。

【CocoaPods】Specsとは?

CocoaPods Specsとは?

ライブラリのバージョン、プラットフォーム 、ソースの取得場所、説明などのメタデータが記載されたファイルのことです。 また、これらのファイルはGithub上のCocoaPods/Specsに一覧として存在します。 ちなみに、これらSpecsCocoaPodsをインストールした時にクローンされ、下記のパスに存在しています。

~/.cocoapods/repos/master/

f:id:yum_fishing:20210324181659p:plain

普段ライブラリをCocoaPods経由でインストールする場合のSpecsとの関係としては、pod installなどをした時にPodfileの内容を見て、Specsから取得したメタ情報を元にソースを取得してくる感じです。これにより、CocoaPodsでインストールするライブラリの整合性をとることができるようになります。ですので、仮にSpecs内にライブラリのメタ情報がない場合は、基本的にpod installは失敗するので、pod repo updateが必要になります。

ちなみに、一旦ローカルのSpecsを全て削除したい場合はpod repo remove masterを実行します。もう一度Specsを取得するときはpod setupを実行します。

てな感じで本日も以上になります🚀

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【CircleCI】CocoaPodsのキャッシュがうまくできない

先日CircleCICocoaPodsによるライブラリの依存関係をキャッシュしようと下記のようなJobconfig.ymlに追加しました。

jobs:
// 省略
  cocoapods_dependencies:
    executor: hoge-macos
    steps:
      - restore_caches
      - run:
          name: CocoaPods install
          command: make install-cocoapods
      - save_cache:
          key: cocoapods-v1-{{ checksum "Podfile.lock" }}
          paths:
            - ~/hoge-ios/Pods

restore_cachesカスタムコマンドについては、以前の記事でも書いた通りKeyを元にrestore_cacheでキャッシュを取得しています。

yamato8010.hatenablog.com

これでpod installしたライブラリをキャッシュできるだろうと思っていたら、キャッシュがされずにBuild Errorが発生。調べてみると同様の問題が報告されていて、どうやらresoter_cachesave_cacheで、cocoapods-v1-{{ checksum "Podfile.lock" }}による生成されるキーが異なっているためでした。

discuss.circleci.com

キャッシュのフォールバック処理を追加してPodsの最新のキャッシュをとってくるようにする、下記のような解決策もありましたが、そもそもcocoapods-v1-{{ checksum "Podfile.lock" }}の値が変動していることは問題です🤔

commands:
  restore_caches:
    steps:
      // 省略
      - restore_cache:
          keys:
            - cocoapods-v1-{{ checksum "Podfile.lock" }}
            - cocoapods-v1-

cocoapods-v1-{{ checksum "Podfile.lock" }}の値がrestore_cachesave_cacheで差異があるということは、Podfile.lockmake install-cocoapods(bundle install --jobs=4)時に変更されていることを意味します。そう思いながらもとりあえず上記の解決法でCircleCIを変更してみると、プロジェクトのコンパイル自体は通るもののCocoaPodsRunScriptフェーズで下記のようなエラーが発生しました。

The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.

どうやらPodfile.locksandboxとの内容が一致していないことが問題みたいです。じゃあpod installPodfile.lockが直るのか?と思いpod installを実行すると、Podfile.lockにdiffが出る代わりに下記のような警告が表示されました。

[!] Your project does not explicitly specify the CocoaPods master specs repo. Since CDN is now used as the default, you may safely remove it from your repos directory via `pod repo remove master`. To suppress this warning please add `warn_for_unused_master_specs_repo => false` to your Podfile.

ローカルのSpecレポに何やら問題がありそうですね。ということで、警告の通りpod repo remove masterした後にpod setupSpecレポを設定し直しました。そして再度。

$ pod install

次はPodfile.lockにdiffが出ていました。

$ git diff
diff --git a/Podfile.lock b/Podfile.lock
index 4ea39d7b..b44124d7 100644
--- a/Podfile.lock
+++ b/Podfile.lock
@@ -110,7 +110,7 @@ DEPENDENCIES:
   - SwiftLint
 
 SPEC REPOS:
-  https://github.com/CocoaPods/Specs.git:
+  trunk:
     - Firebase
     - FirebaseAnalytics
     - FirebaseAnalyticsInterop

diffが出ていることからも分かる通り、どうやらローカルのSpecレポが更新されていないためPodfile.lockも同様に更新されていなかったのが全ての元凶でしょう。つまり、CircleCI上ではCocoaPodsの最新のSpecレポを使ってpod installが実行されることによりPodfile.lockが更新されて、cocoapods-v1-{{ checksum "Podfile.lock" }}の値が更新前と更新後で異なってしまったことが原因だと考えられます。ちなみにここで出ているtrunkはメインSpecレポの新しい名前らしいです。

github.com

ということで、diffをコミットして再度CIを回すと無事ビルドが成功するようになりました🚀

てな感じで本日も以上になります🍺

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Swift】ClosureのCapture List再入門

Capture Listとは?

キャプチャリストは、よくClosure内で強参照によるインスタンスを保持を防ぐ下記のようなもので、Closure内で、ある値のキャプチャ方法を指定する役割があります。

let hoge = {[weak self] in
    self.hoge()
}

キャプチャ方法

キャプチャ方法は主に下記の3つがありますが、最初の2つがインスタンスを弱参照するキャプチャ方法になります。

[weak xxx]

[weak xxx]は対象となるオブジェクトを弱参照でアクセスすることを指定します。つまりアクセスする際にオブジェクトのインスタンスが解放されている可能性があるので、値はオプショナルになります。これは非同期処理のコールバックなんかでよく使いますよね。

let hoge = {[weak self] in
    self?.hoge()
}

[unowned xxx]

[unowned xxx][weak xxx]と同様に対象となるオブジェクトを弱参照でアクセスすることを指定します。weakとの違いは、オブジェクトを暗黙的にアンラップすることです。つまりインスタンスが解放されている場合に[unowned xxx]でキャプチャしたオブジェクトにアクセスするクラッシュします。ちなみにselfをキャプチャする場合には、明示的にselfを記述する必要がなくなります。

let hoge = {[weak self] in
    self.hoge()
}
let hoge = {[weak self] in
    hoge() // self.hoge
}

[xxx]

[xxx]は前述した二つとは異なり強参照でキャプチャされ、暗黙的にオブジェクトを参照することができます。

let hoge = {[self] in
    hoge() // self.hoge
}

余談

Capture List に追加されたオブジェクトは基本的にletに相当するImmutableなので、インスタンスの情報を書き換えるようなことはできません。

var singer = Singer()
let hoge = {[singer] in
    singer = Singer() // Cannot assign to value: 'singer' is an immutable capture
}

また、複数のオブジェクトをweakunownedとしてキャプチャしたい場合に下記のように書きたくなりますが、この場合2番目以降のオブジェクトは強参照されています。

let singer = Singer()
let singer2 = Singer()
let hoge = {[weak singer, singer2] in

}

どちらも弱参照にしたい場合には下記のように記述する必要があります。

let singer = Singer()
let singer2 = Singer()
let hoge = {[weak singer, weak singer2] in

}

参考

www.hackingwithswift.com

docs.swift.org

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Swift】ユーザにアプリのレビューリクエストをする

最近ユーザにアプリのレビューリクエストを行う対応をしたので、備忘録として残しておきます✍️

ちなみにこれ。

f:id:yum_fishing:20210322213420j:plain:w300

結論

iOS10.3以降はこれでできる。ちなみにこのリクエストダイアログは一年で3回までしか表示できないらしいので注意が必要。

import StoreKit

SKStoreReviewController.requestReview()

直接AppStoreでレビューを書いてもらう場合やiOS10.3未満でレビューリクエストするには、下記のようにAppStoreディープリンクを使います。xxxxxxにはアプリのApp Store IDを入力してください。

guard let writeReviewURL = URL(string: "https://apps.apple.com/app/idxxxxxxxx?action=write-review") else { fatalError("Expected a valid URL") }
UIApplication.shared.open(writeReviewURL, options: [:], completionHandler: nil)

てな感じで本日も以上になります🍺

参考

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【Firestore】セキュリティルールの便利な関数たち

get()

指定したパスのドキュメントの情報を取得します。例えばusersコレクションのhogeというドキュメントのnameフィールド値を取得したい場合は下記のようになります。

allow read: if get(/databases/$(database)/documents/users/hoge).data.name == 'hoge';

ちなみにパスの情報を変数で動的に変えたい場合は、$(変数)のようにして変数をエスケープします。

getAfter()

書き込みリクエストが成功した場合のドキュメント情報を取得します。getAfter()は使い方こそget()とほとんど変わりありませんが、取得するデータの性質が少し異なります。リクエストが仮に成功した場合のドキュメント情報を取得してくるので、主にwriteリクエストでの条件指定に使います。

allow create: if get(/databases/$(database)/documents/users/hoge).data.name == 'hoge2';

exists()

指定したパスのドキュメントが存在するかどうかのBool値を取得します。

allow read: if exists(/databases/$(database)/documents/users/hoge);

ちなみに前述した3つの関数には下記のような制限があり、制限を超えてしまうとアクセス拒否エラーが発生するので注意が必要です。

  • 単一ドキュメントに対するリクエストの場合は10個まで使用可能
  • 複数のドキュメントに対する読み取り、トランザクション、一括書き込みの場合は20個まで使用可能

てな感じで本日は以上になります🍺 後日、セキュリティルール の便利な変数たちをまとめようかと思います。

参考

firebase.google.com

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

Visual Studio Codeでのスニペット登録

今回は、Visual Studio Code でのスニペット登録を簡単に紹介します🧑‍🔧

ちなみにバージョンは1.54.3です。

それではやっていく

まずは、Code > Preferences > User Snippets を選択します。

f:id:yum_fishing:20210321125936p:plain

次に表示されたウィンドウから、スニペットを作成したい言語を選択します。今回はJavaScriptを選択しました。

f:id:yum_fishing:20210321130411p:plain

言語を選択すると下記のようなJsonファイルが表示されますので、ここにスニペットの設定を書いていきます。

{
    // Place your snippets for javascript here. Each snippet is defined under a snippet name and has a prefix, body and 
    // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
    // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the 
    // same ids are connected.
    // Example:
    // "Print to console": {
    //   "prefix": "log",
    //   "body": [
    //       "console.log('$1');",
    //       "$2"
    //   ],
    //   "description": "Log output to console"
    // }
}

書き方はコメントに記載されている通りですが、簡単に解説すると、prefixにはトリガーとなる文字列を、bodyには展開するコードを、descriptionにはスニペットの説明をそれぞれ書いていく感じです。$1$2などは、展開後のカーソルの位置を設定するための値です。

てな感じで本日も以上になります🍺

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com

【JavaScript】特定のプロパティを除いたオブジェクトを作成

本日もJavascriptの軽いメモです👷‍♀️

結論

下記の { a, b, ...cObject} のように、{ 取り除くプロパティ, ...オブジェクト名} = 元のオブジェクト名 とすることで特定のプロパティを除いた新しいオブジェクトを生成できるようです👀

const abcObject = { a: "a", b: "b", c: "c" };
const { a, b, ...cObject } = abcObject;
console.log(cObject); // { c:  'c' }

という感じで本日も以上になります🍺

参考

qiita.com

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com