1

I do not understand why I'm getting an error on this method:

let loginCountKey = "loginCount"
let appReviewParamsKey = "appReviewParams"

func resetLoginCount() {
    let defaults = NSUserDefaults.standardUserDefaults()

    if let reviewParameters = defaults.valueForKey(appReviewParamsKey) as? NSMutableDictionary {
        reviewParameters[loginCountKey] = 1

        defaults.setObject(reviewParameters, forKey: appReviewParamsKey)
        defaults.synchronize()
    }
}

the setObject line give me an error: caught "NSInternalInconsistencyException", ... mutating method sent to immutable object"

When debugging, reviewParameters shows as an NSMutableDictionary. I also tried an if var instead of an if let (tho it should be unnecessary) and got the same error.

I also tried setting the value to NSNumber(int: 1) instead of just 1.

Why would this cause the given error?

Aaron Bratcher
  • 6,051
  • 2
  • 39
  • 70

2 Answers2

1

There are two issues:

  • NSUserDefaults returns immutable objects when casted to Foundation types
  • Even if the returned value was mutable you assigned it to a constant (let) which makes the object immutable in Swift.

In Swift the recommended way is to use native Swift types and variables (var) for example

func resetLoginCount() {
  let defaults = NSUserDefaults.standardUserDefaults()

  if var reviewParameters = defaults.objectForKey(appReviewParamsKey) as? [String:AnyObject] {
    reviewParameters[loginCountKey] = 1

    defaults.setObject(reviewParameters, forKey: appReviewParamsKey)
    defaults.synchronize()
  }
}
vadian
  • 274,689
  • 30
  • 353
  • 361
1

NSUserDefaults.objectForKey (which the general KVC method valueForKey has to call through to) returns an immutable NSDictionary when it finds dictionary-format data for that defaults key.

Using as? is a cast, not a conversion. That is, you're telling Swift to interpret the dictionary you have as if it were an NSMutableDictionary (even though it's not, which is why this fails). If you want to convert an NSDictionary into an NSMutableDictionary, you'll need to create an instance of the latter from the former (with mutableCopy or initWithDictionary:).

But you don't even need to work with Foundation dictionaries here — an NSDictionary is automatically bridged to a Swift dictionary, and you can cast to interpret it as the right key/value types. And you can bind an optional as a mutable value by using if var instead of if let:

if var reviewParameters = defaults.objectForKey(appReviewParamsKey) as? [String: Int] {
    reviewParameters[loginCountKey] = 1

    defaults.setObject(reviewParameters, forKey: appReviewParamsKey)
    defaults.synchronize()
}
rickster
  • 124,678
  • 26
  • 272
  • 326