Swift - Weird coding-compliant error - ios

I'm having a problem and I couldn't find anyone else on the web with the same struggle, maybe it's just too silly.
I have the following class:
class UIXOColourPicker : UIView {
#IBInspectable var moodValue: Int!
...
}
And in my storyboard I have a view just like this:
The user defined runtime attribute was generated by Xcode IBInspectable functionality (which is really cool btw), but whenever I try to run the app I get the error
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<UIXOColourPicker 0x7fa86877c420> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key moodValue.'
I'm on Xcode 6 - Beta 6.
Any ideas?
Sorry if it's a silly question, but I've been struggling with it for like 2 hours and I don't have any other Swift developer here to have a look at it.
Cheers

#IBInspectable uses cocoa objects and not native swift types. So, anything that isn't implicitly convertible to a swift type needs to be a cocoa object instead. For Number or Bool you'd need NSNumber. For something like Point, Size, Rect, etc, you'd need to use NSValue. However, for String you can use String directly; you don't need to use NSString.
So, in your case, you need to use NSNumber instead of Int. I'd also use NSNumber? instead of NSNumber! in case the value isn't set in your storyboard/xib.
#IBInspectable var moodValue: NSNumber?
Update
As #JakeLin and #Echelon pointed out, for int like values, Xcode will only show the attribute in the Attributes Inspector if you declare it as an Int?, but then it will crash at runtime. If you use an NSNumber?, it won't crash at runtime, but the attribute won't be available in the Attributes Inspector anymore; it will only show up in the User Defined Runtime Attributes (this seems like a bug in Xcode to me).
The error itself tells us how to get around that problem though:
IBInspectable[66994:58722469] Failed to set (moodValue) user defined inspected property on (q25429792___IBInspectable.ViewController): [ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key moodValue.
What this is saying is that the runtime can't find a "key value coding-compliant" attribute on the class for moodValue (Int attributes aren't key value coding-compliant) and that you can implement setValue:forUndefinedKey: to fix that.
In that case, the implementation might look something like this:
#IBInspectable var moodValue: Int?
override func setValue(value: AnyObject?, forUndefinedKey key: String) {
if let value = value as? Int? where key == "moodValue" {
self.moodValue = value
}
}
So, if you really want the attribute to show up in the Attributes Inspector and you don't mind adding the extra method, declare your property as an Int? and implement setValue:forUndefinedKey:. If you don't want the extra method, you'll have to content yourself with using an NSNumber? and the User Defined Runtime Attributes UI.

This is down to your type for moodValue. It seems the KVO system can't handle the type coercion needed; it's looking for a moodValue with the actual type NSNumber. Changing
#IBInspectable var moodValue: Int!
to
#IBInspectable var moodValue: NSNumber!
Then picking out the moodValue.integerValue should do what you want.

Related

"Cannot find 'RealmProperty' in scope" when trying to use optional double, Swift

I'm using Realm Sync to store data for my iOS app, coded in Swift. I wanted to create an optional double property (budget) for a Realm object (User_budgets). I created the object in the Realm schema and then copied in the Data model SDK that Realm produces which is as below:
import Foundation
import RealmSwift
class User_budgets: EmbeddedObject {
let budget = RealmProperty<Double>()
#objc dynamic var date: Date? = nil
}
I then get the error: "Cannot find 'RealmProperty' in scope". I tried changing the code to the below:
#objc dynamic var budget: Double? = nil
But then I get the error: "Property cannot be marked #objc because its type cannot be represented in Objective-C"
I've had a search but can't seem to find anyone who's had this issue before. There's an easy work around, which is simply to make the budget property required (non-optional), but it would be good to know how to be able to create optional double properties in the future. Can anyone help me out?
I believe you're using the wrong definition for that optional as it's only available in beta 10.8.0-beta.0:
What you have is
let budget = RealmProperty<Double>()
and for all other releases it should be
let budget = RealmOptional<Double>()
See RealmProperty and RealmOptional
oh and here's a link to all of the Support Property Types

AnyObject not working in Xcode8 beta6?

