[iOS] Xcode が開かない、もしくは開いてもずっとプロジェクトのロードが終わらない場合の対処法

前提条件

Mac: macOS Catalina 10.15.6
Xcode: Version 12.4 (12D4e)

これで直る場合があります

Xcode を終了した後、以下のディレクトリを削除すると直る場合があります。

~/Library/Saved Application State/com.apple.dt.Xcode.savedState/

参考) Stack Overflow のこの回答

最後に

これで直れば幸いです!

[iOS] ‘weak’ cannot be applied to a property declaration in a protocol というエラーがあった場合の対応

はじめに

結論から言うと、Protocol での記述は weak は特に気にしなくてよさそうです。
Protocol では実クラスのプロパティが weakstrong かには関心がありません。


Swift において、weak var のプロパティを持つプロトコルを定義したくなった場合、
本ポストのタイトルのようなエラーが出ると思います。

例えば delegate を持つ Protocol を定義したい場合があると思います。

protocol BProtocol {
    weak var delegate: AProtocol? { get set }
}

実クラスでは delegate は weak var で宣言しないと循環参照となってしまいます。

ですが、Protocol での記述は weak は気にしなくてよさそうです。

簡単なプログラムで検証します。

前提条件

Mac: macOS Catalina 10.15.6
Xcode: Version 12.4 (12D4e)

検証

先に言ったように、Protocol での記述は weak は特に気にしなくてよさそうです。

以下のようなサンプルプログラムで検証します。

protocol AProtocol: AnyObject {
    func doSometing()
}

final class A: NSObject, AProtocol {
    var b: B?
    func raiseB() -> B? {
        b = B()
        b?.delegate = self
        
        return b
    }
    func doSometing() {}
}

protocol BProtocol {
    var delegate: AProtocol? { get set }
}

final class B: BProtocol {
    weak var delegate: AProtocol? // <- weak を外すと循環参照になります
    
    func printDelegate() {
        print("delegate: \(delegate)")
    }
}

ここで注目したいのが class B が持っている delegate です。

delegate プロパティはAProtocol に準拠したクラスの変数です。

BProtocol では delegate プロパティを weak 宣言していません(できません)。
が、実クラスのBでは weak 宣言しています。

最も単純なコードだと、A のインスタンスがnil になった時点で、
Bが保持している delegate プロパティも weak であれば、 nil になるはるはずです。

以下のようなコードを書いたとします。
「コードX」とします。

// コードX

var a: A? = A()
let b = a?.raiseB()
print("最初のprint")
b?.printDelegate()
        
a = nil
print("二番目のprint")
b?.printDelegate()

a = nil を実行された時点で、b が保持している delegate プロパティも nil になるはずです。

実際にそのようになります。

最初のprint
delegate: Optional(<HogeApp.A: 0x6000010f4320>)
二番目のprint
delegate: nil

一方、実クラスのB の delegate から weak を外してみます。
BProtocol では delegate プロパティを weak 宣言していません(できません)。

final class B: BProtocol {
    var delegate: AProtocol?
    ... 略 ...
}

これで「コードX」を実行します。

すると以下のようになります。

最初のprint
delegate: Optional(<HogeApp.A: 0x6000018b4530>)
二番目のprint
delegate: Optional(<HogeApp.A: 0x6000018b4530>)

つまり、Protocol では実クラスが weakstrong かには関心がない
ということになります。

当該のプロポーザル

Swift evolution に以下のプロポーザルがあります。
0186-remove-ownership-keyword-support-in-protocols.md

Protocol は weak だろうが strong だろうが宣言できるが、
実クラスではそれに従う必要はないため、混乱を招いてしまう。
そのため、そもそもProtocol ではそれらの宣言をできないようにするべきだ、と書かれています。

最後に

というわけで Protocol は所有権に関心がないということが分かりました。

以上です。
お疲れ様でした!

[デザイン] 欧文書体(フォント)についてざっくり整理してみた

1.書体(Typeface)とフォント(Font)の違い

まずこれの違いについて確認します。

ざっくりと言えば、

  • 書体(Typeface): 文字の形の世界観を表す
  • フォント(Font): その文字たちを実際に表示・印字する際のより細かな規則を表す

ということかなと思います。

具体的に言うと、Helvetica や Times New Roman が書体です。
Helvetica Bold 50px や Times New Roman Regular 10px など、
大きさや線の太さなど表示・印字するときに必要な情報まで含めたのがフォントとなります。

他のブログを見ると
現在だとほぼ同義のように使われているため、(書体の専門家でなければ)それぞれ言い換えても全然伝わるでしょう
と言う感じのようですね。

1-1.フォントの語源

上記のように、現在だと「書体」との境界線が曖昧になりつつありますが、
昔ははっきりと使い分けがなされていたようです。

フォントとはその昔「活字印刷する際に利用する活字たちの一揃い」の事を指していたようです。

印刷するときには活字たちを一文字ずつ手作業で並べる必要があったと思いますが、
その際文字の大きさが違っていると、正しいものに入れ替えをする必要があり労力がかかりますね。

印刷する時にフォントを同じものにするには今よりはるかに注意が必要だったと思うので、
きちんと使い分けられていたのだと思います。

2.欧文書体の大まかな分類

分類方法は特に定まったものがなさそうなため、以降の分類は一例として捉えてもらえれば。

欧文書体はまず大きく分けて以下の4つに分けることができそうです。

  1. セリフ体 (ローマン体)
  2. サンセリフ体
  3. スクリプト体
  4. 装飾体

