NSKeyedUnarchiver issue when moving model to framework [duplicate] - ios

This question already has an answer here:
Added a custom framework, now Swift can't unarchive data
(1 answer)
Closed 2 years ago.
I have moved part of my code into a separate framework, and that framework contains a model used by the app to persist data to CoreData. Now when I try to launch the app, and it tries to restore state from CoreData, I get the following error.
2020-06-17 21:01:42.549851-0400 Project[17576:2986881] [error] fault: exception raised during multi-threaded fetch *** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Project.Track) for key (NS.objects) because no class named "Project.Track" was found; the class needs to be defined in source code or linked in from a library (ensure the class is part of the correct target). If the class was renamed, use setClassName:forClass: to add a class translation mapping to NSKeyedUnarchiver ({
"__NSCoderInternalErrorCode" = 4864;
})
It worked as expected before splitting the code off into a separate framework. I have tried to import the framework into each class that uses CD, and I have even tried to map it to the class' new namespace using Framework.Track, but I am still getting this error. Has anyone encountered this, and if so can you share some insight into resolving the error? I appreciate any help.

I found the answer, it was as specified by the exception. I just had to set the class name using the app's namespace to the new imported class, as so:
NSKeyedUnarchiver.setClass(Track.self, forClassName: "Project.Track")
Hope that helps someone.

Related

Name conflict with Realm "Size" object when using Swift and Objective-C

I have a project named 'Foo' that is mostly Swift, but there is one objective-c view controller. When I include 'Foo-Swift.h' in ObjectiveCViewController.m I get this error:
"Redefinition of 'Size' as different kind of symbol"
This is because I have a Realm Object called 'Size'. The simple solution here is to rename my Realm Object FooSize or something similar - but is there a slicker way to do this? I'm not familiar with every last Swift feature and I wonder if there is some kind of namespace type thing I could use, or similar feature.

'filenames are used to distinguish private declarations of the same name' error

I am getting this error on generating an NSManagedObject in Xcode 8.1 in Swift.
:0: error: filename "DemoOne+CoreDataClass.swift" used twice: '/Users/Swasidhant/Desktop/demo again/DemoOne+CoreDataClass.swift' and '/Users/Swasidhant/Library/Developer/Xcode/DerivedData/demo_again-hiinrbwwbmyfbrbctsfdzvudkkuy/Build/Intermediates/demo again.build/Debug-iphonesimulator/demo again.build/DerivedSources/CoreDataGenerated/Model/DemoOne+CoreDataClass.swift'
:0: note: filenames are used to distinguish private declarations with the same name
:0: error: filename "DemoOne+CoreDataProperties.swift" used twice: '/Users/Swasidhant/Desktop/demo again/DemoOne+CoreDataProperties.swift' and '/Users/Swasidhant/Library/Developer/Xcode/DerivedData/demo_again-hiinrbwwbmyfbrbctsfdzvudkkuy/Build/Intermediates/demo again.build/Debug-iphonesimulator/demo again.build/DerivedSources/CoreDataGenerated/Model/DemoOne+CoreDataProperties.swift'
:0: note: filenames are used to distinguish private declarations with the same name
Command /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swiftc failed with exit code 1
I searched for this and got answers saying the file might be present twice in the project which is not my case. Another solution I came across was to remove the file from the build phase and add again. That too didn't work. Is it some build setting configuration mistake ? Or something else?
Here is the link with a demo project created with Xcode 8.1 and showing this problem:- https://www.dropbox.com/s/xwyzhshktb2hqe7/demo2.zip?dl=0
The issue is happening because of the Xcode automatic subclass generation feature.
According to What's New In Core Data
Xcode automatic subclass generation
Xcode now supports automatic generation of NSManagedObject subclasses
in the modeling tool. In the entity inspector:
Manual/None is the default, and previous behavior; in this case you
should implement your own subclass or use NSManagedObject.
Category/Extension generates a class extension in a file named like
ClassName+CoreDataGeneratedProperties. You need to declare/implement
the main class (if in Obj-C, via a header the extension can import
named ClassName.h).
Class Definition generates subclass files named
like ClassName+CoreDataClass as well as the files generated for
Category/Extension.
The generated files are placed in DerivedData and
rebuilt on the first build after the model is saved. They are also
indexed by Xcode, so command-clicking on references and fast-opening
by filename works.
So for fixing your issue, you need to set the Codegen field value to Manual/None as shown below:
This is what I did to fix this issue,
Select your entity in the data model
Set the Module to Current Product Module
Leave the Codegen to Class Definition though Manual/None also works
Select Editor > Create NSManagedObject Subclass
May help someone, Go to project compile source delete the extra reference if any.
Select code data model then select the Entity change the value of 'Codegen' to Manual/None.
It will work.
The issue was due a second file named:"Extensions.swift" in my project.
After deleting the duplicate "Extensions.swift" the error got resolved. Your error message might tell you which file is duplicated.
In my case Data Model (as example: MyCoreData.xcdatamodeld) was listed twice under Editor -> Create NSManagedObject Subclass... And here was reason why it was getting this type of error.
What I did:
I did delete MyCoreData.xcdatamodeld file.
Created another Data Model as example MyCD.xcdatamodeld.
Checked is here duplicated Data Models in here: Editor -> Create NSManagedObject Subclass...
Clean and later build for two times.
It happened to me when I copied and renamed entity. It failed to rename the class, so do it manually.
I just restart my Macbook and that clean my build folder. And it works like a charm!

