updateValue not working for Dictionary - ios

I'm creating a test app using Swift in Xcode, and I've run into an annoying issue. I'm writing a simple class that will act as a cache using a Dictionary object. My implementation is below:
import Foundation
import UIKit
class ImageCache {
var dict:Dictionary<String,NSData>?;
init() {
dict = Dictionary<String,NSData>();
}
func exists(id:String) -> Bool {
return dict!.indexForKey(id)!==nil;
}
func getImage(id:String) -> UIImage? {
if(!exists(id)) {
return nil;
}
return UIImage(data: (dict!)[id]);
}
func setData(id:String, data:NSData) {
dict!.updateValue(data, forKey: id);
}
}
The issue is in the last method, with Xcode stating "Could not find member 'UpdateValue'". This is weird, because the code hint seems to show it just fine:
But when I try to compile:
Could this potentially be a bug in Xcode? Or am I missing something super-obvious?

this is not a bug or a quirk in the compiler.
it is how Optional implemented (which may be flawed or not)
what happened is that the Optional store the Dictionary as immutable object (with let perhaps). So even Optional it is mutable, you can't modify the underlying Dictionaryobject directly (without reassign the Optional object).
updateValue(forKey:) is mutating method, you can't call it on immutable object and hence the error.
you can workaround it by doing
var d = dict!
d.updateValue(data, forKey: id)
because you copy the dictionary to another mutable variable, which then is mutable and able to call mutating method on it
but without dict = d, your change won't be applied on dict because Dictionary is value type, it makes copy on every assignment
related answer

Smells like a bug or a quirk in the compiler.
I just tried something like
var d = dict!
d.updateValue(data, forKey: id)
and it works as expected.

Related

Swift if let statement results in <<error type>> instead of custom object

