【iOS】RawReperesentableを使用してID間の使用ミスを避ける方法

はじめに

アプリを作成しているとクラスやStructの一意性の判別のためにidを良く使用します。 例えばUserを定義した場合、その一意性を決めるためにUser.idを定義します。 多くの場合はidはStringかIntで定義するかと思います。

idが一つだけならば良いのですが、 jobIdやgroupIdなど複数のIDを扱うようになると、 jobIdとgroupIdにString型を使用していた場合、それらのIdの入れ間違いが発生し得ます。 Idは一意性を担保するため、この間違いは大きな障害に繋がりかねません。

本記事ではこのような問題を発生させない方法を記載します。

環境設定

以下の環境を使用しています。

  • Xcode10.2.1
  • Swift 5.0.1

方法

import Foundation

typealias Identifiable = RawRepresentable & Codable & Equatable & Hashable

struct User: Codable {
    let id: ID
    let name: String
    let job: Job
    
    struct ID: Identifiable {
        typealias RawValue = String
        let rawValue: RawValue
    }
}

struct Job: Codable {
    let id: ID
    let name: String
    
    struct ID: Identifiable {
        typealias RawValue = String
        let rawValue: RawValue
    }
}

let userString = """
{
    "id": "11111",
    "name": "Tanaka",
    "job": {
        "id": "01",
        "name": "engineer"
    },
}
"""

let userData = userString.data(using: .utf8)!


let user: User
do {
    user = try JSONDecoder().decode(User.self, from: userData)
    print("User: \(user)")
    print("UserID: \(user.id.rawValue)")
    print("UserJobID: \(user.job.id.rawValue)")
} catch let error {
    fatalError(error.localizedDescription)
}

do {
    let encodedUserData = try JSONEncoder().encode(user)
    let encodedUserString = String(data: encodedUserData, encoding: .utf8)!
    print(encodedUserString)
} catch let error {
    fatalError(error.localizedDescription)
}

上記のようにすることで、 UserのidはUser.ID型、JobのidはJob.ID型となり別々の型となるためIDの間違いがなくなります。

IDをRawReperesentableに準拠させることで、文字列から直接User.ID型にDecodeしたり、User.IDから文字列としてのEncodeが可能になります。 また、ID型をEquatableとHashableに準拠させることで、ID自体の同値判定やDictionaryのkeyとしてID自体を使用することを可能にしています。

IDについては各Struct内に定義するのが良いかと思いますが、 IDとしてStringしか使用しないなどの取り決めがある場合は以下のようにすると定義回数が減らせるのでいいかもしれません。

struct ID<T>: Identifiable {
    let rawValue: String
}

struct User: Codable {
    id: ID<User>
}

またRawRepresentableに準拠する際には以下の書き方もできます。

struct ID: RawRepresentable {
    let rawValue: String
}

RawRepresentableについて

RawRepresentableに関して知識が曖昧だったため学習がてらまとめてみます。

  • rawValueとして指定された型へ、もしくは型から変換できる。
  • Enumの型としてString, Int, 不要小数点型(CGFloat, Floatなど)を指定した場合はCompilerが自動的にRawRepresentableを付与する。
  • OptionSet ProtocolはRawRepresentable Protocolに準拠している。

まとめ

IDの間違いは致命的なバグに繋がり得るため慎重な扱いが必要です。 IDを定義する際の選択肢として本記事の方法がお役に立てば幸いです。

参考

「Creative Selection Apple 創造を生む力」の感想

読んだ本 

Creative Selection  Apple 創造を生む力

Creative Selection Apple 創造を生む力

なぜ読んだか

私はiOSアプリエンジニアとして3年半ほど企業で勤めてきました。 日々アプリ開発を行う中で開発ドキュメントやWWDC動画を通じてヒントを得たり、 開発中にiPhoneiPadの実機を使用することもありAppleという企業に対して強い親しみがあります。 プライベートでもiPhoneを使用しており、Appleのプロダクトがどのような思想や組織文化によって作られているのか興味があり本書を読みました。

印象に残った内容

7つの要素

Appleでは以下の7つの要素を重視している。

  1. インスピレーション - 広い視野をもって発想し、様々な可能性を考える
  2. コラボレーション - 他者と協力し、互いの強みを活かし補完し合う
  3. テクニック - スキルを使って質の高い結果を得る。そして、常によりよい仕事ができるように励む
  4. 勤勉さ - つまらない仕事でも、必要なら手抜きや妥協をせずにやり抜く
  5. 決断力 - 難しい選択を、遅れたり、引き延ばしたりせずに行う
  6. テイスト - 見る目を養い、「魅力的でありながらまとまりのあるもの」をつくるバランスを見つける
  7. 共感力 - 他者の視点から世界を見、彼らの生活とニーズに適応するものをつくる。