Serializing a Swift class to JSON using J2ObjC and GSON library

I'm very new to iOS, moving from Android development.
We have an Android application which has several libraries which we're using J2ObjC to translate into Objective C.
I've been gradually bringing over the libraries and so far, so good.
However, after translating Google's GSON library I have an issue where if I try and use the translated method toJsonWithId I come across problems:
let gson = ComGoogleGsonGson()
let swiftTest = GsonSwiftTest()
swiftTest.name = "Ricky"
print(gson.toJsonWithId(swiftTest))
I receive the following error:
libc++abi.dylib: terminating with uncaught exception of type
JavaLangAssertionError
Further it shows:
Terminating app due to uncaught exception 'JavaLangAssertionError',
reason: 'unknown Java type encoding'
My Swift class is:
import Foundation
#objc
public class GsonSwiftTest : NSObject {
var name:String?
}
If I instead use an Objective C class in my Swift project, add it to the bridging header and use the Objective C class it works fine.
I believe this is a misunderstanding on my part, but I can't find the answer as to why this won't work. Checking the J2ObjC source code shows the error is raised when the type of class can't be found.
If anyone could help explain the reasons for this issue, it'd be appreciated.
Thanks!
There's a much easier way to serialize Swift instances: use the Foundation class NSJSONSerialization directly. Here's a relevant blog posting: JSON Serialization in Swift.
The reason your test fails in Swift is that not all Swift classes extend NSObject, which j2objc maps to java.lang.Object. All Java serialization frameworks assume/require that all classes extend Object, like they do in Java.
For a cross-platform API, I recommend creating a simple serialize/unserialize interface, then implement it once for Android using GSON, and once natively using NSJSONSerialization. Swift is evolving rapidly, so if a better serialization mechanism becomes available, your project can easily take advantage of it without affecting the Android port.

How to test Core Data properly in Swift

