Swiftのリテラルについて

インターン先でSwiftの言語仕様について学ぶ機会があり、その中でリテラルについて学んだので少し理解を深掘りたいと思っています。

リテラルについて

まず、この記事を書くにあたり、リテラルについて調べてみる事にしました。

リテラルは、シンタックスシュガーの一つです。

hoge := "abc"

これをみた時、hogeString型である事がわかります。

僕たち人間は、このhogeをStringだと見ただけでわかりますが、機械ではこれを判断出来ません。

しかし、リテラルを用いる事で、値からそれがどの型なのかを自動的に推測しています。

Swiftのリテラル

Swiftでも、Int, Double, String, Character, Unicode, Bool, Optional, Array, Dictionaryのリテラルを提供しています。

https://nshipster.com/swift-literals/

let a = "abc" //String
let b = 1 // Integer
let c = true // Boolean

Optionalは、someとnoneの2つのケースが用意されているenumです。

しかし、Optional型と宣言しておけば、nilと文字を与えるだけでOptional型と推測されます。

let d: Optional<String> = nil

ExpressibleByNilLiteral

これは、Optional型がExpressibleByNilLiteralを継承しているからです。

nilリテラル nilを使用して初期化できる型と書かれています。 https://developer.apple.com/documentation/swift/expressiblebynilliteral

public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)

    public init(nilLiteral: ())
}

Optional以外で、ExpressibleByNilLiteralを使うことは非推奨で、SwiftではOptionalImplicitlyUnwrappedOptionalおよび`_OptionalNilComparisonType`にのみ使用されています。

応用で使ってみると以下のようになります

struct Nilable: ExpressibleByNilLiteral {
    init(nilLiteral: ()) {}
}

let a: Nilable = nil

Nilableかたで、nil型推論を使って宣言する事ができます。

少し応用したものを書いてみました

enum HogeHoge {
    case none
    case success(String)
    case failure
}

extension HogeHoge: ExpressibleByNilLiteral {
    public init(nilLiteral: ()) {
        self = .none
    }
}

extension HogeHoge: ExpressibleByStringLiteral {
    init(stringLiteral value: StringLiteralType) {
        if value.count >= 8 {
            self = .success(value)
        } else {
            self = .failure
        }
    }
}

let a: HogeHoge = nil
let b: HogeHoge = "1234"
let c: HogeHoge = "12345678"

print(a) // none
print(b) // failure
print(c) // success("12345678")

ExpressibleByStringLiteralExpressibleByNilLiteralを使う事で、Validation機能を持った型を作成しました。

このように、リテラルの種類は決めれらていますが、Protocolを使う事でいろんな型をコントロールする事ができます。

まとめ

当たり前のように型推論を使っていましたがなぜという所に立ち戻ると、今回のようなリテラルのようなProtocolを使って実装されている事がわかりました。

XCodeGenを導入してみた

XcodeGen

Xcodeでチーム開発をすると、*.xcodeprojでコンフリクトが度々起こりコンフリクトを修正する機会が多くあります。

iOS開発者なら、誰しもが歩ん道だと思います。

*.xcodeprojは、ソースファイルの管理を行なっておりファイルの追加や削除をするとこのファイルが更新されます。

大規模になればなるほど、このファイルはコンフリクトする可能性が高まります。

また、大規模開発ではマルチモジュール化により大規模な改修が見込まれます。

その際、ファイル移動は必須であり*.xcodeprojがコンフリクトする可能性は高いです。

ベンチャー企業でも下記のような問題は起こってくると考えています。

  • プログラミングに慣れていない人がいるため、コンフリクトを比較的起こしたくない
  • プロジェクトが始まってまもないためファイル生成する機会が多い

ですので、XcodeGenの仕組みについて理解しておく必要があると思っています。

導入

XcodeGenは、Swiftで記述されたコマンドラインツールでXcodeのプロジェクトデータをYamlで定義する事で*.xcodeprojを生成してくれます。

プロジェクトの仕様は、ターゲット、構成、スキーム、カスタムビルド設定、その他カスタマイズしていく事で生成が可能です。

https://github.com/yonaskolb/XcodeGen

では、実際に作ったYamlファイルを見ていきます。

name: proj-name
options:
  bundleIdPrefix: com.proj-name