参考: Fonts.com: Type Classifications

3.セリフ体とサンセリフ体の違い

セリフ体とサンセリフ体の違い
  • セリフがあるのがセリフ体
  • セリフがないのがサンセリフ体

セリフ(Serif)とは、文字の端にあるひげのような装飾です。

サンセリフ(Sans-Serif)のサン(Sans)はフランス語で「無い」という意味になります。
ですのでサンセリフはセリフがない書体という意味になります。


では以下、先述の4つの分類について更に細かく分類します。

4.セリフ体(ローマン体)

セリフ体

上述の通り、文字の端にひげのような装飾のある書体です。
和文書体で言うと明朝体に近いです。

4つに分類する場合、例えば以下のような分類ができます。

  1. オールドローマン
    1. 主な特徴: 古めかしい印象。
      ひげが太い。oが傾いている、eの横棒が斜めになりがち など。
      更に2つに細分化する場合は以下のようにもできる。
      1. ヴェネチアン・ローマン
        1. 主な特徴: e の横棒が斜め、oが傾いている、ひげが太い(Bracket Serif) など
        2. (Adobe) Jenson
        3. Centaur
        4. 他にも TrajanITC Berkeley OldstyleBriosoHightowerSchneidlerLutetiaSpira など
      2. オールド・ローマン
        1. 主な特徴: e の横棒が真横、oが少し傾いている、ひげが太い(Bracket Serif) など
        2. (Adobe) Garamond
        3. Caslon
        4. 他にも PalatinoBemboSabonVan DijckGalliardGranjonGoudy Old StyleMinion など
    2. 英語版のWiki: オールド・ローマンの一覧
  2. トランジショナル・ローマン
    1. 主な特徴: オールド・ローマンと以下のモダン・ローマンの中間。
      変遷中という意味でトランジショナル。トラジショナル(伝統的)という単語ではない。
      e の横棒が真横、oが直立、ひげがやや太い(Bracket Serif) など。
    2. Baskerville
    3. Times New Roman
    4. 他にも CenturyFreight TextFournierBookmanPerpetuaGeorgiaPlantin など
    5. 英語版のWiki: トランジショナル・ローマンの一覧
  3. モダン・ローマン
    1. 主な特徴: 都会的で洗練された印象。
      e の横棒が真横、oが直立、ひげがとても細い(Hair Serif) など
    2. Didot
    3. Bodoni
    4. 他にも Walbaum など
    5. 英語版のWiki: モダン・ローマンの一覧
  4. スラブ・セリフ
    1. 主な特徴: エジプシャンとも呼ばれる。
      モダン・ローマンとは逆で、ひげがとても太い(Slab Serif)。
    2. Rockwell
    3. Egyptienne
    4. 他にも ClarendonTrilbyCourierPrestige EliteIonicMemphisBetonSerifa など
    5. 英語版のWiki: スラブ・セリフの一覧

1.ヴェネチアン・ローマン から3.モダン・ローマンに向かって年代が新しくなります。

セリフ体の詳細な解説は以下のサイトたちがとても参考になりそうです。

5.サンセリフ体

サンセリフ体

こちらは文字の端にひげがない書体となります。
和文書体で言うとゴシック体に近いです。

4つに分類する場合、例えば以下のような分類ができます。

  1. グロテスク
    1. 主な特徴: エレガントなセリフ体との対比からグロテスク。ゴシックとも呼ばれる。線の太さが不均一。
    2. Akzidenz Grotesque
    3. Venus
    4. その他にも Franklin GothicDINMmonotype GrotesqueNews Gothic など
    5. 英語版のWiki: グロテスクの一覧
  2. ネオグロテスク
    1. 主な特徴: 線の太さがより均一、没個性的、汎用的
    2. Helvetica
    3. Folio
    4. その他 UniverseApercuRobotoArialAcumin など
    5. 英語版のWiki: ネオグロテスクの一覧
  3. ジオメトリック
    1. 主な特徴: より幾何学的な形。そのため可読性はあまりよくない場合が多い
    2. Futura
    3. Kabel
    4. その他にも Bernhard GothicErbarAvenirGothamAvan Garde GothicBrandon Grotesque など
    5. 英語版のWiki: ジオメトリックの一覧
  4. ヒューマニスト
    1. 主な特徴: セリフ書体の影響が強く、人間的。
    2. Gill Sans
    3. Optima
    4. 他にも FrutigerJohnstonMyriadTahomaTrebuchet MSVerdanaOpen SansSkia など
  5. 英語版のWiki: ヒューマニストの一覧

サンセリフ体の詳細な解説は以下のサイトたちがとても参考になりそうです。

6.スクリプト体

スクリプト体

こちらは手書きに似せたフォントになります。
2つに分類する場合、例えば以下のような分類ができます。

  1. フォーマルスクリプト
    1. 主な特徴: エレガントで風格がある。羽根ペンのような筆跡となる。
    2. Bickham Script
    3. Shelley Script
    4. その他にも Snell ScriptKuenstler ScriptSnell Roundhand など
  2. カジュアルスクリプト
    1. 主な特徴: 親しみやすく格式ばらない形。ペンやブラシで素早く書いたような筆跡となる。
    2. Brush Script
    3. Kaufmann
    4. その他にも ZapfinoMistral など

読みづらくはあるので、地の文で使うことは少なそうで、
表彰状や招待状などエレガントで高級な世界観を表現するときによく使われるようです。

