Core Data Scalar error only on some devices - ios

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.

Related

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

Firebase server timestamp and non 64 bit device: Integer literal '...' overflows when stored into 'Int'

I have stored messages in Firebase like so:
messageObject["timestamp"] = FIRServerValue.timestamp()
The objects have a child like: timestamp: 1465222757817. The problem is that older non 64 bit devices cannot handle Integers of that length. What would be a good workaraound for this problem?
Edit:
When declaring the timestamp as Int64, it throws an error:
var timestampQueryValue: Int64 = 1465222757817
self.chatRef.queryOrderedByChild("timestamp")
.queryStartingAtValue(timestampQueryValue)
.observeEventType(.ChildAdded, withBlock: {
(snapshot) -> Void in /* ... */ })
/* Error: Cannot convert value of type 'Int64'
to expected argument type 'AnyObject?' */
You can explicitly deal with larger numbers, even on 32 bit devices, if you explicitly specify UInt64 or Int64 (unsigned, and signed, respectively).
(This answers the edited question: error when explicitly using the Int64 type)
Given your error message, it would seems as if the method .queryStartingAtValue(...) expects type AnyObject?, which will allow using, as argument, types automatically (implicitly) bridgeable to AnyObject, which explains why you don't have this issue with the Int type, whereas you do with the Int64 type.
Interoperability - Working with Cocoa Data Types - Numbers
I.e., the former (Int) is automatically bridged to a Obj-C/Cocoa class type (NSNumber) whereas this automatic bridging is not natively accessible for the latter (Int64).
There's two ways to redeem this
Explicitly perform the bridging from Int64 to the equivalent NSNumber type, using the NSNumber initializer init(longLong: <Int64>)
let foo: Int64 = 1465222757817
let bar = NSNumber(longLong: foo)
I.e., in your example, you could attempt the following:
//...
.queryStartingAtValue(NSNumber(longLong: timestampQueryValue))
Or, using undocumented features (that might break in the future): conform Int64 to the internal protocol _ObjectiveCBridgeable, to allow the same implicit NSNumber bridging as is available for Int type. The following threads explains exactly this implementation:
Is it possible to replicate Swifts automatic numeric value bridging to Foundation (NSNumber) for (U)Int8/16/32/64 types?
After implementing this implicit bridging for Int64, your existing code should work as is, as the Int64 argument to .queryStartingAtValue(...) will be automatically converted to the appropriate NSNumber (class) type.

Dynamic optional properties in Swift 2.0

I have seen this post Optional dynamic properties in Swift but I don't want to have to wrap up the class in an NSObject. This just is concerning the Realm database I don't have to have nil properties but it would be a nice way I think to model my database. In the Realm documentation which can be found here https://realm.io/docs/swift/latest/ it says optionals are supported. Here is my
Code
dynamic var complete: Bool? = nil
and here is my
Error
Property cannot be marked dynamic because its type cannot be represented in Objective-C
I know this is the same code and error as the post above but I am just curious if the Realm documentation says it supports it do they have another work around?
From the docs on supported types and optional properties.
String, NSDate, NSData and Object properties can be optional. Storing optional numbers is done using RealmOptional.
RealmOptional supports Int, Float, Double, Bool, and all of the sized versions of Int (Int8, Int16, Int32, Int64).
So optionals are supported for String, NSDate, NSData and Object types nicely with the standard swift syntax.
For other numeric types (such as Bool) that is done with RealmOptional. Then to use a variable of this RealmOptional type you access its value property, which is an optional that represents your underlying value.
// definition (defined with let)
let complete = RealmOptional<Bool>() // defaults to nil
// usage
complete.value = false // set non-nil value
...
complete.value = nil // set to nil again

Swift - Weird coding-compliant error

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.

How do I use CFArrayRef in Swift?

I'm using an Objective-C class in my Swift project via a bridging header. The method signature looks something like this:
- (CFArrayRef)someMethod:(someType)someParameter;
I started by getting an instance of the class, calling the method, and storing the value:
var myInstance = MyClassWithThatMethod();
var cfArr = myInstance.someMethod(someValue);
Then try to get a value in the array:
var valueInArrayThatIWant = CFArrayGetValueAtIndex(cfArr, 0);
However I get the error Unmanaged<CFArray>' is not identical to 'CFArray'. What does Unmanaged<CFArray> even mean?
I looked through How to convert CFArray to Swift Array? but I don't need to convert the array to a swift array (however that would be nice). I just need to be able to get values from the array.
I have also tried the method of passing the CFArray into a function outlined in this answer:
func doSomeStuffOnArray(myArray: NSArray) {
}
However I get a similar error when using it:
doSomeStuffOnArray(cfArr); // Unmanaged<CFArray>' is not identical to 'NSArray'
I am using CFArray because I need to store an array of CGPathRef, which cannot be stored in NSArray.
So how am I supposed to use CFArray in Swift?
As explained in
Working with Core Foundation Types, there are two possible solutions when
you return a Core Foundation object from your own function that is imported in Swift:
Annotate the function with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED.
In your case:
- (CFArrayRef)someMethod:(someType)someParameter CF_RETURNS_NOT_RETAINED;
Or convert the unmanaged object to a memory managed object with takeUnretainedValue() or takeRetainedValue() in Swift. In your case:
var cfArr = myInstance.someMethod(someValue).takeUnretainedValue()
An Unmanaged is a wrapper for an actual CF value. (Sort of like an optional.) It's there because ARC can't tell from looking at the declaration of someMethod: whether that method retains the value it returns.
You unwrap an Unmanaged by telling ARC what memory management policy to use for the value inside. If someMethod calls CFRetain on its return value:
let cfArr = myInstance.someMethod(someValue).takeRetainedValue()
If it doesn't:
let cfArr = myInstance.someMethod(someValue).takeUnretainedValue()
After you do that, cfArr is a CFArray, so you can use the bridging tricks from the other questions you linked to for accessing it like a Swift array.
If you own the code for someMethod you can change it a bit to not need this. There's a couple of options for that:
Annotate with CF_RETURNS_RETAINED or CF_RETURNS_NOT_RETAINED to tell the compiler what memory behavior is needed
Since it's an ObjC method, bridge to NSArray and return that--it'll automatically become an [AnyObject] array in Swift.

Resources