Creative Selection

デモが開発プロセスの中心にある。明確かつ具体的なデモを行い、フィードバックに基づいて次にやるべきことを絞り込み、新たなデモを行い着実に前に進む、このことを「クリエイティブ・セレクション」と読んでいる。Appleのクリエイティブなプロダクトはひらめきによって一瞬で生む出されるものではなく、デモを中心とした「選択の繰り返し」によって完成するものである。このような進化論に基づく品種改良のようによりよい物を繰り返し選びとって改善を続けるプロセスがアップルの想像力の本質「クリエイティブ・セレクション」である。

ヒューリスティクス(経験則)とアルゴリズムのバランスをとる

Appleでは数値だけではなく、経験則を重視している。言い換えると、ヒューリスティクスアルゴリズムの「交差点」の仕事をしている。 何らかの決定をする際に、ユーザーがより使いやすいプロダクトにするためにデータと数値だけではなく、「五感」に訴える発想 が求められた。例えば、アプリアイコンのサイズ、アプリアイコンのタップからアプリが全面に表示されるまでのアニメーション時間などはチーム内で使用する中で経験則的に導かれた。

シンプルイズム

シンプルイズムはプロダクトデザインでも説明の際にもアップルのあらゆる場面で上位概念とされた考え方の一つである。ただでさえ忙しいユーザーにソフトウェアが原因で過負荷にならず、ユーザーから学びやすく、長い目でみて使いやすいプロダクトになるためにはシンプルにする必要がある。そのため、重要でない機能はカットし、ユーザーが考えなくても使えるようにする。プロダクトを開発していて難しい疑問が発生するようなら、その答えを探すのではなく、「どうしたらその疑問そのものをなくせるだろうか」と考える。

実用的な発明、製品づくりに関する基本的な概念

「デザインとは、どう機能するかだ」

感想など

なんとなくAppleに抱いていたイメージとあっているなというのが本書を読んだ最初の感想です。 細部のディテールにまで妥協しない製品開発への姿勢、ユーザー志向によるシンプルさの追求といった要素が、クリエイティブなAppleのプロダクトに結びついていることを理解しました。個人的に印象に残っているのが、デザインに対するAppleの考え方とデモを中心としたプロダクト開発プロセスです。

デザインに関しては、ともすれば綺麗なUI、綺麗なアニメーションにこだわりがちですが、「どう機能するか」を重視し、ユーザーからみてシンプルなUXを提供できることを追求する姿勢をもって業務に励んでいこうと思いました。

プロダクト開発プロセスにおいては、なにを目的としたデモなのかを明確にし具体的に表せることの重要性を学びました。 目的をシンプルに表現することによって、どこに注目すれば良いかが明確になり、具体的なデモであることでフィードバックが的確になります。結果として、次の改善点も明確になり、その繰り返しによって、より良いプロダクトの開発につながっていきます。

本書を読むことで普段使用しているiPhoneなどのAppleのプロダクトがどのような思想やプロセスで作成されているのか理解することができました。また、デザインに関する考え方や明確で具体的なデモを行うなど自身の業務においても活用できる点が多々ありました。 今後の自身の仕事にも活かしていきたいと思います。

備考

本書の中にiPhoneの時計の時間に関する小話がのっていました。 広告などにのっているiPhoneの時計の時間は「9:41」となっているそうです。 なぜかというと、iPhoneの発表された時刻が9:41であり、製品の時刻は発表と同じ時刻か、少なくともそれに近くなければいけないという発想により、この時刻を使用するのが伝統になっているからだそうです。実際にAppleの公式ストアのiPhoneの時計の時刻を見たら「9:41」となっており、ちょっとした感動を味わいました。

また、アップルの基調講演は、開始後40分くらいでヤマ場の製品紹介に入るように計画されているらしいです。 今年のWWDCも来月に迫ってきていますが、どのような発表がされるのか今からとても楽しみですね。

2019年4月振り返り

4月の振り返り

  目標の進捗状況の定期確認です。 4月は全くもってダメでした。言い訳できません。  

体の基礎づくり

こちらは特に数値上の進捗はありません。 週1でのジム通いと毎日のダンベルを使ったトレーニングを続けていますが、現状維持になってしまっています。 頻度と負荷をあげる必要があるかもしれません。 なお、最近遺伝子検査をしたのですが、遺伝的にはタンパク質の合成力が弱いようです。ちょっと悲しい。

アウトプット