ちなみにとても荘厳な趣のあるブラックレターという書体も、スクリプト体の一部です。

ブラックレター

スクリプト体の詳細な解説は以下のサイトたちがとても参考になりそうです。

7.装飾体

装飾体

看板や見出しなどに使用される、印象的なフォント。
インライン、グランジ、ステンシルなど様々な形態が内包されています。

  1. Ostrich Sans Inline
  2. Astonished
  3. Stencil

などがあります。

多様なため一括りに語れませんが、
インパクトを与えたいキャッチコピーなどに使うのがよさそうです。

可読性はよくないと思うので、通常は本文に使うのは避けた方がいいかもしれません。

8.最後に

ざっくりとまとめてみました。

ロゴやWebのデザインに活かせればと思います!

[iOS] BDDフレームワークのQuick について概要をまとめる

環境

  • Xcode 12.4
  • Swift 5.3.2

Quick とは

Quick is a behavior-driven development framework for Swift and Objective-C. Inspired by RSpecSpecta, and Ginkgo.

https://github.com/Quick/Quick#readme
振る舞い駆動開発のためのSwift や Objective-C 用のテストフレームワークです。
振る舞い駆動開発とは何ぞや、ということについては上のリンクの他、
本ブログの以下ポストなどを参照していただければと思います。
Web-y.dev: 振る舞い駆動開発(Behavior-Driven Development)とは

インストール方法

公式ドキュメントがあります。
https://github.com/Quick/Quick/blob/main/Documentation/en-us/InstallingQuick.md

SwiftPM でもインストール可能でしたが、動作が不安定だった
(失敗したテストを再度回しても何のテストも実行されないなど)ので、
個人的には、SwiftPM以外の方法でインストールするのがいいかなと思います。

使い方のイメージ

Web-y.dev: 一つ一つの振る舞いテストのお作法 に書いた通り、
一つ一つの振る舞いテストは以下の3ステップをひとまとまりとして記述していきます。

  1. Given: システムがこの状態の時(前提条件)
  2. When: この動作をすると
  3. Then: このような振る舞いをする

Quick はこの3ステップを、人が読みやすい形で記述することをサポートしてくれる
フレームワークとなっています。

Quick でテストを書くときのお作法として Arrange, Act, Assert の手順が挙げられていますが、
上記の Given, When, Then と同義かなと思います。
https://github.com/Quick/Quick/blob/main/Documentation/ja/ArrangeActAssert.md#effective-tests-using-xctest-arrange-act-and-assert

具体的な使い方

Git に公式ドキュメントがあります。
https://github.com/Quick/Quick/blob/main/Documentation/ja/QuickExamplesAndGroups.md

そちらを見てもらうのが早そうですが、イメージだけ書いておきます。

import Quick
import Nimble
@testable import HogeApp

class HogeAppSpec: QuickSpec {
    override func spec() {
        describe("メイン画面") {
            var vc: MainVC!
            beforeEach {
                vc = MainVC()
                vc.load()
            }
            it("メインコンテンツが表示される") {
                expect(vc.collectionView.cellForItem(at: indexPath) as? ContentCell)
                    .toEventuallyNot(beNil()) // ここは Nimble で書ける
            }
        }
        describe("アカウント画面") {
            beforeEach {
                // 下2つのcontext の事前準備の共通処理をここに書ける
            }
            context("ログインしている場合") {
                it("アカウント情報が表示される") {
                    
                }
            }
            context("ログインしていない場合") {
                it("ログイン画面が表示される") {
                    
                }
            }
        }
    }
}

spec() メソッドをオーバーライドし、その中でクロージャをネストさせてテストを書いていきます。

describe もしくは context 内のクロージャで、
Given: システムがこの状態の時(前提条件)」と「When: この動作をすると」まで記述します。
(describecontext に違いはなく、ただのエイリアスです)

it 内のクロージャで「Then: このような振る舞いをする」を記述します。

beforeEachafterEach で共通の事前準備、後始末を書けます。

実行すると

上記のテストコードを実行すると、以下の3つのテストケースが順番に実行されます。

  1. アカウント画面__ログインしていない場合__ログイン画面が表示される
  2. アカウント画面__ログインしている場合__アカウント画面が表示される
  3. メイン画面__メインコンテンツが表示される

テストファイルの上から実行されるのでは無く、テストケース名の辞書順に実行されます。

メリットとデメリット

僕の思うメリットとデメリットは以下です。

メリット

  1. describecontext で処理を共通化するため、ボイラーテンプレート的な事前準備などのコードを複数書かなくて済む
  2. describe, context, it の組み合わせにより仕様書として読みやすい形になる
  3. 構造化することで、特に条件で出し分ける箇所の仕様が読み取りやすくなる

デメリット

  1. うまく仕様を分解して構造化できなかった場合、テストコード自体がスパゲッティコードになる
  2. 行数が多くなると、いまどの describe 内のコードなんだっけ?と混乱しがち
  3. func にテストを書かないため、ファイル内のテストケース一覧が参照できない

注意しないとデメリットにあるようにけっこうスパゲッティコードになりがちかなと思います。

そのような場合、テストコードの設計見直しや、
describecontext やテストファイルの分割などを検討するとよいのかなと思います。

銀の弾丸ではないので、本格導入する前に一度試してメリデメを把握するのがよいかと思います!

便利な機能

特定のテストケースを無効にする

