swiftdictionarydidset

Update values in a dictionary based on changes made to another dictionary


I would like to know if there is a way to automatically update the value in a dictionary based on changes made to values in another dictionary. I have 2 dictionaries as follows:

enum CharacterAttributes: String, Codable  {
    case strength
    case constitution
    case dexterity
    case intelligence
}

enum CharacterStats: String, Codable  {
    case maxHealthPoint
    case maxTacticPoint
    case maxActionPoint
    case movementSpeed
}

var attributes = [CharacterAttributes: Float]()
var stats = [CharacterStats: Float]()

The logics are:

stats[.maxHealthPoint] = attributes[.constitution] *  100
stats[.maxTacticPoint] = (attributes[.dexterity] +  attributes[.intelligence]) * 1.5
stats[.maxActionPoint] = attributes[.constitution] * 10
stats[.movementSpeed] = attributes[.dexterity] + 70

What I want to achieve is that whenever a value is changed from attributes dictionary, the corresponding values in stats get updated automatically. I know for a normal variable I can use didSet keyword, but how to apply the concept to dictionaries?

Thanks in advance for any help.


Solution

  • Try using the didSet observer after encapsulating your dictionaries within a class:

    import Foundation
    
    enum CharacterAttributes: String, Codable {
        case strength
        case constitution
        case dexterity
        case intelligence
    }
    
    enum CharacterStats: String, Codable {
        case maxHealthPoint
        case maxTacticPoint
        case maxActionPoint
        case movementSpeed
    }
    
    class Character {
        private var _attributes = [CharacterAttributes: Float]() {
            didSet {
                updateStats()
            }
        }
        
        private var _stats = [CharacterStats: Float]()
        
        var attributes: [CharacterAttributes: Float] {
            get { _attributes }
            set {
                _attributes = newValue
                updateStats()
            }
        }
        
        var stats: [CharacterStats: Float] {
            get { _stats }
            set {
                _stats = newValue
            }
        }
        
        init(attributes: [CharacterAttributes: Float]) {
            self._attributes = attributes
            updateStats()
        }
        
        private func updateStats() {
            _stats[.maxHealthPoint] = (_attributes[.constitution] ?? 0) * 100
            _stats[.maxTacticPoint] = ((_attributes[.dexterity] ?? 0) + (_attributes[.intelligence] ?? 0)) * 1.5
            _stats[.maxActionPoint] = (_attributes[.constitution] ?? 0) * 10
            if _stats[.movementSpeed] == nil { // Allow manual setting
                _stats[.movementSpeed] = (_attributes[.dexterity] ?? 0) + 70
            }
        }
    }
    

    Example Usage:

    var character = Character(attributes: [.constitution: 5, .dexterity: 3, .intelligence: 2])
    print(character.stats)
    character.attributes[.constitution] = 7
    character.stats[.movementSpeed] = 100
    character.attributes[.dexterity] = 4
    print(character.stats)
    

    Output:

    [SwiftPlayground.CharacterStats.maxHealthPoint: 500.0, SwiftPlayground.CharacterStats.maxTacticPoint: 7.5, SwiftPlayground.CharacterStats.maxActionPoint: 50.0, SwiftPlayground.CharacterStats.movementSpeed: 73.0]
    [SwiftPlayground.CharacterStats.maxHealthPoint: 700.0, SwiftPlayground.CharacterStats.maxTacticPoint: 9.0, SwiftPlayground.CharacterStats.maxActionPoint: 70.0, SwiftPlayground.CharacterStats.movementSpeed: 100.0]