targets:
  proj-name:
    type: application
    platform: iOS
    deploymentTarget: "11.0"
    sources: [proj-name]
    settings:
        MARKETING_VERSION: 1.0.0
    schemes:
      proj-name:
        testTargetName: proj-nameTests
    dependencies:
      - carthage: RxCocoa
      - carthage: RxRelay
      - carthage: RxSwift
      - carthage: Action
      - carthage: RxOptional
      - carthage: Swinject
      - carthage: DifferenceKit
      - carthage: KeychainAccess
      - carthage: SVProgressHUD
    preBuildScripts:
           - path: /bin/sh
             name: Run R.swift
             inputFiles:
               - $TEMP_DIR/rswift-lastrun
             outputFiles:
               - $SRCROOT/$PROJECT_NAME/R.generated.swift
             script: |
                     "Pods/R.swift/rswift" generate "$SRCROOT/testProject/R.generated.swift"

  proj-nameTests:
    type: bundle.unit-test
    platform: iOS
    deploymentTarget: "11.0"
    sources: [proj-nameTests]
    dependencies:
      - target: proj-name
      - carthage: RxBlocking
      - carthage: RxTest

実装は、このようになりました。

ほとんど、ReadMeに書いてあるままで生成出来ました。

settings: MARKETING_VERSION: 1.0.0

こちら、Build Settingでエラーが起こったためこの設定を入れました。

R.swiftをよく使うのですが、BuildScriptsで、普段の導入時に指定している内容を書くだけでR .swiftの導入も可能でした。

今後、Xcodeの設定も日々複雑になると思うので悪戦苦闘しながらこの辺り学んでいきたいと思います。

今後したいこと

XcodeGenをMintで、管理したいと思っています。 他には、Xcode Genのようなコマンドラインを作ってみるのも面白そうだと書きながら感じました。

記事としては、Makefileも同時に作成したのでそれを書いていきます。

SwiftでFunctor型を書いてみる

Functorとは

構造内の値に関数を適用できるものである。

しかし、これだけを聞いてもわかりづらい。

この記事では、HaskellのFunctorを参考にSwiftのOptionalを用いてFunctorを理解していきたい。

HaskellのFunctor

class Functor f where 
    fmap:: (a-> b) -> f a -> fb 

Functor型は、一つの関数fmapを持つクラスである。

fは、aやbのような具体型ではなく、一つの型引数を受け取る型コンストラクタだ。

  具体型 : 型引数を一つも取らない全ての型引数が埋まっている型

  型コンストラクタは、一つの型引数を取って具体型になるもの。

  型コンストラクタの例としてリストがあげられる。

  リストは、[]では具体型ではなく、[Int]となって具体型になるからだ。

つまり、fa -> fb ということから、fという構造を持ったままa型 -> b型に関数を適用していることがわかる。

Swiftでの実装

Optionalも型コンストラクタなので、

SwiftのOptionalを使って、Functorの実装を行なっていくことにする。

まずは、Functor型を作っていく。

protocol Functor {
    associatedtype A
    associatedtype B
    associatedtype F
    func fmap(f: ((A) -> B)) -> F
}
enum FunctorOptional<T>: Functor {
    typealias A = T
    typealias B = T
    typealias F = FunctorOptional

    case some(value: T)
    case none

    func fmap(f: ((A) -> B)) -> F{
        switch self {
        case .some(let value):
            return .some(value: f(value))
        case .none:
            return .none
        }
    }
}

let value = FunctorOptional.some(value: 1)
let notValue = FunctorOptional<Int>.none

print(value.fmap(f: { $0 + 2} ))
print(notValue.fmap(f: { $0 + 2 }))

//some(value: 3)
//none
 

OptionalにもMapが用意されているため、実装に置いてのメリットは提示できないが、fmapを使っての実装を書くことができた。

中国のデザインはなぜ複雑なのか

中国のデザインの複雑さについて

1. 漢字

中国のサービスは、情報が高密度に置かれている。これは、漢字自体がアルファベットのように、小文字、大文字、そして、他のFontとの切り替えに向かない文字である特性がある。どれだけ、UIをシンプルにおいても漢字を利用することで醜い見た目になり複雑に感じる。

また、見た目だけではなく機能的な面でも英語圏とは異なり制約が多い。ドメインが英語であるため、ドメイン名を覚えるのが難しいため、中国では直接リンクを提供することを行っている。リンクを大量に配置することで英語の文字を読むことができない文字を解決している。

これらの言語の特性は、日本も同じ問題がある。Yahoo JapanとYahooを比べると一つのWEBに存在する情報量は、異なる。

2. 西洋と比較すると

複雑な環境の観察が得意

脳科学的にアメリカ人は、単一のオブジェクトに気づくことにすぐれ、中国人は環境、背景などの複数の要素のバランスの取れた知覚を持っている。つまり、中国人は複雑な映像を見た時に全体に集中することができる。この近くは中国人だけではなく、アジア人が持つ特性だそうだ。つまり、文化からも複雑なデザインスタイルを頭の中で自然に受け入れてきたため中国の複雑なUIに対して中国人は気にかけていない。

3. 製品設計