公式の説明はこちら。
https://github.com/Quick/Quick/blob/main/Documentation/en-us/QuickExamplesAndGroups.md#temporarily-disabling-examples-or-groups

xdescribexcontextxit のように先頭に x を付けると、
そのクロージャ内のテストを無効にできます。

特定のテストケースのみ有効にする

公式の説明はこちら。
https://github.com/Quick/Quick/blob/main/Documentation/en-us/QuickExamplesAndGroups.md#temporarily-running-a-subset-of-focused-examples

fdescribefcontextfit のように先頭に f を付けると、
そのクロージャ内のテストのみを有効にできます。

テストを共通化する

it の内容がボイラーテンプレート的に同じような内容になる場合、
その部分も共通化することができます。

公式の説明はこちら。
https://github.com/Quick/Quick/blob/main/Documentation/en-us/SharedExamples.md

SharedExample という機構を使います。

以下のようなイメージで使います。

class HogeAppSpec: QuickSpec {
    override func spec() {
        describe("アカウント画面") {
            context("ログインしている場合") {
                itBehavesLike(HogeSharedExamplesConfiguration.accountViewExample) {[
                    HogeSharedExamplesConfiguration.faqKey: "ログインのFAQを表示"
                ]}
            }
            context("ログインしていない場合") {
                itBehavesLike(HogeSharedExamplesConfiguration.accountViewExample) {[
                    HogeSharedExamplesConfiguration.faqKey: "ログインのFAQを表示"
                ]}
            }
        }
    }
}

class HogeSharedExamplesConfiguration: QuickConfiguration {
    static let accountViewExample = "アカウント画面"
    
    static let faqKey = "faq"
    
    override class func configure(_ configuration: Configuration) {
        var faq: String!
        sharedExamples(accountViewExample) { (sharedExampleContext: @escaping SharedExampleContext) in
            it("情報が正しい") {
                faq = sharedExampleContext()[faqKey] as? String
                expect(faq).to(equal("ログインのFAQを表示"))
            }
        }
    }
}

QuickConfiguration を継承したクラスを作成し、共通する it の処理を書いていきます。

で、テストする側は it で呼び出すのでは無く、代わりに itBehavesLike を使います。
テスト対象の値などを itBehavesLike のクロージャ内に辞書形式で書き、SharedExample に渡します。

サンプルではString を渡していますが、それ以外の型ももちろん利用可能です。

SharedExample 側は sharedExampleContext()[キー名] のような形で値を取り出し、
あとはそれを元にテストしていきます。

ちなみに、SharedExample 内には、以下のように it 以外も記述可能です。

class HogeSharedExamplesConfiguration: QuickConfiguration {
        sharedExamples("hogeExample") { (sharedExampleContext: @escaping SharedExampleContext) in
            beforeEach {   
            }
            context("hogeContext") {
                it("hogeIt") {
                }
            }
        }
    }
}

けっこうコストがかかる書き方のため、必要に応じて使うとよさそうですね!

最後に

Quick についてつらつらっと書いてみました。

本文に記した通り、銀の弾丸ではないと思うので、本格導入の前に一度試すのがよいのかなと思います。

他にもこんな便利な機能があるよ、というのがあればコメントに書いていただければと思います!

以上です。
お疲れ様でした!

振る舞い駆動開発(Behavior-Driven Development / BDD)とは

1.はじめに

テストコード。

開発期限が迫っているため書く余裕がない、ということはママあると思いますが、
個人的には書けるなら書いた方がいいかなと思っています。

テストを書ける状態のプロダクトコードになっていないなら、まずはリファクタから頑張る。。!
Baby-Steps で KAIZEN していくしか。。!


本ポストではテストコードを書くメリデメを含め、TDDの一流派のBDDについて書いていきます。

2.テストコードを書くメリデメ

個人の見解ですが、以下のようなメリデメがあるのではと考えています。

  • メリット
    • テストで担保されている箇所に関しては恐れることなくロジック変更ができる
    • 仕様を把握しないとテストが書けないため、逆に言うとテストを書く段階で仕様の認識漏れが減る
    • テスタブルなコードになるため、コードの結合度や凝集度がより適切になり、コードの品質が高くなる
  • デメリット
    • プロダクトコードのみを書く場合と比べると、短期的な開発スピード(特にコードをまずテスタブルに改修する場合)はもちろん遅くなる
    • 学習コストがそれなりにある。チーム内でテストコードのお作法を合わせるのもコストがかかる
    • 記述するコードが増えるため、コードレビューの負担がその分増える。巨大なプルリクエストだとテストコードまで見る余裕がないことも。。

3.TDDについて思うこと

短期的には成果が見えづらいため、焼畑的な開発を続けざるを得ない状況下においては、
取り入れるのが非常に難しいかなと思っています。

ですが長期的に見ると、低品質のコード仕様の認識漏れバグ修正などでの手戻りが減り、
スパゲッティコード化による属人化開発速度の低下を防げるため、
プロダクト的にも、またもちろん精神衛生上にもとてもよいのではと思っています。

で、プロダクトコードを書いてからテストコードを書くのも速くていいですが、
可能であればテストコードを書いてからプロダクトコードを書いた方が品質が高くなるのでは
ないかと思っています。

プロダクトコードがある程度の品質で動いているなら
テストが書けない設計になっていても、多少サボってもいいと思ってしまうのが人間なのかなと🤔

もちろん、最初からテスタブルなコードを書けるなら順番はどちらでもいいと思います。
が、それなら最初からテストを書いた方が、リファクタによる手戻りの可能性は減りそうですね。

