Instantiating Realm objects using Generics in Swift - swift

I am working in a iOS project involving Realm and the use of Generics. I am exploring ways to clone a retrieved Realm object to update it outside a write transaction and then send it to an update function using generics.
I am facing a weird problem and i don't know if it is related to Realm or to the Generics stuff. You help will be appreciated
Setting:
One class GenericObject that inherits from Realm Object, and a subclass called Sale:
GenericObject: Object
Sale: Generic Object // This class includes a primary key called "id"
I fetch a Sale object from the web and I am able to save it in Realm, creating a new object outside the write transaction (I could save it without worrying about the write transaction, but I want to use the same code and flow for any update)
When I modify a property of the object and try to update Realm, it throws an exception because the primary key can't be found. (The primaryKey is defined in the subclass Sale)
I have been able to pinpoint the problem to my newItem() method in Sale as follows:
override func newItem<T:GenericObject>(ofType itemType: T.Type) -> T {
let dictionary = self.getDictionary()
let newItem = T.init()
newItem.updateWithDictionary(dict: dictionary)
print("Type: \(type(of: newItem)) - Object: \(newItem)")
return newItem
}
And then, I call it as follows:
let newObject = object.newItem(ofType: Sale.self)
self.realm.add(newObject, update: true)
So far, so good. I retrieve the object from the web and it works. The print() reports that type(of:) the instantiate object is Sale, and the printout of the object also says Sale
Type: Sale - Object: Sale { ....
When I update the object and save it, it fails saying that Realm could not find the primary key, type(of:) reports Sale, but the instance is printed as the GenericObject superclass, as follows:
Type: Sale - Object: GenericObject { ....
This result is running the same code and the same code execution. I am using Xcode 10 and Swift 4.2, with Realm 3
Any idea what may be happening here?

After 6 months, the problem seems to be fixed without a clear indication of what was going on.
As of 2019-04-22, having migrated to Realm 3.14.1, Xcode 10.2.1 and Swift 5.0, I am able to get a clone of the object using T.init(), and saving it successfully outside the Realm write transaction with the original code used when I posted the question
I don't see anything related to a syntax change from Swift 4.2 to 5.0, but I understand that Xcode 10.2.1 includes updates to LLVM/clang.
I'd love to spend some time checking the new compiler with the previous Realm version

Related

Binding in OSX: class is not key value coding-compliant for the key {name of binding var}

I was trying to follow instructions from Learning Swift book (creating note taking app) by B.A. Paris & Co, but faced with the following problem with binding. I am mostly practicing iOS programming, so binding concept is new for me.
Steps I made (tried both xcode 9 beta 5 and 8.3.3):
Create OSX Cocoa App (not using storyboard, document based app – on, document
extension “test”, don’t use core data)
Add “var text = NSAttributedString()” to Document.swift
Add a NSTextView to Document.xib
In Bindings inspector of NSTextView setting “Attributed String” to File’s owner “self.text” (Model Key Path)
And I see exclamation mark with notion “Xcode cannot resolve the entered key path”
Build is successful, but when I run it says “2017-09-03 22:17:40.739643+0200 test3[6017:424072] [<test3.Document 0x6180000c3410> valueForUndefinedKey:]: this class is not key value coding-compliant for the key text.”
I tried to control drag from Xib to Swift, it warns that “Xcode cannot locate the class Document in the current workspace”.
I tried to convert to workspace instead of proj, checked the file owner, checked the stackoverflow threads witch relate to the error – but they mostly concerned about some connection made by mistake or non actual connections (I can delete the connection, I know what connection is wrong, the question is how to make it right). So far could not find solution.
Thanks in advance
You need to declare the text property with the #objc attribute to make it accessible via dynamic dispatch like Key-Value Coding.
Also, because you want modifications of the property to be observable via Key-Value Observing (for Bindings), you need to tell Swift to always dispatch modifications of it dynamically. So, you need to use the dynamic modifier on the declaration, too:
#objc dynamic var text = NSAttributedString()

Cannot call value of non-function type 'ThreadConfined.Type' - Swift 4.0

I upgraded a project I am working on to Swift 4.0. After doing so I realized this was not the best idea. I've fixed all bugs but one and can't figure it out. I have installed RealmSwift in my project and am getting the following error in one of the Realm files.
ERROR: Cannot call value of non-function type 'ThreadConfined.Type'
public init(to threadConfined: Confined) {
let bridged = (threadConfined as! AssistedObjectiveCBridgeable).bridged
swiftMetadata = bridged.metadata
type = type(of: threadConfined). ****ERROR CALLED ON THIS LINE****
objectiveCReference = RLMThreadSafeReference(threadConfined: bridged.objectiveCValue as! RLMThreadConfined)
}
Lesson learned about upgrading too soon. I was hoping someone could give me a hand so I can start developing again. Any thoughts?
Realm's master branch now contains support for Swift 4 and beta 1 of Xcode 9 (#5006). Using a build of Realm Swift from source should get you up and running.
I noticed that even though I was building from source (using CocoaPods), this issue was happening for me as well.
To solve it, two lines need to be removed (as seen in the file in #jonthornham's comment):
private let type: ThreadConfined.Type
and:
type = type(of:threadConfined)

Typhoon loading storyboard programmatically appears to perform asynchronous instantiation without blocking

I am developing an iOS application and am trying to integrate Typhoon into the testing. I am currently trying to mock out a dependency in a view controller that comes from the storyboard, so with in my assembly:
public dynamic var systemComponents: SystemComponents!
public dynamic func storyboard() -> AnyObject {
return TyphoonDefinition.withClass(TyphoonStoryboard.self) {
(definition) in
definition.useInitializer("storyboardWithName:factory:bundle:") {
(initializer) in
initializer.injectParameterWith("Main")
initializer.injectParameterWith(self)
initializer.injectParameterWith(NSBundle.mainBundle())
}
}
}
I want to create a CameraModeViewController (the class I am unit testing) with its dependency upon a system-camera-functions-providing protocol mocked out. The dependency is dynamic var cameraProvider: CameraAPIProvider?. I think I correctly created a replacement collaborating assembly to replace systemComponents; MockSystemComponents is a subclass of SystemComponents that overrides functions. This is where I inject the mock:
let assembly = ApplicationAssembly().activateWithCollaboratingAssemblies([
MockSystemComponents(camera: true)
])
let storyboard = assembly.storyboard()
subject = storyboard.instantiateViewControllerWithIdentifier("Camera-Mode") as! CameraModeViewController
The next line of code in the tests is let _ = subject.view, which I learned is a trick to call viewDidLoad and get all the storyboard-linked IBOutlets, one of which is required for this test.
However, I am getting very mysterious result: sometimes but not always, all the tests fail because in the viewDidLoad I make a call to the dependency (cameraProvider), and I get an "unrecognized message sent to class" error. The error seems to indicate that at the time the message is sent (which is a correct instance method in protocol CameraAPIProvider) the field is currently a CLASS and not an instance: it interprets the message as +[MockSystemCamera cameraStreamLayer] as reported in the error message.
~~~BUT~~~
Here's the kicker: if I add a breakpoint between the calls to assembly.storyboard() and subject.view, the tests always pass. Everything is set up correctly, and the message is correctly sent to an instance without this "class method" bogus interpretation. Therefore, I have to wonder if Typhoon does some kind of asynchronous procedure in the injection that I have to wait for? Possibly only when dealing with storyboard-delivered view controllers? And if so, is there any way to make sure it blocks?
After digging around in Typhoon's source for a while, I get the impression that in the TyphoonDefinition(Instance Builder) initializeInstanceWithArgs:factory: method there is an __block id instance that is temporarily a Class type, and then is replaced with an instance of that type; and possibly this can be called asynchronously without blocking, so the injected member is left as a Class type?
UPDATE: Adding the code for MockSystemComponents(camera:). Note that SystemComponents inherits from TyphoonAssembly.
#objc
public class MockSystemComponents: SystemComponents {
var cameraAvailable: NSNumber
init(camera: NSNumber) {
self.cameraAvailable = camera
super.init()
}
public override func systemCameraProvider() -> AnyObject {
return TyphoonDefinition.withClass(MockSystemCamera.self) {
(definition) in
definition.useInitializer("initWithAvailable:") {
(initializer) in
initializer.injectParameterWith(self.cameraAvailable)
}
}
}
}
UPDATE #2: I tried replacing the constructor injection in the MockSystemComponents.systemCameraProvider() with a property injection. Different issue, but I suspect it's equivalent in cause: now, the property that is injected (declared optional) is still nil some of the time when I go to unwrap it (but not always -- probably about 4/5 of test runs fail, about the same as before).
UPDATE #3: have tried using the following code block, using factory construction according to this answer (note that setting factory directly didn't work as that OP did, but I think I correctly used the feature added in response to Jasper's issue). The results are the same as when using property injection like Update #2 above), so no dice there.
This issue was in fact arising even before the call to the instantiation. In fact, the problem was assemblies aren't generally intended to be stateful. There are a few ways to get around this, but the one I used -- having a member variable and an initializer method -- is NOT recommended. The problem with doing this is that in the activateWithCollaboratingAssemblies method, all the instance methods of the assembly are enumerated for definitions, and initializers will actually get called on the collaborating assembly. Consequently, even if you create your assembly with an initializer, it may get called again with a bogus value.
Note that the reason there appeared to be async behavior is actually that there is nondeterministic order in which definitions are assembled (property of storing them in an NSDictionary). This means that if activateWithCollaboratingAssemblies happens to enumerate methods which depend on state first, they'll work fine; but if the initializer is enumerated first, and the state is destroyed, definitions that are created after will be borked.

Core Data: Could not cast value of type 'MyType_MyType_2' to MyType

I have an Objective-C model class MyType. This class is used in Swift code:
NSEntityDescription.insertNewObjectForEntityForName("MyType", inManagedObjectContext: context) as! MyType
The as! cast results in the error message
Core Data: Could not cast value of type 'MyType_MyType_2' (0x7be99a30) to MyType (0xf33db74).
If I cast it as NSManagedObject it works. When I print the result, I can nevertheless see, that it is an actual instance of MyType:
<MyType: 0x7ae06d50> (entity: MyType; id: 0x7aeba6d0 <x-coredata:///MyType/t957F2860-85F8-46E0-B6D6-4D1DF6C4EC613> ; data: {
...the fields...
})
What is happening here? And where does the name MyType_MyType_2 come from?
When I had this issue, it was because I had forgotten to set the "class" on the entity. This is what I came up with:
Click on .xcdatamodelId file in your file structure/project
navigator pane (far left).
Select the entity that you are having issues with.
In the Utilities pane (far right), look for the icon that looks like a 1997 cell phone, the Data Model Inspector.
Under the entity settings, you should see two fields, "Name" and "Class" - set up "Class" with the name of the class you are using (typically the same as "Name").
You'll even notice before you follow these steps that the default "class" is NSObject, reflecting the error message. I found some programatic ways to do this too, but this seemed like the simplest/quickest solution.
I should note that my model WAS written in Swift, so I did have to add the #objc(Entity) interoperability reference mentioned by #zellb. But that shouldn't make a difference in the solution as long as you are doing that part properly (and that would cause a different unrelated error from my understanding).
Set Entity Class Name
Set Module "Current Product Module" like below
just try this:
#objc(MyType)
public class MyType: NSManagedObject {
// your class
}
instead of this:
class MyType: NSManagedObject {
// your class
}
I had mistakenly set a "parent entity" in one of my entities in the data model inspector in the entity section. I mistakenly thought that referred to the destination of a one-to-many relationship.
Setting it back to "no parent entity" fixed the problem, although I did have to delete and reinstall the app in the simulator to deal with the messed up core data database.

Trying to create honest NSDecimalNumber in Wax

As I studied the issue I am facing in Trying to create NSDecimal in iPhone-Wax I am now aiming much lower. How can I create a fully functional NSDecimalNumber in Wax?
I have added the following two lines at the top of AppDelegate.lua in a fresh wax project.
local x = NSDecimalNumber:initWithString("2.3")
print(x)
print(x:class())
The output is
(0x631e054 => 0x631d1a0) 2.3
(0x631e924 => 0x25f618) NSCFNumber
instead of something like
(0x621e834 => 0x620c550) <NSDecimalNumber: 0x620c550>
Turning on full logging in wax give the following trace in the debug window:
Creating class for WaxServer(0x621bf40)
Storing reference of class to userdata table WaxServer(0x621bf40 -> 0x621c454)
Storing reference to strong userdata table WaxServer(0x621bf40 -> 0x621c454)
Creating class for NSDecimalNumber(0x261120)
Storing reference of class to userdata table NSDecimalNumber(0x261120 -> 0x6205e44)
Storing reference to strong userdata table NSDecimalNumber(0x261120 -> 0x6205e44)
Creating instance for NSDecimalNumberPlaceholder(0x6213450)
Retaining instance for NSDecimalNumberPlaceholder(0x6213450 -> 0x621d7c4)
Storing reference of instance to userdata table NSDecimalNumberPlaceholder(0x6213450 -> 0x621d7c4)
Storing reference to strong userdata table NSDecimalNumberPlaceholder(0x6213450 -> 0x621d7c4)
Creating instance for NSCFNumber(0x620c550)
Retaining instance for NSCFNumber(0x620c550 -> 0x621e834)
Storing reference of instance to userdata table NSCFNumber(0x620c550 -> 0x621e834)
Storing reference to strong userdata table NSCFNumber(0x620c550 -> 0x621e834)
(0x621e834 => 0x620c550) 2.3
Creating class for AppDelegate(0x621ec50)
:
:
Two things are showing in this log which I did not ask for, NSDecimalNumberPlaceholder and NSCFNumber. I believe these are the source of my grief and I have no idea where they are coming from. Any ideas on how to fix the issue?
Ultimately I want to call the method decimalValue, but wax complains that it can't call a method on a number.
NSDecimalNumber overrides -description to return the number it represents. When you're logging statement is printing "2.3", it is in fact printing the NSDecimalNumber object. You can verify this for yourself by calling -class on your x value and printing that as well.