日本のデザインコンセプトは、シンプルながらも機能的であることが一つだ。インターフェースの設計は、ユーザーのニーズに応えることが常にPMの最優先事項だ。中国のデザインが複雑なことは、単に中国人のニーズに答えてきたとも言える。中国の社会環境では、情報密度が高い複雑なデザインがユーザーの行動と一致している。淘宝網とアマゾンのUIからアメリカ人と中国人の思考の違いを比べてみる。アメリカ人は、アマゾンにログインする前から買いたいものが決まっている。反対に中国人は、アプリ内で買いたいものを決める傾向がある。つまり、中国人からするとアマゾンはシンプルなデザインであるため情報不足を感じることがある。

https://www.sohu.com/?spm=smpc.content.nav.1.1585181160363xM5CBbT

アリババ

アリババ

中国に最近とても関心があります。

そこで、アリババと言う本を目にしこれを読んでみる事にしました。

第1章 陰と陽のビジネスモデル

「天猫」と「タオバオ」は、アリババが手掛けるeコマース事業

天猫は大手ブランド向け、タオバオは小規模なブランドや独立系のブランド。

両プラットフォームで共通しているのが「ネットワーク•コーディネーション」と「データインテリジェンス」にこだわること。

「ネットワーク•コーディネーション」は、複雑な事業を分解し、複数の人や企業で分担して効率的に行うこと。

「データインテリジェンス」は、消費者の活動や反応に従って適切なプロダクトやサービスを産み出していく能力。

事業活動を分解していくことで正しく細かなデータを集めていくことができる。

そういったデータに機械学習を活用することで、新たな市場を産み出していく。

また、アリババが考えるデータインテリジェンスは、ユーザの意思決定の自動化を目的にしている。

企業の出荷時刻や製造会社の完了通知を継続的に集めることで、企業の意思決定の精度を上げている。

第2章 陽のネットワークインテリジェンス

ネットワーク•コーディネーションは、1つの業務を遂行するため複数のプレーヤーが同時並行なやりとりを自動で管理すること。

垂直統合型とは異なり、ネットワーク上で多くの企業や人が連携することで効率のいい方法を浮き彫りにしていくことができる。

ネットワーク•インテリジェンスを醸成する4つの運営原則

直接的なつながりを作ること

買い手と売り手が、コミュニケーションを取れることで様々な情報の共有をもたらした。

役割を変化させること

知識の豊富なタオパオの売り手が、タオパオの新参者にセミナーを開くタオパオ大学というサービスをつくった

インフラへの投資を行うこと

プラットフォームを利用する人が必要とするサービスを用意すること(APIなど)

事業活動をオンライン化すること

タオパオには、多くのデータがあるためそれらをユーザーに提供すること売り手は事業活動のあらゆることをデジタル化していった。

第3章 陰としてのデータインテリジェンス

アリババのMyバンク事業

他の中国の銀行は、政府機関が担うため大規模な国有企業や個人の預金者のために存在している。

My バンクは、中国の第3、第4都市の企業や個人向けで、融資額は50ドル~16万ドル程度。

第3、第4都市に住む人は、最低限の教育しか受けていないので、貸借対照表すらも書けない相手に融資を行っている。

ただ、書類提出は求めず、そういった都市に住む人でもアリババのプロダクト(タオパオや天猫)を利用しているため、信用スコアをつけて判断している。

信用スコアに関しては、機械学習で行われている。

MYバンクは、プロダクト、データ化、機械学習を徹底している。

プロダクト

顧客のニーズに応じて、金額や条件を変える適応力がある。

データ化

借りての事業のあらゆる側面をデータ化して、その情報をオンラインにしている。

機械学習

記録されたデータは、全て機会学習のアルゴリズムに付与される。

第4章 意思決定を自動化する

意思決定の自動化

ステップ1

物理的世界のデータ化

ステップ2

事業活動の全てをソフトウェア化させる

ステップ3

データフローを確保しAPI化する

ステップ4

データを完全に記録する

ステップ5

機会学習アルゴリズムを投入する

第5章 C2Bへ

フィードバックを通じて機械学習が事業場の意思決定を担う事で、企業のアクションは顧客の意向が反映される。

つまり、カスタマーエクスペリエンスは、オンデマンドで決定されるべき。

ここでは、中国のインフルエンサー、ビックEの影響力について書かれている。

ビックEは、隔週でウェイボーに服のデザインの写真をあげファンがタオバオで服を購入する。

ビックEのようなブランドのバックエンドとフロントエンドの一部まで全てを請け負うルーハンと言うインキュベーターが存在する。