テストを書いてからプロダクトコードを書く開発手法をTest-Driven Development (TDD) と呼びます。

本ポストでは以下、TDDの一流派である Behavior-Driven Development (BDD) について
概要を書いていきます。

4.振る舞い駆動開発(BDD)とは

BDDはTDDの一流派ともいえますが、TDDに対し以下の実現のための原則や工夫が加えられています。

– テストを「振る舞い」(機能的な外部仕様)の記述に特化させる

– ユーザーの要求やアーキテクチャの設計仕様といった、より上位のインプットとTDDのテストにつながりを持たせる

https://www.atmarkit.co.jp/ait/articles/1403/05/news035_3.html

「コード自体の振る舞い」や「システム・アプリの振る舞い」に焦点を当てたテストを書いていくTDDの一流派です。

僕のイメージとしては、

  • テストファイルに仕様を書いていく
  • つまり、テストファイルを読めばコードやシステムの外部仕様が理解できる

ということを目指している開発手法、と捉えています。


目指しますが、個人的には、
ある程度以上複雑なアプリの場合、仕様を完璧に網羅するのは難しいと思うので、
コスパを鑑みた上である程度で妥協するのが現実的にはコスパがよいのではとも感じています。

特に動的なコンテンツ、インタラクティブなコンテンツは、
とかく考えうる仕様・条件・動きが膨大になりがちです。

妥協する場合、どの程度で妥協するかはチームやプロジェクト内で、
「価値のあるテストとは? 価値のないテストとは?」などについて話し合い、
最適な落とし所の認識を合わせる必要があるかと思います。

5.振る舞い駆動開発のお作法

そもそも、振る舞い駆動開発(というより派生元のテスト駆動開発)は以下の3ステップの繰り返しで進めていきます。

  1. Red
  2. Green
  3. Refactor

5-1.Red

Red では振る舞いテストを書きます。
システムがこの状態の時この動作をするとこう振る舞う、というテスト(つまり仕様)を書きます。

なので、ここではテストというよりは実質、仕様を書いていくという意識に近いかなと思います。

で、実装がまだないため、テスト実行すると必ずこけます
テストがこけているのでIDEでは赤ポチがついているはずです。
なのでRed。

5-2.Green

次にGreenです。
こけていたテスト(つまり仕様)を通すように実装をします。
実装が終わった後にテストを回すと、赤ポチが緑ポチになります。
なぜならテスト(つまり仕様)を満たすように実装したからです。

5-3.Refactor

最後に、必要であれば実装をリファクタリングします。

これでおそらく、ある仕様を満たす最小限の実装ができたかと思います。

あとはこれの繰り返しです!

え、複数の出し分けの仕様があるのにこのままだと一つの条件しか考慮できておらず、マジックナンバーがある?
次のRed, Green, Refactor できれいに出し分け/リファクタすればいいでしょう!


こんな感じで、仕様を満たす最低限のロジックのみを書き、
それをテンポよく繰り返し改修していく。
仕様の解像度を徐々に上げていくような進め方。

それがBDD(TDD) かなと僕は捉えています。

6.一つ一つの振る舞いテストのお作法

一つ一つの振る舞いテストは以下の3つの手順で記述できると言われています。
Agile Allianceのメンバー Martin Fowler さんのブログより。

  1. Given: システムがこの状態の時(前提条件)
  2. When: この動作をすると
  3. Then: このような振る舞いをする

これらを1セットとして、一つ一つの振る舞いに対してのテストを記述できるはずです。

7.最後に

今ではテストを書かないとなんだか気持ち悪いと思えるようになりましたが、
当初はもちろん戸惑いました。

もちろんTDD/BDD が絶対正しいという訳ではなく、
コードの品質と、ある期間内での開発スピード感、どちらを重視するかによって
最適解は変わってくると思います。

以上、BDDについてでした.。
お疲れ様でした!

[iOS][Xcode] error: Building for iOS Simulator, but the linked framework ‘Hoge.framework’ was built for iOS.というエラーが出たときの対処法

はじめに

Xcode でビルドしようとした際にタイトルのエラーに遭遇したので対処法を書いていきます。

テストターゲットにCarthage で framework を追加しており、アプリをビルドしようとした際に遭遇しました。

前提条件

Mac: macOS Catalina 10.15.6
Xcode: Version 12.4 (12D4e)
Carthage: 0.36.0 (後半で0.37.0 以降にアップデートします)

現象

ビルド時に以下のようなエラーが出ました。

テストビルドのエラー

テキストに起こすと以下。

error: Building for iOS Simulator, but the linked framework 'Quick.framework' was built for iOS. (in target 'SampleApp02Tests' from project 'SampleApp02')

SampleApp02 というアプリなんですが、それのTestターゲットに
Quick.framework を追加した状態でのエラーです。
(このエラーメッセージはCarthage のバージョンで少し異なってくるかもしれません)

iOSシミュレータをビルドしているが、
リンクされているのはiOS用(=シミュレータ用ではない)のframeworkです

と怒られています。

簡単な対処法(ただし根本的ではない)

Stackoverflowに回答があります

Build SettingsValidate Workspace の設定を一度 Yes にし、その後 No に変更すればOKです。
デフォルト表示は No ですが、実際の挙動はどうやら Yes (Error) となっているようです。

Yes のままでもエラーは回避できますが、ビルド時にワーニングが出ます。