ブログ投稿

  • 目標: 100記事 / 年
  • 実績: 今月 - 本記事も合わせて2記事 / 通年 - 19記事

ダメダメでした。  

登壇

  • 目標: 6件 / 年 
  • 実績: 今月 - 登壇なし / 通年 - 全1件

こちらもダメダメです。

サービス開発(アプリに限定しない )

  • 目標: 3件
  • 実績: 今月 - 0件 / 通年 - 0件

開発を進めていますが、いまいち進捗がでません。

スキルアップ

AtCoder

  • 目標: 水色
  • 実績: 茶色

ダメダメです。

LeetCode

  • 目標 50問
  • 実績 今月 - 0問 / 通年 - 0問

ダメでした。

Git

  • 目標: 500 commit / 年
  • 実績: 13 commit / 月
  • 実績: 109commit / 年

全然ダメです。

その他

  • Viperアーキテクチャを少し理解しました。
  • Apple創造を生む力を読み始めました。
  • Kindle PaperWhiteの最新版を買いました。防水なのでお風呂で読めます、良い。
  • FitBitを買いたいなぁと思うこの頃です。

4月は全くダメでした。 5月で挽回します。。。

【iOS】表示されているテキストをコピーできるUIを作成する簡単な方法

はじめに

iOSアプリを作成していると文言の表示に通常UILabelを使用します。 ただ、そのままではラベル上の文言のコピーを行うことができず、利便性の面であまり満足できないような場合があります。 以下で文言をコピーできるUIを作成する方法を記載します。

環境設定

以下の環境を使用しています。

  • Xcode10.2
  • Swift 5.0

実装

いくつか方法があるかと思いますが、簡単な方法としてUILabelの代わりにUITextViewを使用する方法があります。 基本的にはUILabelの代わりにUITextViewを設定するだけですが、注意点がいくつかあります。

  • isEditable を falseにする
    • trueのままだとタップで編集できてしまうためそれを防ぎます
  • isScrollEnabled を falseにする
    • このパラメータをfalseにすることで、UILabelのように内部の文言によってUITextViewのサイズが決定するようになります

また、表示する文言の行数を制限する場合下記のように設定します。

textView.textContainer.maximumNumberOfLines = 2;
textView.textContainer.lineBreakMode = .byTruncatingTail

Storyboard上では下記の赤枠の設定を変更します。行数制限についてはcode上で設定を行います。

f:id:Iganin:20190313175528p:plain

実際の画面表示は下記のようになります。該当のText部分をドラッグするとCopy, 検索, Shareの選択肢が表示されます。 ただ、左右のマージンを見ると赤背景の部分と、緑背景の部分でテキスト部分の表示に差があります。 ともに左右10ptのConstraintを親Viewに対してかけていますが、TextViewとUILabelはViewとTextContentとのPaddingなどに差があるためこのような表示の違いが生まれます。

f:id:Iganin:20190407214813p:plain

この部分は、下記の設定によって変更することができます。

  • textContainerInset
  • textContainer.lineFragmentPadding
  • layoutManager.usesFontLeading
        // textView内のInset
        textView.textContainerInset = .zero
        
        // TextView内の左右のPadding
        // defaultの値は5.0
        // contentsに使用できるwidthの計算に使用される
        textView.textContainer.lineFragmentPadding = 0.0
        
        // 通常はleadingがfontに依存して変わる
        // usesFontLeadingをfalseにすることでfontに依存しないようになる
        textView.layoutManager.usesFontLeading = false

ただ、上記の設定もコード上から行う必要があります。そのため、IB上からは設定の状況が確認できません。 そこで下記のように IBDesignableのクラスを作成し、使用することでIB上から確認できるようになります。

import Foundation
import UIKit

@IBDesignable
class UILabelTextView: UITextView {
    
    @IBInspectable
    var numberOfLines: Int = 1 {
        didSet {
            textContainer.maximumNumberOfLines = numberOfLines
        }
    }
    
    // IBInspectableにはEnumであるNSLineBreakModeを使用できなかったためIntを経由しています。
    // より良い方法をご存知の方がいましたら共有いただけますと幸いです。
    @IBInspectable
    var lineBreakModeNumber: Int = 0 {
        didSet {
            textContainer.lineBreakMode = NSLineBreakMode(rawValue: lineBreakModeNumber) ?? .byWordWrapping
        }
    }
    
    override init(frame: CGRect, textContainer: NSTextContainer?) {
        super.init(frame: frame, textContainer: textContainer)
        commonInit()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }
    
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        commonInit()
    }
    
    private func commonInit() {
        textContainerInset = .zero
        textContainer.lineFragmentPadding = 0.0
        layoutManager.usesFontLeading = false
    }
}

