Robert Herrera Robert Herrera - 1 month ago 8
Swift Question

Updating portions of data using Locksmith Swift 3

Say I originally stored some data using Locksmith via:

do
{

try Locksmith.saveData(data: ["a": a_data, "b":b_data, "c":c_data, "d":d_data], forUserAccount: "user") //store it
print("Data successfully Saved.")
} catch ...


Would it be possible to go back later on and only update a specific field without overwriting existing data stored in the keychain. For instance, if I had originally stored a,b,c and d. When i update field b with:

try Locksmith.updateData(["b": "new"], forUserAccount: "user")


Will I overwrite a,b,c,d with only new data (i.e. a,b,c,d -> new_b) or will Locksmiths update function only update the specific field. I should mention that I have tried to find the answer within the documentation but cannot seem to find it, unless I have overlooked it. In that case, I apologize. Nevertheless, any help is appreciated.

Thanks

Answer

I believe the documentation is quite clear that the available static Locksmith.updateData(_:, forUserAccount:) method replaces any given data for the user. I quote from matthewpalmer/Locksmith [emphasis mine]

Update data

as well as replacing existing data, this writes data to the keychain if it does not exist already

try Locksmith.updateData(["some key": "another value"], 
                         forUserAccount: "myUserAccount")

We can verify this statement by looking at on the following non-failing test (Locksmith/Tests/LocksmithTests.swift):

// ....

let userAccount = "myUser"

func testStaticMethods() {
    // ...    

    let otherData: TestingDictionaryType = ["something": "way different"]

    // ...

    let updatedData = ["this update": "brings the ruckus"]
    try! Locksmith.updateData(data: updatedData, forUserAccount: userAccount, inService: service)

    let loaded3 = Locksmith.loadDataForUserAccount(userAccount: userAccount, inService: service)! as! TestingDictionaryType

    // NOTE: this assert checks that the loaded data equals the 
    //       updated data (_not_ 'otherData' updated by 'updatedData')
    XCTAssertEqual(loaded3, updatedData)

    // ...
}

where the static Locksmith.updateData(_:, forUserAccount:) method clearly replaces the data of a given user account.


If you'd like to simply add sub-data to existing data of a user, you could implement this yourself:

  1. Read data for a given user account

    let currentData = Locksmith.loadDataForUserAccount(userAccount: userAccount)
    
  2. Update the data dictionary manually. There exists numerous SO threads explaining this step, see e.g.

  3. User the Locksmith.saveData(_:, forUserAccount:) static method to save your updated data for the given user account.