ルーハンは、中国の衣料品製造工場とパートナーシップを結びウェブセレブのビジネスモデルに存在する需要と供給のピークと落ち込みに対処できるようにした。現在、ルーハンが作成したソフトウェアによって、ルーハンが注文をうけるとどの工場で何人が作業に携わるのかなど製造プロセスの全てを把握することが可能だ。

今後の流れ

恐らく、今後の世界ではデータと機械学習を用いることが当たり前になります。

近頃のサービスは、レコメンド機能を付けユーザの欲しいものを提示します。

しかし、今後のサービスの先には、提示ではなく機械学習でユーザーの欲しいものを決め、ユーザーには意思決定をさせない社会がきます。

ただ、私にはこの問題に関して課題感を感じています。 

データ社会となる中で、これからスタートアップがどのように戦っていくかです。

僕が持つ仮説は、大手の企業はうまくデータと機械学習を紐づけていないと思っています。

この紐付けを行うことが今後闘う上で最も大切になると思っています。

Golangにおけるポインタについて

これまで、Swiftをメインに書いてきましたが、最近golangを触る機会がありました。 Swiftでは考える機会がなかった ポインタメモリ についてまとめていきます。

初心者の方にわかりやすく伝わればいいなと思っています。

ポインタって?

まず、ポインタという言葉の前にメモリについて理解をすることが必要です。

n := 200

変数nが定義された時に、200を一時的に保存する場所が メモリです。

メモリは、複数存在しそれぞれのメモリに番号が振られています。

この番号を アドレス と呼びます。

実際にアドレスを確認してみます。 Goでは、&を定義した変数の前につけることでメモリを確認することができます。

func main() {
    n := 200
    fmt.Println(&n)
    // -> 0xc000084000

つまり、アドレス0xc000084000に変数nの200が保存されていることになります。

アドレスとメモリについて理解したところで、ポインタについて考えていきます。

ポインタを調べると、このようにまとめらていました。

  • メモリのアドレス情報のこと
  • アドレス情報を格納するための変数のこと

もう少しこの2つを詳細に見ていきます。

Golangには、 ポインタ型ポインタ変数

ポインタ型は、stringやintなどの方に*をつけることで定義します。

下では、nがポインタ変数にあたり、*intがポインタ型になります。

func main() {
    var pointer *int
    n := 100

    // 1.
    pointer = &n

    // 2.
    fmt.Println("pointerの中身:", *pointer)
}
  1. &nを使うことで、nのアドレスの番地をpointerに格納している
  2. pointer型から値を取得するためには、*をつけることで値を表示することができる

実際にポインタ型を使う時

ポインタ型は、参照渡しを行う時に用います。

Swiftでは、inoutを使いますね。

func main() {
    a, b := 100, 100

    checker(a, &b)
    fmt.Println(a, &b)
        // -> 100 101
}

//1
func checker(a int, b *int) {
    a++
    *b++

}
  1. 参照渡しを行うことで、呼び出しもとのbの値が変更される。

まとめ

多言語からGolangを始める方、初心者の方はこのポインタ型におそらく苦労されると思います。(自分がそうだった)

そこで、実際に手を動かして書くことで少し理解が深まると思います!

ぜひ、この記事を読みながら実際にパソコンでも手を動かして欲しいなと思います。

UIViewRepresentableとUIHostingController

UIViewRepresentable

SwiftUIの中で、UIKitのViewを使用したい時に継承します。

  • makeUIView(context:)
  • updateUIView(_:context:)

この2つの関数を呼ぶことで、Viewの実装を完結することができます。 makeUIView(context:)は、viewの初期化 updateUIView(_:context:)は、layout を決める時に使用しています。

struct MapView: UIViewRepresentable {
    func makeUIView(context: Context) -> MKMapView {
        MKMapView(frame: .zero)
    }
    
    func updateUIView(_ view: MKMapView, context: Context) {
        let coordinate = CLLocationCoordinate2D(
            latitude: 34.011286, longitude: -116.166868)
        let span = MKCoordinateSpan(latitudeDelta: 2.0, longitudeDelta: 2.0)
        let region = MKCoordinateRegion(center: coordinate, span: span)
        view.setRegion(region, animated: true)
    }
}

SwiftUIのチュートリアルでは、MapViewにUIViewRepresentableを継承させました。 このように継承させることにより、

struct ContentView: View {
    var body: some View {
        VStack {
            MapView()
                .edgesIgnoringSafeArea(.top)
                .frame(height: 300)
        }
    }
}

上のように、SwiftUIの中で、UIKitのコンポーネントを使用することができます。

UIHostingController

UIKitをメインにSwiftUIを用いたい時にUIHostingController を使用します。

let viewController = UIHostingController(rootView:  ContentView)

struct ContentView: View {
    var body: some View {
           Text("Joshua Tree National Park")
    }
}

1つのViewControllerとして扱えるようになるようです。