なお、IBDesignableのクラスを作成する際は、IBを開いた際のビルドスコープを狭めるためにUIパーツをEmbeded Frameworkに分離するのがおすすめです。

まとめ

UILabelのような扱いができるUITextViewを使用して表示文言のコピーができるUIを作成する方法をまとめました。 開発の中で上記要件が出た際はぜひ使用してみてください。

参考

2019年3月振り返り

3月の振り返り

  目標の進捗状況の定期確認です。 3月はプロジェクトの状況もひと段落し、多少時間に余裕ができました。 ただ、ギリギリの状況で行動していた反動か燃え尽きに近い状態になってしまってもいました。 休息をとって多少回復してきたので引き続き頑張っていきたいです。  

体の基礎づくり

こちらは特に数値上の進捗はありません。 レッグプレスが150kgまできたので脊柱起立筋を鍛えることでスクワットをもう少し伸ばせるかもしれません。  

アウトプット

ブログ投稿

  • 目標: 100記事 / 年
  • 実績: 本記事も合わせて5記事

目標達成のためには 8記事/月は必要なためショートしています。 4月は比較的時間がとれそうなためもう少しかけるかと思います。  

登壇

  • 目標: 6件 / 年 
  • 実績: 登壇なし 全1件

特に進捗ありません。

サービス開発(アプリに限定しない )

  • 目標: 3件
  • 実績: 0件

開発を進めています。

スキルアップ

AtCoder

  • 目標: 水色
  • 実績: 茶色

こちらは進捗なしです。

LeetCode

  • 目標 50問
  • 実績 0問

新規で目標を作成しました。一月6問ペースで到達できます。

Git

  • 目標: 1000 commit / 年 => 500 commit / 年(2019/03 下方修正)
  • 実績: 34 commit / 月

2月よりはいいですが、引き続き大幅ショートです。 目標を500 commit / 年に下方修正し、現実的な数字に変更します。

その他

  • iOSアプリ設計パターン入門を読み終わりました。
  • try! Swift 2019に参加しました。
  • iOS12Programmingを読み始めました。
  • Goの概要を勉強し始めました。

4月も引き続き頑張ります。

「このまま今の会社にいていいのか?と一度でも思ったら読む 転職の思考法」の感想

読んだ本 

このまま今の会社にいていいのか?と一度でも思ったら読む 転職の思考法

このまま今の会社にいていいのか?と一度でも思ったら読む 転職の思考法

 

 

概要

社会人としてのキャリアを考える上でどのようなことを考えなければいけないかについて述べられています。市場価値はどのように決まるかを「技術資産、人的資産、業界の生産性」の3点から述べられ、年齢とともにどの要素を重視して伸ばしていくべきか、またそのためにどのような企業に所属するべきか、また実際にそのような企業を探す上でどのようなことに気をつけるべきかが書かれています。

簡単な内容

全部を記載すると長くなりますので、気になった箇所や肝に命じて置きたい箇所を中心に記載します。

人材のマーケットバリューは技術資産、人的資産、業界の生産性の3点の掛け合わせで決まります。技術資産はプログラミングを綺麗にかけるなどの専門性、人的資産は「この人のためなら協力しよう」と思ってもらえる人がどれだけいるかなど一般的にいう人脈、業界の生産性は一人あたりの粗利を意味します。また技術資産はさらに細分化され、プログラミングを綺麗にかけるなどの専門性、PJのリーダーを行ったといった経験に分けられます。

本書では、これら3点に関して20代では専門性、30代は経験、40代は人的資産を重視しながらキャリアを築くのが良いと述べれています。

 

特に重要だと述べられていたのは「一人当たりの業界の生産性」です。なぜなら、個人に与えらえれる給与の上限は一人当たりの粗利を上限として決まるためです。一人あたり粗利が500万円であるのならば給与の上限はどれだけ頑張ったとしても500万円となります。そのため、上記要素は重要であり、そのような業界の生産性が高い企業をいかに見つけるか、どのように判断するべきかも本書には記載されています。

 

他には、いいベンチャーの見極め方、転職エージェントについて、仕事の楽しみ方についてなどキャリアを考える上での思考軸となり得る情報が詰まっています。

感想など

本書は非常に実践的な内容が物語形式と合わせて展開されており、非常に理解しやすく整理されています。各章の終わりに登場人物のメモという形で情報がまとまっており、要点をとても掴みやすくなっています。巻末には要点のまとめもあり、一度読み終わった後ではその部分を読み返すのみでもよいでしょう、読者にとても配慮した構成になっていると感じました。

 