I'm trying to use if let Swift statement to use an optional if it's not equal to nil. But for some reason, when I use it, Xcode shows the object is `<>z
I have a function which returns MyObject? and I want to check if it is nil, and if it's not I want to use it.
I'm trying to do it like this:
if let anObject = self.myFunc() {
anObject //Xcode shows that anObject is not MyObject but <<error type>>
}
(I'm using SwiftUI if that matters)
Does anyone knows why?
The if let is not allowed within body as is, so if you just need to conditionally shown some view on result of function, then the solution will be
if self.myFunc() != nil {
// some view here
}

Swift + Realm newbie: Problems with a simple Realm object and its initializers

I've been a long time Objective-C developer and heard about Realm some weeks ago. On the other hand I've always wanted to migrate little by little to Swift, so I created a small project involving Realm + Swift.
What does it mean? I'm a Swift + Realm newbie.
Anyway, I created a small demo/Proof of concept for a project I have in mind and I thought it had to be easier. But Xcode compiler says otherwise.
My problem is with one of my Objects' initializer(s).
My intentions were simple, but apparently Realm needs more initializers than I wanted.
The code of one of my Realm Objects is this:
import Foundation
import Realm
import RealmSwift
class Partida: Object
{
dynamic var id_partida: String
dynamic var inventario: Inventory?
required init ()
{
inventario = Inventory()
id_partida = "id_partida_test"
super.init()
}
required init(value: AnyObject, schema: RLMSchema) {
//fatalError("init(value:schema:) has not been implemented")
super.init(value: value, schema: schema)
}
required init(realm: RLMRealm, schema: RLMObjectSchema) {
//fatalError("init(realm:schema:) has not been implemented")
super.init(realm: realm, schema: schema)
}
override class func primaryKey() -> String? {
return "id_partida"
}
}
My original code only had the "normal" init initializer. But Xcode forced me to create two additional initializers more (value and realm ones).
If I compile the code I've just pasted above, Xcode complains in the 2nd and 3rd required initializers, specifically in the super.init part.
It says:
Property 'self.id_partida' not initialized at super.init call
I understand the meaning of it, but I don't know how to avoid the error because if I remove both super.init lines, the program crashes in runtime.
if I uncomment the fatalError lines, they also crashes in runtime.
In fact I don't want to use these 2 initializers. If I could, I wouldn't add them, but Xcode needs to, apparently. The only code I really want to add to my object init function is "the simple" init function, which was the only part of code considered mine.
I think I might have some concept misunderstandings in Realm + Swift + initializers.
I'm also having the feeling Xcode is forcing me to add code I don't need and/or I don't understand either.
Any help on understanding "required init" initializers in Realm will be more than welcome.
Official Realm + Swift documentation is beyond my knowledge as I don't understand many of its concepts even after re-reading them many times.
Google and StackOverflow haven't been really helpful this time...
Thanks.
Initializers in Swift definitely behave a bit differently to Objective-C, so I can definitely see the angle you're coming from here.
In this case though, since you're just using the initializer to set some default values, it's wholly un-necessary since you should be able to assign the default values to the properties themselves:
class Partida: Object
{
dynamic var id_partida = "id_partida_test"
dynamic var inventario: Inventory? = Inventory()
override class func primaryKey() -> String? {
return "id_partida"
}
}
Let me know if that still doesn't work! :)
Because it already has init () in Object class, you are using subclass of Object, so you already have its init in Realm object, you should give your var init value, like dynamic var id_partida: String = "id_partida_test", and then if you call let test = Partida() it already has your 2 init value, other init should be marked with convenience
When you save the Object to persistent store, it should be always have value, you can use Realm's optional then need read the documentation
Here's my sample Realm class so that u got the idea:
import Foundation
import RealmSwift
import SwiftyJSON
class ProjectModel: Object {
dynamic var id: Int = 0
dynamic var name: String = ""
//Dont need this, call init() already have value
required init() {
id = 0
name = ""
super.init()
}
convenience init(fromJson json: JSON!){
self.init()
if json == nil {
return
}
id = json["id"].intValue
name = json["name"].stringValue
}
override class func primaryKey() -> String? {
return "id"
}
}

Crash using failable initializer

I'm getting a strange crash using a failable initializer in Swift. I pretty sure it's a bug in Swift, but figured I do a quick post to see if I'm missing something obvious.
I'm writing an app using CloudKit and added a new convenience initializer to CKRecord:
extension CKRecord {
convenience init?(cloudInfo: NSData?) {
guard let info = cloudInfo else { return nil }
self.init(coder: NSKeyedUnarchiver(forReadingWithData: info))
}
This just uses archived data I created using CKRecord's encodeSystemFieldsWithCode: method to create a CKRecord instance. I used to simply have a method in my class to do this same thing and it worked fine.
When I call this initializer like this:
let record: CKRecord
if let rec = CKRecord(cloudInfo: self.cloudInfo) {
record = rec
} else {
record = CKRecord(recordType: "Item", recordID: CKRecordID(recordName: self.id))
}
When I make that call, I get a crash. It doesn't matter if I pass in nil for cloudInfo or a value. When stepping through the code, the crash seems to happen between returning from the init call and getting back to the caller. For example, if I pass nil and step into the guard statement in the init that seems to work, but as soon as I step off of it, I crash.
I've tried getting rid of guard and going with a simple if let construct, but same outcome. And, I've also just returned nil from the init without any other code, and that also crashes.
I've done the usuals: cleaned the build folder, rebooted, etc.
BTW, by "crash" I mean I'm getting: EXC_BAD_ACCESS (code=1, address=0xfffffffc) at the call site.
Does anyone have any ideas? Maybe it's something obvious that I'm missing.
Thanks.
According to The Swift Programming Language (Swift 2.1) book,
For classes, however, a failable initializer can trigger an initialization failure only after all stored properties introduced by that class have been set to an initial value and any initializer delegation has taken place.
Taken from here.
You can't extend an existing class with stored properties, so the first requirement is met. But to meet the second requirement, you have to delegate your initialization process like in code below:
extension CKRecord {
convenience init?(cloudInfo: NSData?) {
guard let info = cloudInfo else {
self.init(coder: NSKeyedUnarchiver(forReadingWithData: NSData()))
return nil
}
self.init(coder: NSKeyedUnarchiver(forReadingWithData: info))
}
}
Actually in Swift 3, there seems to be a bug and a regression from Swift 2: returning nil from a failable intializer crash the runtime.

Swift - casting a nil core data string as an optional value

I have a field stored on a core data object called "metadata" which is of type String (no optional, because Apple docs say not to mess with optionals in CD). Sometimes, the metadata field is nil. In checking whether this value is nil, I do the following check:
if object.metadata as String? != nil {
...
}
However, my code continuously crashes on this line as an EXC_BAD_ACCESS. I have also tried:
if let metadata = object.metadata as String? {
...
}
Which doesn't work either. I cast objects successfully to optionals in other parts of my code, so I don't understand why this particular case isn't working. How do you check whether a core data property is a nil string?
It looks like what you really want is this:
if object.metadata != nil {
...
}
or this:
if let metadata = object.metadata as? String {
// You can now freely access metadata as a non-optional
...
}
--EDIT--
My mistake, I didn't read the first part of your question thoroughly enough. It looks like the duplicate answer has a solution for this. Essentially, the generated managed object subclass is a bug and you should modify the properties to be either optional or implicitly unwrapped. You can check both of those using the first method for implicitly unwrapped and second for optionals.
There are several questions which discuss the issue of the generated subclasses not producing optional properties. I wouldn't be too concerned about editing the subclasses; there's nothing special about them except that Apple is making it easier to create them.
Check if property is set in Core Data?
Swift + CoreData: Cannot Automatically Set Optional Attribute On Generated NSManagedObject Subclass
--Edit2--
If you really don't want to touch the subclass you can access the property using valueForKey() and could add that as an extension if you wanted something a bit cleaner.
if let metadata = object.valueForKey("metadata") as String? {
...
}
In an extension:
extension ObjectClass {
var realMetadata: String? {
set {
self.setValue(newValue, forKey: "metadata")
}
get {
return self.valueForKey("metadata") as String?
}
}
}

Swift function that return two strings fail when its in loop

I Wrote a function that return two Strings, when calling the function regularly its works fine, but when I'm running the function through loop, I'm getting this error:
Thread 1: EXC_BAD_ACCESS (code=2, address=0xbfffcba0)
override func viewDidLoad()
{
super.viewDidLoad()
test()
}
func test()
{
var funcs = [checkButton]
var a = checkButton(value: 1) // Runs OK
for f in funcs{
var result = f(value: 1) // Fail
}
}
func checkButton(#value: Int) -> (location: String, pattern: String){
return ("abc","cba")
}
Update:
I'm using Xcode 6 beta 2, and running Mavericks on VMware Workstation.
Also, I've just created new clean project with that code and still getting the error.
This code runs fine for me. Your EXC_BAD_ACCESS must be coming from some other part of your code. Try setting a breakpoint and stepping through the code to find the line throwing the error.
From the “The Swift Programming Language.”
“An instance method can be called only on a specific instance of the type it belongs to. It cannot be called in isolation without an existing instance.”
checkButton() is an instance method, not a closure. It works in the first case because there is an implicit self. before checkButton(). It will not work in the second case.
If you want to make checkButton a closure you could declare it like so:
let checkButton = { (#value: Int) -> (location: String, pattern: String) in
return ("abc","cba")
}
I can confirm that it doesn't work for me either. Created a iOS single-view app from template, and added above code. crash. As an experiment, I took it out of the array (just f = self.checkButton) and got the same result.
I think it's a bug in the compiler.
First according to the book, a method is actually a function which is actually a closure, albeit one with special properties and restrictions. Shouldn't self.checkButton (or implicit version) be sufficient to "give it an existing instance", making it a closure? If MattL is correct that instance methods can't be used as closures, then the compiler shouldn't allow you to assign one to anything.
Second, the crash occurs on the exit, not on the call. And if you reference self in checkButton, (e.g. println(self.title) having previously set title), it works fine. That suggests that the instance is indeed known and operating, just something wrong on the return.
Third, changing it to a class method doesn't help. Changing these lines
var a = ViewController.checkButton(value: 1)
var funcs = [ViewController.checkButton]
class func checkButton(#value: Int) -> (location: String, pattern: String)
results in the same crash. I don't see any similar prohibition on context for class methods.
Fourth, if you simply change the return type from (location: String, pattern: String) to just String and return abc, then the whole thing works fine.
Fourth, if you wrap test and checkButton in a new class testClass, and then call it as below, it works:
class testClass {
func test()
{
var funcs = [checkButton]
var a = checkButton(value: 1) // Runs OK
for f in funcs {
var result = f(value: 1) // Fail
println(result)
}
}
func checkButton(#value: Int) -> (location: String, pattern: String){
return ("abc","cba")
}
}
class ViewController: UIViewController {
override func viewDidLoad()
{
super.viewDidLoad()
let g = testClass()
g.test()
}
}
Now change testClass to testClass: NSObject and it crashes.
So it looks like the compiler is botching up a method return when called as a closure with a tuple in a Obj-C subclass. When I put it like that, I must say that it's not terribly surprising nobody's noticed yet; you're really pushing the edge here!
More practically, in the meantime, if it's helpful, an alternative to changing your method to a closure is to keep it unchanged and just wrap it as you put it in the array:
var funcs = [{value in self.checkButton(value: value)}]
This seems to work.

Resources