デバイス検出ライブラリUIDeviceComplete

# iOSデバイスモデルの検出
iOSデバイス（iPhone SE, iPhone 8またはiPhone Xなど）のモデルごとに異なる動作を実装することはとても恐ろしいことです。
デバイスモデルごとに異なる動作をするコードはメンテナンスの悪夢となり、新しいデバイスのリリース毎にユーザビリティの低下につながります。
しかし一方、ユーザーのデバイス分布（アプリユーザーの40％がiPhone Xを使用している、半分以上がiPhone SEを使用しているといった情報）を把握することはアプリ開発の役に立ちます。
これらの理由から、既存のサードパーティ製のデバイス判定ライブラリの代わりとなるような、モダンでカスタマイズしやすいライブラリの開発に着手しました。そして、このライブラリは既存のUIDeviceクラスの機能を拡張するという簡単な設計アプローチをとっています。


# UIDeviceComplete

https://github.com/Nirma/UIDeviceComplete

`UIDeviceComplete` は、`UIDevice` の機能拡張を集めたiOSライブラリです。

## 特徴
- iOSデバイスファミリーを取得する（iPod, iPhoneまたはiPad）
- iPhone 7, iPhone SE, またはiPad Proのような特定のデバイスを検出する
- デバイスの画面サイズ（インチ単位）で取得する

## 使用法
ライブラリの使用は非常に簡単です。

現在のiOSデバイスの名前を取得するようなことは、次のように行うことができます。

```swift 
let deviceName = UIdevice.current.dc.commonDeviceName
print(deviceName) // "iPhone X"
```

デバイスファミリーの検出は、次のように行うことができます。

```swift
let deviceFamily = UIdevice.current.dc.deviceFamily

switch deviceFamily {
    case .iPhone:
        print("...phone home?")
    case .iPad:
        print("when it comes to screen size; more is more")
    case .iPod:
        print("Why not?")
    default:
        print("No family...")
}
```

画面のサイズを特定することも出来ます。

```swift
let screenSize: Double = UIDevice.current.dc.screen.inches

if screenSize <= 4.0 {
    print("Modest screen size; not so modest price tag")
} else if screenSize >= 5.5 {
    print("Plus is always a plus")
} else {
    print("Chances are this is an iPad or an iPhone (Texas Edition).")
}
```

必要に応じて、特定のデバイスごとの分岐処理を実装することができます。

```swift
let device = UIdevice.current.dc.deviceModel

switch device {
    case .iPhone6Plus, .iPhone7Plus:
        print("Lots of screen realestate eh?")
    case .iPhoneSE, .iPhone5, iPhone5S:
        print("Less iPhone is more iPhone...")
    case .iPadPro:
        print("Why?")
    default:
        print("Not sure what this is...")
}
```

# ライブラリの仕組みとカスタマイズ
このライブラリは、POSIXのC言語関数でデバイス識別子を取得し、判別しています。
[utsname()](http://pubs.opengroup.org/onlinepubs/7908799/xsh/sysutsname.h.html)

iPhone Xの場合は「iPhone10,3」、iPad Proの場合は「iPad6,7」のような識別子になります。
識別子を "iPhone",  "10",  "3" に分割し、デバイスファミリーを取得し、メジャーバージョンとマイナーバージョンをタプルにしてがパターンマッチングで分類しています。

このライブラリに将来の新しいデバイスを追加することを想定し、仕組みを説明していきます。
新しいデバイスは「iPhone 9」、識別子は「iPhone11,1」と仮定します。

## ステップ1: デバイスを追加

デバイスを表現するenumに「iPhone 9」用の新しいケースを追加します。

https://github.com/Nirma/UIDeviceComplete/blob/master/Sources/DeviceModel.swift

```swift
public enum DeviceModel {
    ...
    case iPhone9
    ...
}
```

## ステップ2: 新しいパターンマッチケースに追加

メジャーバージョン、マイナーバージョンのタプルからデバイスモデルを判別する処理を追加します。

https://github.com/Nirma/UIDeviceComplete/blob/master/Sources/DeviceModel.swift

```swift 
extension DeviceModel {
    fileprivate static func detectIphoneModel(with identifier: Identifier) -> DeviceModel {
        ...
        switch (major, minor) {
        ...
        case (11, _):               return .iPhone9
        ...
        }
   }
}
```

## ステップ3: 文字列表現
デバイス名の文字列を追加します。

https://github.com/Nirma/UIDeviceComplete/blob/master/Sources/Identifier.swift

```swift
extension Identifier: CustomStringConvertible { 
     private func iphoneStringRepresentation(major: Int, minor: Int) -> String { 
         switch (major, minor) { 
         ...
         case (11, _): return "iPhone 9"
         ...
         }
     }
}
```

# まとめ
このライブラリを使えば、デバイス情報を簡単に取得できますし、新しいiOSデバイスが発売されても簡単にカスタマイズすることもできます。
このライブラリに価値を感じてもらえたなら、GitHubでクローンを作成してみてください。そして、改善点を見つけたらPull Requestを送信してください。

にこらす