ただ、この対処法は少なくとも「フレームワークの検証をしない」という状態になるため、
将来的にはビルドできなくなる可能性も出てくるのかなと思います🤔

より根本的な対処法

そもそもの原因

そもそも Validate Workspace が Yes (Error) でエラーになるのは、
フレームワークの検証で引っかかっているからのようです。

どういうことかと言うと。

Carthage 0.36.0 で作成された framework は、
その中に実機用やシミュレータ用の複数のバイナリが含まれています。
それらが1つのframework として生成されます。

これはuniversal framework もしくは、より直感的に fat framework と呼ばれています。

fat framework のイメージは以下が非常に分かりやすいと思います。
Qiita: XCFrameworksに対応したCarthageを使ってみた CarthageがIntel Mac上でどのように動いていたか

そして、Xcode12.3 からはデフォルト(つまりValidate Workspaceが Yes(Error)の状態)で
fat framework を弾くようになりました。

そのため、ビルドができなくなったようです。

ちなみにアプリのビルド時、Carthage の copy-frameworks スクリプトを実行していると思いますが、
そこでは fat framework を元に

  • 実機ビルドなら実機用のみの framework を出力する
  • シミュレータビルドならシミュレータ用のみの framework を出力する

という処理をしているようです。

そこで XCFramework

fat framework が弾かれるようになった。
ではどうするか。

fat framework をやめ、XCFramework を使います。

XCFramework とは何か。
イメージを掴むには、中身を見るのが手っ取り早そうです。

XCFramework の中身

このようにXCFramework の中には複数の framework が含まれています。
ios-arm64-armv7 がiOS用、ios-arm64_i386_x86_64-simulator がシミュレータ用のframework かな🤔

このXCFramework を、既存の framework の代わりにXcode に組み込むことで、
ビルド時にXcode が適切なアーキテクチャのframework を選択してリンクするようになります。

ということは先に述べた copy-frameworks スクリプトも不要になります🙌

XCFramework を使う

主流になりそうだし使った方がよさそう、ということでCarthage でXCFramework を作ります!

そのためには Carthage 0.37.0 以降が必要なので準備しましょう。

準備できたらあとは --use-xcframeworks オプションを付けて
carthage コマンドを打つだけです。

carthage update --use-xcframeworks --platform iOS

これ以降の導入手順は難しくないです。

DevelopersIO: [iOS] CarthageでもXCFrameworkが使いたい
Qiita: XCFrameworksに対応したCarthageを使ってみた CarthageでXCFrameworksをビルドする
などが参考になりそうです。

最後に

以上です。

可能ならワークアラウンド的な方法ではなく根本的な対応をすると、
今後困る可能性が減りそうですね🙌

お疲れ様でした!

[Mac][Homebrew] brew update で unshallow を求められたときの対処法

はじめに

Carthage 0.38.0 をインストール しようとしたら、
既に0.37.0 が入っているしそれが最新だよと言われました。

$ brew upgrade carthage
Warning: carthage 0.37.0 already installed

Homebrew 自体のアップデートから Carthage をアップデートするまで
諸々対処したのでまとめます。

前提条件

Mac: macOS Catalina 10.15.6

Homebrew をアップデートする

まずは Homebrew 本体をアップデート。
brew update コマンドを打ちます。

$ brew update
Error: 
  homebrew-core is a shallow clone.
  homebrew-cask is a shallow clone.
To `brew update`, first run:
  git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
  git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow
These commands may take a few minutes to run due to the large size of the repositories.
This restriction has been made on GitHub's request because updating shallow
clones is an extremely expensive operation due to the tree layout and traffic of
Homebrew/homebrew-core and Homebrew/homebrew-cask. We don't do this for you
automatically to avoid repeatedly performing an expensive unshallow operation in
CI systems (which should instead be fixed to not use shallow clones). Sorry for
the inconvenience!

いきなり怒られました!🤯

brew update を実行するためにはまず以下のコマンドを打て、と言われています。

  git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
  git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow

その下に書かれているのは、意訳すると以下のような感じです。

GitHub の要請で shallow clone にしていました。
unshallow は時間などのコストがかかるため、CIで毎回自動実行されるとやばいことになるので、
手動で unshallow してもらってる状況です。
(というかそのような場合はshallow clone しないように修正すべきですね)

そもそも shallow clone とは🤔

shallow clone とは

古いコミットを切り捨ててgit clone する機能のようです。
公式ブログに説明がありました。

シャロークローンは、 git clone の --depth=<N> パラメータを使って
コミット履歴を切り捨てます。

https://github.blog/jp/2021-01-13-get-up-to-speed-with-partial-clone-and-shallow-clone/

clone するのが早くなりそうなので一見良い機能に見えますが、そうでもなさそうです。
非常にピーキーな機能のようなので、日々の業務では積極的に採用しない方がよさそうですね。

これらの理由から、
その後すぐにリポジトリを削除するようなビルドのケースを除いて、
シャロークローンはお勧めしません
シャロークローンからのフェッチは、
メリットよりもデメリットの方が大きくなることがあります。

https://github.blog/jp/2021-01-13-get-up-to-speed-with-partial-clone-and-shallow-clone/

unshallow コマンドを打つ

Homebrew が指定した2つのコマンドを打ちます。

$ git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
略
From https://github.com/Homebrew/homebrew-core
   fe112d0e1b..259931b1d2  master     -> origin/master