There are quite a few subjects on this already, but I have yet to find a solution that is workable for Swift (Xcode 6.2).
To test Core Data backed classes in Swift, I generate new Managed Object Contexts that I then inject into my classes.
//Given
let testManagedObjectContext = CoreDataTestComposer.setUpInMemoryManagedObjectContext()
let testItems = createFixtureData(testManagedObjectContext) as [TestItem]
self.itemDateCoordinator.managedObjectContext = testManagedObjectContext
//When
let data = self.itemDateCoordinator.do()
//Then
XCTAssert(data.exists)
The issue comes from passing a MOC created in the Test to the class that's doing. Because entity classes are namespaced, Core Data won't fetch your the appropriate ManagedObject subclass and instead hands back a NSManagedObject set. When looping or doing anything with these objects (which in your class would be an array of test items ([TestItem]).
For example, the offending class ItemDateCoordinator would execute this loop (after pulling the relevant data from a NSFetchRequest)"
for testItem in testItems {
testItem.doPart(numberOfDays: 10)
}
would result in:
fatal error: NSArray element failed to match the Swift Array Element type
Also, I have come across a collection of information without much of a solid answer:
To cast entities when creating them, I have been using a solution by Jesse, but that doesn't work on a larger scope of testing.
A solution has been posted on another question that involved swapping out the classes at runtime, but that hasn't worked for me with entity inheritance.
Is there another method to testing your objects with Core Data in this case? How do you do it?
I was about to point you toward Swift, Core Data, and unit testing but see you've already found it. :)
That post doesn't elaborate much on where your files should exist (i.e., in which Target). You should not add NSManagedObject subclasses (or any files really) to both targets. I've found that this leads to all kinds hard discover bugs and cryptic errors.
And definitely DO NOT do this. That is a terrible hack.
Instead, make your classes public and import MyAppTarget in your XCTestCase files. Better yet, your model should be in its own framework as I mention in my recent talk (a video will be posted in a few weeks on realm.io). Doing this makes your models namespace very clear and generally easier to deal with. Then you'll need to import MyAppModel everywhere you access your managed objects.
I also have a new framework, JSQCoreDataKit that intends to make Core Data easier to use in Swift. One key part of this framework is the CoreDataStack which you can initialize using an in-memory store for your tests. There's demo app with examples, and well-commented unit tests.
I believe this was updated recently (iOS 9/Swift 2.0) to have the testable keyword on an imported target, mean that the target's internal classes (the default) become public. From the docs:
So to add to jessesquires answer above, append #testable to your import, and this should solve the unit test errors:
#testable import MyAppTarget

Using bTouch to bind to the ArcGIS iOS SDK

I apologize for such a long message in advance, but I'm trying for detail here...
I'm working on using bTouch to create a compiled dll for referencing the ArcGIS iOS SDK.
When running bTouch using :
/Developer/MonoTouch/usr/bin/btouch libArcGIS.cs
it returns the following error
/tmp/fp2ivuh8.3gj/IncidentReportApp/AGSMutablePolygon.g.cs(39,31):
error CS0102: The type `IncidentReportApp.AGSMutablePolygon'
already contains a definition for `selAddPointToRing'
/tmp/fp2ivuh8.3gj/IncidentReportApp/AGSMutablePolygon.g.cs(38,31):
(Location of the symbol related to previous error)
/tmp/fp2ivuh8.3gj/IncidentReportApp/AGSMutablePolyline.g.cs(39,31): error CS0102:
The type `IncidentReportApp.AGSMutablePolyline'
already contains a definition for `selAddPointToPath'
/tmp/fp2ivuh8.3gj/IncidentReportApp/AGSMutablePolyline.g.cs(38,31):
(Location of the symbol related to previous error)
Compilation failed: 2 error(s), 0 warnings
I checked my cs class and neither type is referenced\invoked. I'd like to understand why this message is occuring.
I have tried to use the instructions (and downloaded) code by Al Pascual at How to use the ArcGIS iPhone SDK with MonoTouch to call the Map View, but when attempting to launch the view with the code causes a crash. When I try to debug, it locks up when adding a mapping layer. I tested this with the MKMapView, but didn't experience the same behavior.
The error means that you defined more than one method mapping the same objective-C method.
Without the source, it is hard to diagnose.
I'm doing the same thing actually, I heavily modified the old "parser" library and am working on doing it now, hopefully dropping it in the public domain.
I'm seeing a similar (and probably related) problem in the ApiDefinition, there is a class AGSGPResultLayer that derives from AGSDynamicLayer. The AGSGPResultLayer overrides a property called Credential among other and since both are defining the same property.
How should over-riden properties be handled in bTouch? I'm guessing I'm missing something in the syntax.
Use the solution I provide with the correct bindings

Resources