enum切り替え用のValueSwitcherってのを作ってみたが(わかりづらいので微妙)

enum切り替え用のValueSwitcherってのを作ってみたが(わかりづらいので微妙)SwiftのValueを切り替えて保存するValueSwitcherってのを作った。Qiitaに投稿しない。Carthage対応しない。Cocoapods公開しない。不完全だと思うので非公開ライブラリとして。

これ、自分でも必要性がすごくピンときているわけではない。せっかく作ったし、あとで読み返して考えるためにもブログに書いてみる。そういう感じです。

なぜ作ったのか

foregroundColor, backgroundColor, blurなど複数のカラーをまとめたColorSetというenumがあって、それをアプリ内で切り替えていた。アプリを開き直したらそのセットが適用されるようになっていた。これを複数ターゲットで使おうと思ったが、色のセットは微妙に異なることがわかった。

それで共通部分を切り出して自分用ライブラリにしようと思った。

最初は、色の切り替え専用でいいやと思ってたんだけど、考えてみたら別に色のセットだけじゃなくていろんなものに適用できそうだったのでもう少し汎用的にした。

ただそれだけです。

コード

コードをそのまま貼り付けます。

import Foundation

public struct ValueSwitcherNotificationName {
    public static let Changed = "[REVERSE_DOMAIN].value_switcher.changed"
}

public func ==<T: SwitchableValueType> (rhs: T, lhs: T) -> Bool {
    return rhs.id == lhs.id
}

public protocol SwitchableValueType: Hashable, Equatable {
    static var allValues: [Self] { get }
    static var defaultValue: Self { get }
    static var identifier: String { get }
    
    var id: String { get }
}

public extension SwitchableValueType {
    public var hashValue: Int {
        return id.hashValue
    }
}

public struct ValueSwitcher<SwitchableValue: SwitchableValueType> {
    public static func allValues() -> Set<SwitchableValue> {
        return Set<SwitchableValue>(SwitchableValue.allValues)
    }
    
    public static func defaultValue() -> SwitchableValue {
        return SwitchableValue.defaultValue
    }
    
    public static func findValue(id: String?) -> SwitchableValue {
        guard let id = id else {
            return defaultValue()
        }
        guard let value = allValues().filter({ $0.id == id }).first else {
            return defaultValue()
        }
        return value
    }
    
    public static func currentValue() -> SwitchableValue {
        let storedId = NSUserDefaults.standardUserDefaults().objectForKey(SwitchableValue.identifier) as? String
        let id = storedId ?? SwitchableValue.defaultValue.id
        return findValue(id)
    }
    
    public static func updateValue(value: SwitchableValue) {
        NSUserDefaults.standardUserDefaults().setObject(value.id, forKey: SwitchableValue.identifier)
        NSUserDefaults.standardUserDefaults().synchronize()
        NSNotificationCenter.defaultCenter().postNotificationName(ValueSwitcherNotificationName.Changed, object: nil, userInfo: ["identifier": SwitchableValue.identifier])
    }
}

利用時

DemoColorというenumを作った。これは、SwitchableValueTypeに準拠している。

import Foundation
import SwiftValueSwitcher
import UIKit

struct Color {
    static let Blue: UIColor        = .blueColor()
    static let Red: UIColor         = .redColor()
    static let White: UIColor       = .whiteColor()
    static let Black: UIColor       = .blackColor()
}

enum DemoColor: SwitchableValueType {
    case Blue
    case Red
    case Black
    case White
    
    static var allValues: [DemoColor] {
        return [.Blue, .Red, .Black, .White]
    }
    
    static var defaultValue: DemoColor {
        return .Blue
    }
    
    static var identifier: String {
        return "[REVERSE_DOMAIN].StopwatchColor"
    }
    
    var id: String {
        switch self {
        case .Blue:         return "blue"
        case .Red:          return "red"
        case .Black:        return "black"
        case .White:        return "white"
        }
    }
    
    var foregroundColor: UIColor {
        switch self {
        case .Blue:         return Color.Blue
        case .Red:          return Color.Red
        case .Black:        return Color.Black
        case .White:        return Color.White
        }
    }
    
    var backgroundColor: UIColor {
        switch self {
        case .Blue:         return Color.Black
        case .Red:          return Color.Black
        case .Black:        return Color.White
        case .White:        return Color.Black
        }
    }
    
    var name: String {
        switch self {
        case .Blue:         return "BLUE"
        case .Red:          return "RED"
        case .Black:        return "BLACK"
        case .White:        return "WHITE"
        }
    }
}

これを下記のように使う。

import UIKit
import SwiftValueSwitcher

class ViewController: UIViewController {

    @IBOutlet weak var colorNameLabel: UILabel!
    @IBOutlet weak var updateButton: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "colorChanged:", name: ValueSwitcherNotificationName.Changed, object: nil)
        update()
    }
    
    // MARK: Notifications
    
    @objc
    func colorChanged(sender: NSNotification) {
        print(sender.userInfo)
    }
    
    // MARK: Actions
    
    @IBAction func updateButtonTapped(sender: AnyObject) {
        let colors = ValueSwitcher<DemoColor>.allValues()
        let index = Int(arc4random_uniform(UInt32(colors.count)))
        let color = Array(colors)[index]
        ValueSwitcher<DemoColor>.updateValue(color)
        update()
    }
    
    // MARK: Update
    
    func update() {
        let color = ValueSwitcher<DemoColor>.currentValue()
        view.backgroundColor = color.backgroundColor
        colorNameLabel.text = color.name
        colorNameLabel.textColor = color.foregroundColor
        updateButton.setTitleColor(color.foregroundColor, forState: .Normal)
    }
}

微妙なところ

ValueSwitcherはジェネリクスなので何か型を当てはめて使うんだけど、この構造がちょっと微妙。いろんな型に対応したいけど、idで扱う対象を分けるとかもう少し分離できそう。とりあえず、今日はここでやめるけど、もっと汎用的になったら使いやすくなるかも。

Valueはenumを前提としているので分かりづらい。

そもそも、valueのセットを扱うだけのライブラリは大げさではないか。

Pocket
LINEで送る

You may also like...

  • morizotter

    わかりづらいのが一番良くない。

  • morizotter

    保存、取り出し、通知くらいしかやってない。とりあえず、EnumSwitcherとうい名前にしておこうかな。それとも、そもそもこんな仕組みいらないからenum自体に適応できるprotocol一つ作ればいいんじゃないか!?