なんとかするから、なんとかなる

エンジニア関係のことを書きます

UserDefaultsを初期化する

UserDefaults を初期化するためには

if let bundleId = Bundle.main.bundleIdentifier {
    UserDefaults.standard.removePersistentDomain(forName: bundleId)
}
  • persistentDomain(forName:)で domainNameで指定した、KeyとValueのディクショナリーが受け取れる。

  • domainNameをBundleIdentiferに指定することで、アプリ内すべてのKeyとValueが得られる

iOSで使いにくいUserDefaultをなんとかする

はじめに

iOSアプリにおいて、アプリを消しても値を保存しておける、UserDefaultはとても便利だと思う。 しかしながら、どうしても使いにくい部分が存在する。 例えば、UserDefaultの値を取り出すKeyがStringなところだ。

よくある使い方

// 保存する
UserDefaults.standard.set("sample", forKey: "sampleKey")
UserDefaults.standard.synchronize()

// とりだす
var str = UserDefaults.standard.string(forKey: "sampleKey")
  • Key値をタイポすると実行時まで築くことができない
  • stored propertyのように使用したい

stored propertyのように振る舞うために

前準備

class DefaultsKeys {
   static let sampleKey = DefaultsKey<String>("sampleKey")

   private init() {}
}

class DefaultsKey<ValueType>: DefaultsKeys {
   let key: String
   init(_ key: String) {
      self.key = key
   }
}

extension UserDefaults {
   subscript(key: DefaultsKey<String>) -> String {
      get {
         if let value = string(forKey: key.key) {
             return value
         } else {
             return ""
         }
      }
      set {
         set(newValue,forkey: key.key)
         synchronize()
      }
   }
}

使い方

UserDefaults.standard[.sampleKey] = "sample"
let str = UserDefaults.standard[.sampleKey]

その他

  • static let sampleKey のところを増やすことでKeyを増やせる
  • subscript を増やせばString以外にも対応可能
  • DefalutsKeyとDefaultsKeysを統合したかったのですが、staticプロパティはGenericタイプに対応していない。

おわりに

なんとかスッキリと利用することができるようになったので満足。

参考URL

github.com

qiita.com

レガシーコード改善を読んで

クラスをテストにしやすくするには

 

1. クラス間の依存性を下げる。

2. 他のクラスを使用する場合、そのやりとりをプロトコルで定義することで、テスト用のモックと振替可能にする。

 

1. クラス間の依存性を下げる

クラスをテストする上で、大変になってくるのは、テスト対象のクラスを個別で生成•使用することが難しくなこと。

あるクラスが他のクラスを参照している場合、そのクラスをテストするためには他のクラスも生成しなければならない。他のクラスもまた別のクラスを参照するとかになると、どんどん話はややこしくなっていく。

では、どうするのか。

一番良いのはクラスが他のクラスに依存しなくても成立するようにすることだ。しかし、それが困難な場合もある。

その場合、必要に応じて他のクラスの情報を渡すのが良いだろう。インスタンス生成時のイニシャライザやメソッドの引数にその情報を渡すのだ。

引数と渡すとこで、テスト時にはその引数をテスト用クラスに差し替えることが可能となる。

 

2. 他のクラスとのやりとりをプロトコルで定義する。

1. の話の続きとして、1.では引数で他のクラスとの依存性注入すると書いた。

今度は、依存される側のクラスをテストクラスに差し替えることを考えよう。

依存される側とクラスをどうやって、テストクラスに差し替えるか。

そこで利用するのがプロトコルである。プロトコルで、依存する側が必要とする処理を定義しておき、依存される側がそのプロトコルに則って実装する。

こうすることで、依存される側をテストクラスに差し替える場合、プロトコルで定義されたメソッドのみをテスト用に書くだけで良い。

後は、イニシャライザやメソッドの引数としてテスト用クラスを渡すことで、テストを実施することができる。

 

追記(2017/4/22)

イニシャライザの引数にすべてのクラスを渡すのは困難。

渡されるクラスがないと、生成されるクラスが存在できない場合、依存が高い場合はイニシャライザの引数として渡す。

クラス生成後に状態のように後から別のクラスをSetする場合は、イニシャライザで渡したり、メンバ変数に参照を渡すよりも、setメソッドを定義したほうがよさそう。

テスト時もsetメソッドにダミーのテストを渡せるように。

メンバ変数を直接いじるは、カプセル化的にもよくない。

もちろん、setメソッドではプロトコルでsetできるようにしておくのがベスト。

 

 

レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)