$ git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-cask fetch --unshallow
略
From https://github.com/Homebrew/homebrew-cask
   05081b9fd0..2e49b12876  master     -> origin/master

数分待てば完了します。

改めて brew update を打つ

$ brew update
略
You have 12 outdated formulae and 2 outdated casks installed.
You can upgrade them with brew upgrade
or list them with brew outdated.

正常に完了したようです。

続いて Carthage をアップデートします!

パッケージをアップデートする

brew upgrade と打ちます。

$ brew upgrade
略
==> Upgrading carthage 0.37.0 -> 0.38.0 
略
==> Pouring carthage--0.38.0.catalina.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d
==> Summary
🍺  /usr/local/Cellar/carthage/0.38.0: 8 files, 7.7MB
略

0.37.0 しか入れられなかったのが、0.38.0 を入れることに成功しました!

$ carthage version
0.38.0

最後に

以上です。

Carthage を 0.36.0 から 0.38.0 に上げるのにそこそこ苦労しました。

  1. [Mac][Homebrew] brew reinstall XXX で /usr/local/lib/node_modules/npm/node_modules/.bin/ のパーミッションエラーが出る場合の対処法
  2. [Mac] UNIX のユーザとグループの世界観

本ポストも本当の目的の途中経過です。
環境構築は難しいですね 😇

お疲れ様でした!

[Mac][Homebrew] brew reinstall で /usr/local/lib/ 配下のパーミッションエラーが出る場合の対処法

はじめに

以下のコマンドを実行した際、エラーが出て完了できませんでした。

$ brew reinstall carthage
中略
==> `brew cleanup` has not been run in 30 days, running now...
Error: Permission denied @ apply2files - /usr/local/lib/node_modules/npm/node_modules/.bin/JSONStream

これの対処法をまとめます

前提条件

Mac: macOS Catalina 10.15.6

何が起こっているのか

ターミナルを見ると、brew cleanup をしようとして失敗しているようです。

`brew cleanup` has not been run in 30 days, running now...

brew reinstall, brew install, brew upgrade を実行すると自動で brew cleanup が走るようです。

brew cleanup is run periodically (every 30 days) and triggers for individual formula cleanup on reinstall, install or upgrade. 

https://brew.sh/2019/02/02/homebrew-2.0.0/

で、そこでパーミッションの問題で弾かれているようですね。

対処法

Stack Overflow に対処法がありました

以下のコマンドを実行します。

sudo chown -R ${LOGNAME}:staff /usr/local/lib/node_modules

/usr/local/lib/node_modules とその配下のファイルやディレクトリの所有権を
再帰的に<アカウント名>:staff に変更するコマンドです。

補足1. staff グループとは

「staff グループ」を初めて知ったんですが、
PCの管理者以外の触る人、という位置付けのグループのようです。

そもそもグループとはどういうものかあまりイメージできなかったので、
調べて本ブログの [Mac] UNIX のユーザとグループの世界観 にまとめました。

補足2. staff でよかったのか

Stack Overflow の一つ下の回答 には少し別のコマンドが書かれていました。

