helloB
helloB

Reputation: 3582

Possible to persist HKQueryAnchor in NSUserDefaults?

So I'm a bit dismayed that Apple has replaced a transparent, easy-to-handle NSUInteger with an HKQueryAnchor but has not provided an easy way to persist that HKQueryAnchor. Has anyone found a good way to do this with NSUserDefaults? The only persistence method I have seen is an archiver to a local file, but my app persists everything in NSUserDefaults, and I'd like to keep it that way if possible. Is there a reliable way to store an HKQueryAnchor this way?

Upvotes: 3

Views: 1123

Answers (3)

Dominic Holmes
Dominic Holmes

Reputation: 598

This answer is building off @allmightu 's answer, but without the SwiftUI @AppStorage requirement. I also packaged it into a nice get/set syntax, so this is pretty much invisible to any consumer of the variable.

    let anchorKey = "defaults-anchor-key"
    var anchor: HKQueryAnchor? {
        get {
            guard let anchorData = UserDefaults.standard.data(forKey: anchorKey) else {
                return nil
            }
            return try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: anchorData)
        }
        set {
            guard let newValue = newValue else {
                UserDefaults.standard.set(nil, forKey: anchorKey)
                return
            }
            if let anchorData = try? NSKeyedArchiver.archivedData(withRootObject: newValue, requiringSecureCoding: true) {
                UserDefaults.standard.set(anchorData, forKey: anchorKey)
            }
        }
    }

Upvotes: 0

allmightu
allmightu

Reputation: 1

Swift 5

You can declare the anchor as a computed variable which decodes the Data that you store in NSUserDefaults using NSKeyedUnarchiver

@AppStorage("anchorKey") private var encodedAnchor: Data?
    private var myAnchor: HKQueryAnchor? {
        if let encodedAnchor {
            // Decode the data to get HKQueryAnchor
            if let decodedAnchor = try? NSKeyedUnarchiver.unarchivedObject(ofClass: HKQueryAnchor.self, from: encodedAnchor){
                return decodedAnchor
            }
        }
        return nil
    }

On your HKAnchoredObjectQuery, you can then encode the newAnchor which the closure provides using NSKeyedArchiver which stores it to your stored value using NSUserDefaults

if let newEncodedAnchor = try? NSKeyedArchiver.archivedData(withRootObject: newAnchor, requiringSecureCoding: true) {
          // Store the encoded anchor data in UserDefaults
          self.myAnchor = newEncodedAnchor
      }

Upvotes: 0

dan
dan

Reputation: 9825

You can convert it to NSData using NSKeyedArchiver and store that in the user defaults.

To store:

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:anchor];
[[NSUserDefaults standardUserDefaults] setObject:data forKey:@"AnchorData"];

To retrieve:

NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:@"AnchorData"];
HKQueryAnchor *anchor = [NSKeyedUnarchiver unarchiveObjectWithData:data];

Upvotes: 7

Related Questions