本書のタイトルには「転職」とついていますが、個人的には転職する、しないにかかわらず仕事をする上で一度目を通した方が良い内容だと感じました。

社内の業務に目が行き過ぎていると、つい目の前の仕事を行い、その評価が高ければ良いと考えがちです。ただ、目の前のことが高レベルでできたとしても、それが市場価値の高い、他の場所でも通用するものでなければ、給与や待遇などにはなかなか反映されず評価もされないと考えます。なぜならその人物が他社に行ったとしても高待遇をえられないため、会社側から考えると待遇を向上するインセンティブが働きづらいためです。

そのため、目の前の仕事をただこなすだけではなく、いかにマーケットバリューを高めるように仕事をするか、そのような点に寄与する仕事を作り出すかが重要になってくると思います。

 

また、本書でも述べられていますが一人当たりの業界生産性を意識することも重要だと感じています。個人の待遇はその業界の金回りをベースに決まるため、成長している業界に身を置くこと、そのような業界で経験をつむことは大変価値のあることだと考えます。

 

自分らしく自由に生きるためには、自分が選択できる状態である必要があり、そのためにはマーケットバリューを高めることが不可欠です。他人のではなく、自分自身の人生を生きるために、本書に記載されている内容は常に念頭に置きながら仕事に向き合っていきたいと感じています。

 

最後に、本書のなかで「いつでも転職できる人間が、それでも転職せずに在籍し続ける組織が逆説的ではあるが一番強い」といったような内容のことが記載されていました。私が所属する組織にもそのようであってほしい、と願っています。

try! Swift Tokyo 2019 に参加しました

はじめに

3/21(木)- 3/23(土)に開催されたtry! Swift 2019に参加してきました。 try! SwiftはSwift関連の技術に関する技術カンファレンスです。

www.tryswift.co

今年はベルサール渋谷ファーストで開催されました。 3/21(木) - 3/22(金)はセッションメインのカンファレンス、 3/23(土)は午前中がWorkshopで、午後がpeerlabでした。 その感想になります。

Swiftの可能性の広さを知れた

iOSアプリ開発以外へのSwiftの適用事例がとても興味深かったです。 例えば、ハードウェアへの応用例としてRaspberry Piへの適用やServerSideSwift(SSS)などのセッションがありました。 Server Side Swiftは一時期よく耳にしましたが、最近の動向があまり終えておらず、状況が把握できていませんでしたが、 本カンファレンスでの講演を聞き、俄然やる気が出ました。 紹介されていた書籍も購入しました。5月末までくらいを目標に読み進めていきたいです。

Swiftの深い内容を知れた

アセンブリ周辺の話やStringの仕組み、importの種類に関することなどは普段アプリ開発をしている中ではなかなかじっくりと調べることがなく、 理解が曖昧な部分だなと感じていました。また、理解を深めるためのとっかかりがなかなか得られないでいましたが、 今回のセッションを通して知識を得るためのガイドのようなものを手に入れることができたと思っています。

あまり詳しくない分野を掘り進めるための手がかりが得られた

Swiftでの音の成形、Core DataやSiri ショートカットなど、なかなか触ることがなかったiOSMacOS周辺の技術に関して、 どのようにすれば良いかの手がかりを得られたのは今後iOSエンジニアとしてキャリアを積む中でとても役だつものだと感じています。

とにかくモチベーションが上がった

正直なところセッションの内容は理解が追いつかない部分も多くて、まだまだ学ばなければいけないことはたくさんあるなと思いました。 こんなに深く理解しているんだ、こんな考え方を持って働いているんだ、こんなすごい人たちがいるんだ、 そういった感情を持てる目標となる人たちにたくさん出会えたことはtry! Swiftに参加してよかったことの一つです。

交流を広げることができた

try! Swiftは国際カンファレンスのため、日本国外から来られている方もたくさんいました。 そのような方と交流を持つことができ、非常に刺激的な時間を過ごすことができました。 国境を超えても同じような感じだな、この点はかなり違うのだな、そういったことを知れたのは面白かったです。

今後学ぶこと

以下、try! Swiftを終えて、直近学んでいく予定のことです。 目標締め切りは5月末です。

最後に

著作や講演の発表、ブログ記事など日頃の業務で大変お世話になっている憧れのような方が同じ会場にたくさん集まっている非常に充実した時間でした。 今後も全力で楽しく学び作っていこうと、とてもモチベーションが上がっています。 try! Swift tokyo 2019に参加してよかったと心より思っています。登壇者のみなさま、運営のみなさま、関わってくださったみなさま、ありがとうございました!