sudo chown -R $(whoami):admin /usr/local/* \
&& sudo chmod -R g+rwx /usr/local/*

/usr/local/ 配下の所有権や権限を全て変更するコマンドで、
個人的にちょっと怖かったのでこちらのコマンドは採用しませんでした。

が、こちらの回答の方が支持されているようですし、
Qiita などで様子を見る限りこちらでも問題はなさそうです。

むしろサーバなど共有PCで作業する場合は staff より admin の方がよいかもしれませんね🤔

試していませんが staff を admin に読み替えたコマンドを試してみるのもいいかもしれません。

$ sudo chown -R ${LOGNAME}:admin /usr/local/lib/node_modules

brew cleanup 成功

さて、前述のコマンドを打って改めて brew cleanup すると、成功しました!🙌

$ brew cleanup
略
Pruned 19 symbolic links and 318 directories from /usr/local

更に carthage を reinstall してみます。

$ brew reinstall carthage
略
==> Summary
🍺  /usr/local/Cellar/carthage/0.37.0: 8 files, 7.7MB

成功しました🍺

最後に

以上です。

Carthage は本当は0.37.0 ではなく 0.38.0 を入れたかったんですが、Homebrew の何かが古いのかな?
次回はupdate, upgrade まわりについてまとめようと思います。

お疲れ様でした!

[Mac] UNIX のユーザとグループの世界観、そしてwheel とは

はじめに

普段あまり意識しないMacのユーザやグループですが、
調べる機会があったのでコラム的な話も交えつつ概要を書いていきます!

世界観・外観を知ることで、
何が分からないか分からない状態から、何が分からないか分かる
くらいになる助けになればと思います。

前提条件

Mac: macOS Catalina 10.15.6

ことの発端

調べ物をしていたとき、adminstaff グループというものが出てきて、
admin とは何だ!となったところから始まります。

管理者なのでroot 的な権限を持てるグループか??などとも思いましたが、
後述しますが実際はけっこう違いました。

ユーザとグループについての僕の当初のイメージ

ユーザにはどんな種類があるでしょうか。
思いつくのは自分のアカウント、root、くらいかな……
サーバで開発していると他の人のアカウントもありますね。

グループは意識したことないのでよく分かりません。
というかなぜグループがあるのかもよく分かりません🤯

とっかかりとしてまず root ユーザについて調べます。

root ユーザとは

Linuxでは、あらゆる権限が与えられているユーザーを「root」あるいは「rootユーザー」「スーパーユーザー」と呼びます。rootというユーザー名でログインすると、あらゆるコマンドの実行やファイル操作ができるようになります。

https://www.atmarkit.co.jp/ait/articles/1706/02/news014_2.html

出典はLinux についての記事ですが、Mac と同じUnix系システムということでご容赦を。

root ユーザは何でもできます。
sudo コマンドを打つとパーミッションを突破して色々操作できるのは
その間だけroot ユーザになっているからなんですね。


ちなみに、sudo は super user do の略と思いきや substitute user do (代理人として実行)の略という説もあるようです。


少し調べると、初期状態では wheel グループに所属するのは root ユーザのみ、という情報がありました。
https://itectec.com/superuser/the-difference-between-the-default-groups-on-mac-os-x/

つまり、所謂 root 的な権限を持つ特権的なグループが wheel ということになりそうです。

微妙に見覚えのある wheel グループですが、本当に root ユーザだけなのか調べます。

wheel グループについて

以下のコマンドを打ちます。
参考) https://superuser.com/a/444218

$ dscacheutil -q group -a name wheel
name: wheel
password: *
gid: 0
users: root

wheel グループについての情報が表示されました。

password はそのままですがパスワードで、 * でマスクされています。
gid はグループIDで0。0なのでいかにも特権的な感じですね。
users がグループに所属しているユーザ。

確かにroot のみ所属しているようです。
やはり強力なグループのようです。

admin や staff グループは?

同じようにコマンドをうちます。

$ dscacheutil -q group -a name admin
name: admin
password: *
gid: 80
users: root <自分のアカウント> ... 
$  dscacheutil -q group -a name staff
name: staff
password: *
gid: 20
users: root <自分のアカウント> _serialnumberd ...

admin グループと staff グループには自分のアカウントが所属していました。

持っているMac が自分専用の場合 admin と staff の違いが実感しづらいかも知れません。

が、やはり admin グループの方が権限が強く
例えばアプリのインストールなどは admin グループの方のユーザしかできないようです。
参考) https://superuser.com/a/20430

自分のMac にはどんなグループやユーザが定義されているのか?

グループ一覧を見てみる

以下のコマンドで見てみます。

 $ dscl . list /GROUPS
_amavisd
_analyticsd
略
utmp
wheel

思っていたよりめっちゃ多かったです。
dscl . list /GROUPS | wc -l でカウントすると131個もありました。

_hogehoge のようなアンダースコアで始まるグループが多かったのですが、
触れるなよ的な圧を感じます。

mail や network や sys などもあり、用途によって色々グループ分けされているようです。

ユーザ一覧を見てみる

以下のコマンドで見てみます。

$ dscl . list /USERS
_amavisd
_analyticsd
略
<自分のアカウント>
root

やはりこちらも思ったより多く、dscl . list /USERS | wc -l でカウントすると103個ありました。

こちらもアンダースコアで始まるユーザが多いです。

daemon や nobody といったユーザがいましたが、
軽く調べたところ古いお作法だったりと個人的にはあまり有効な用途がなさそうに見えます。

ですので僕の場合、ユーザに関しては
root と自分や他人のアカウントのみ意識していればよさそうかなと思いました。

ユーザが所属しているグループ一覧を見てみる

ついでにこちらもコマンドで確認してみます。

$ groups root
wheel daemon kmem sys tty 略

root ユーザについて見てみると、所属グループは20個ほどでした。
131個の全グループに所属していると思ってましたが、そんなことはないんですね。

groups <自分のアカウント> を実行してみるのも面白いかもしれません。

ユーザとグループについての僕の今のイメージ

ユーザは root と自分や他人のアカウント。
あとは nobody とか daemon とか。

グループは権限の持ち方を束ねるもので、そこにユーザを振り分けることで
各ユーザに対する権限を、適切に効率的に管理することができる。

そもそもなぜwheel が特権グループか

けっこう気になるポイントで、
ここが意味不明なので覚えづらいという面があると思います。

wheel はホイール、車輪です。
なぜ車輪が特権グループなのか。

実は英語にはBig Wheel というスラングがあり、
それは大きな車輪の馬車に乗る人、つまり大きな権力を持つ者・組織の重要人物のことを指していたそうです。
参考) https://unix.stackexchange.com/a/1271

Unix はこのスラングを取り入れたと。

なるほどね!
諸説あるようですが、確からしさはありそう。

ということで wheel の意図ができたので、だいぶ覚えやすくなりました。

最後に

以上です。

ユーザとグループについて雲を掴むようなふわふわしたイメージしか持てませんでしたが、
今回調べてみてなんとなく輪郭は分かったと思います。

お疲れ様でした!

[Mac][Android] Android Studio のキーボードショートカットが効かなくなったときの復元手順

はじめに

Android Studio をアップデートしたらキーボードショートカットが全然効かなくなりました。
設定が勝手に変更されてそうなった場合があるので、復元手順を載せます。

秒で復元できます。

前提条件

MacOS Catalina 10.15.6
Android Studio 4.2

復元手順

まず「Android Studio」の「Preference」を開きます。

「Android Studio」の「Preference」を開く

Preference で Keymap を選択します。
詳細のところにプルダウンがあるので、そこで「macOS」を選択し、「apply」を押します。
以上です!

「macOS」を選択

ちなみに僕の場合はなぜか「IntelliJ IDEA Classic」が勝手に設定されていました。

最後に

以上です。
お疲れ様でした!