In Xcode8 beta6, the following code will cause a warning: 'is' test is always true. But it won't print pass.
struct TestStruct {
}
//warning: 'is' test is always true
if TestStruct() is AnyObject {
print("pass")
}
And the following code will cause a warning: Conditional cast from 'T' to 'AnyObject' always succeeds
public static func register<T>(_ protocolType: T.Type, observer: T) {
//Warning: Conditional cast from 'T' to 'AnyObject' always succeeds
guard let object = observer as? AnyObject else {
fatalError("expecting reference type but found value type: \(observer)")
}
//...
}
The warning works as intended: the false return of TestStruct() is AnyObject, however, does not
The prior version of this answer perceived the warning,
'is' test is always true
as the bug, and contained some discussion as to why this perceived buggy warning would manifest itself. That TestStruct() is AnyObject evaluated to false at runtime, however, was perceived as expected behaviour.
Given the comments to the bug report filed by the OP (SR-2420), it seems the situation is the reverse: since Xcode 8/beta 6, the is test should always evaluate to true, and the bug the OP:s post is the fact that TestStruct() is AnyObject evaluates to false during runtime.
Joe Groff writes:
This is correct, because everything bridges to AnyObject now.
...
is/as AnyObject always succeed for all types now. It's behaving
as intended.
The new SwiftValue box for conversion from Swift values to Obj-C objects
(for additional details, see discussion in the comments below, thanks #MartinR)
It seems as if Swift values that are not explicitly implemented to be bridgeable to Obj-C objects via e.g. conformance to _ObjectiveCBridgeable (see e.g. the following Q&A for details regarding _ObjectiveCBridgeable), will instead automatically make use of the new SwiftValue box to allow conversion to Obj-C objects.
The initial commit message for swift/stdlib/public/runtime/SwiftValue.mm reads:
Runtime: Implement an opaque 'SwiftValue' ObjC class to hold bridged values
If there's no better mapping for a Swift value into an Objective-C
object for bridging purposes, we can fall back to boxing the value in
a class. This class doesn't have any public interface beyond being
NSObject-conforming in Objective-C, but is recognized by the Swift
runtime so that it can be dynamically cast back to the boxed type.
Long story short.
To check if value has a reference type:
if type(of: value) is AnyClass {
// ...
}
To check if type is a reference type:
if SomeType.self is AnyClass {
// ...
}
More helpful answers:
https://stackoverflow.com/a/39185374/746347
https://stackoverflow.com/a/39546887/746347

Xcode 8 Core Data abnormality

I am trying to update my project to Swift 3 which has Core Data. I have confronted with serious error and I really don't know what is going on. My Core Data Model has following properties
#NSManaged var name: String?
#NSManaged var count: NSNumber
#NSManaged var isDelivered: NSNumber
I can set any other properties but isDelivered. When I try to use
myobject.isDelivered = true
I get following error on the console.
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: '-[MyModel setDelivered:]:
unrecognized selector sent to instance 0x6000000d3780'
It looks like Xcode is removing is part from isDelivered property which crashes the app. Is there anything that I can do to prevent this other than updating my model? Thanks.
It is a BUG. It is very confusing bug. For anyone visiting this question, here is the answer I found on Apple forums.
The current version of Swift 3 beta seems to have some flaw about treating properties prefixed with "is".
https://forums.developer.apple.com/thread/50863
Answer from user OOPer
Avoid using "is" in your property name, or if you cannot, try this:
newWriter.setValue(true, forKey: "favorite")
(Update)
Try adding #objc name to the property:
#NSManaged #objc(isFavorite) var isFavorite: Bool

Swift 2.0 Subscript issues with Dictionary

I've looked at the other questions on here about subscripting with dictionaries and I didn't see anything that quite fit what my scenario is. It may be that I'm still too new to Swift to realize it but in any case here is my scenario. I'm getting the typical can't "Subscript" dictionary with type string. I've seen the posts on here about it being an optional and needing to unwrap it however when I try that, Xcode suggests that I remove the !, so I do that, then I get the subscript error.
I've watched tons of tutorials on swift development and a lot of them use playgrounds and I never remembered seeing anyone have to do this in any of the tutorials. So I tried the same thing in a playground and it worked.
Here is what I have in the ViewController that DOESN'T work.
var validFields:Dictionary = ["loanBalanceInput":false,"cashOutInput":false,"appraisedValueInput":false,"interestRateInput":false]
func validationSuccess(sender:UITextField){
sender.backgroundColor = green
switch sender {
case loanBalanceInput:
validFields["loanBalanceInput"] = true
break
default:
break
}
}
What I've done is create a dictionary of strings that refer to textfields and their validation status to track whether or not they have been validated. The concept is that when everything in the dictionary is true, I can activate the calculate button.
However I get the "cannot subscript a value of type dictionary with an index of type string" error... However this code in a playground works...
var validatedFields:Dictionary = ["loanBalanceInput":false,"cashOutInput":false,"appraisedValueInput":false,"interestRateInput":false]
validatedFields["loanBalanceInput"]
validatedFields["loanBalanceInput"] = true
I don't understand what's going on here. Is it because this is an optional?
#IBOutlet weak var loanBalanceInput: UITextField!
I'm not unwrapping it in my switch? I'm not trying to get at the value of loanBalanceInput though, I'm just checking to see if it was the sender.
Normally a Swift Dictionary declaration needs also the type of the containing keys and values like
var validFields: Dictionary<String,Bool> = [" ...
but as the compiler can infer the type just delete the annotation.

Core Data Scalar error only on some devices

I've been testing primarily using iPhone 6, 6 plus, and iPad. Just tried my app today on the iPhone 5 simulator, and got a Core Data error.
CoreData: error: Property 'setId:' is a scalar type on class 'AppName.EntityName' that does not match its Entity's property's scalar type. Dynamically generated accessors do not support implicit type coercion. Cannot generate a setter method for it
Now, there is no 'setId' object in my app, but of course the entity does have an 'ID' object, which is set as an int.
class Entity: NSManagedObject {
#NSManaged var id: Int
}
In the Core Data model, the attribute type is set to Integer64. That might be my problem, as I picked that without knowing what was best. I have other attributes in the model and class, but they are all strings.
Looking for both a fix and an explanation as to why this happens only on some devices, so I can learn!
If the Core Data type is Integer 64 then you should declare the property as Int64 (or let Xcode create the managed object subclass).
Int can be 32 bit or 64 bit, depending on the processor architecture.
Alternatively, define the property as NSNumber* instead of a scalar property. Of course you have to take care that on a 32-bit platform the values do not exceed the range of Int.
Alternatively, you can write your own setter/getter method that transforms the Int into a NSNumber instance and passes this on the the primitive value. E.g.:
private(set) var publicId: Int {
get {
self.willAccessValueForKey("publicId")
let value = self.primitivePublicId.longValue
self.didAccessValueForKey("publicId")
return value
}
set {
self.willChangeValueForKey("publicId")
self.primitivePublicId = NSNumber(long: newValue)
self.didChangeValueForKey("publicId")
}
}
#NSManaged private var primitivePublicId: NSNumber
Don't forget to try a "Clean"! That fixed it for me even though I had deleted the app from my device.

Resources