LoginSignup
0
2

More than 1 year has passed since last update.

【Swift】 クロージャーで呼び出し元 struct のプロパティを更新する

Posted at

【Swift】 クロージャーで呼び出し元 struct のプロパティを更新する

カスタム Struct の例

自身または更新させたいプロパティを参照で渡す関数を定義する。

protocol Editable {}
extension Editable {
    mutating func edit( _ editor: ( inout Self ) -> Void  ) {
        editor(&self)
    }
}
struct SomeParent {
    var child: SomeChild = SomeChild(
        val: 1
    )
}
struct SomeChild: Editable {
    var val: Int
}

var parent = SomeParent()
parent.child.edit {
    $0.val += 1
}
print(parent.child.val) // 2 を出力

Array の例

要素を参照で渡す関数を定義する。

extension Array {
    mutating func updateEach( _ updater: ( inout Element ) -> Void ) {
        for i in 0..<self.count {
            updater(&self[i])
        }
    }
}

var array =  [1, 2, 3, 4, 5]
array.updateEach {
    $0 += 1
}
print(array) // [2, 3, 4, 5, 6]

部分的な更新

fatalError の代わりに throw する例外を用意しておく

extension Array {
    enum ArrayError: Error, LocalizedError {
        case indexOutOfBounds( index: Int )
        case invalidRange( range: Range<Index>)
        case invalidClosedRange( range: ClosedRange<Index>)
        var errorDescription: String? {
            switch self {
                case .indexOutOfBounds( let index ):
                    return "Index out of bounds: \(index)"
                case .invalidRange( let range ):
                    return "Invalid range: \(range)"
                case .invalidClosedRange( let range ):
                    return "Invalid range: \(range)"
            }
        }
    }
}
Index 指定
extension Array {
    mutating func update( index: Int, _ updater: ( inout Element ) -> Void ) throws {
        guard self.indices.contains(index) else {
            throw Array.ArrayError.indexOutOfBounds(index: index)
        }
        updater(&self[index])
    }
}
var array =  [1, 2, 3, 4, 5]
try array.update( index: 2 ) {
    $0 += 10
}
print(array) // [1, 2, 13, 4, 5] 
Range ( n..<m ) 指定
extension Array {
    mutating func update( range: Range<Index>, _ updater: ( inout Element ) -> Void ) throws {
        guard self.indices.contains(range) else {
            throw Array.ArrayError.invalidRange(range: range)
        }
        for i in range {
            updater(&self[i])
        }
    }
}
try array.update(range: 1..<3) {
    $0 += 20
}
print(array) // [1, 22, 23, 4, 5]
ClosedRange ( n...m ) 指定
extension Array {
    mutating func update( range: ClosedRange<Index>, _ updater: ( inout Element ) -> Void ) throws {
        guard self.indices.contains(range) else {
            throw Array.ArrayError.invalidClosedRange(range: range)
        }
        for i in range {
            updater(&self[i])
        }
    }
}
try array.update(range: 1...3) {
    $0 += 30
}
print(array) // [1, 32, 33, 34, 5]

Dictionaryの例

Arrayと同様、Valueを参照で渡す関数を定義する。

extension Dictionary {
    mutating func updateEach( _ updater: ( inout Value ) -> Void ) {
        for key in self.keys {
            updater(&self[key]!)
        }
    }
}
var dic = [
    "1" : 1,
    "2" : 2,
]
dic.updateEach {
    $0 += 1
}
print(dic) // ["2": 3, "1": 2]
Key 指定
extension Dictionary {
    mutating func update( key: Key, _ updater: ( inout Value ) -> Void ) {
        guard keys.contains(key) else {
            return
        }
        updater(&self[key]!)
    }
}
var dic = [
    "1" : 1,
    "2" : 2,
]
dic.update(key: "1") {
    $0 += 100
}
print(dic) // ["1": 101, "2": 2]
0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2