Multiple subclasses of same PFObject subclass causing errors - swift

I have this very simple setup for my PFObject subclasses in Swift.
Foo : PFObject
Bar1 : Foo
Bar2 : Foo
Here is what they look like in code:
Foo
import Foundation
class Foo : PFObject, PFSubclassing
{
class func parseClassName() -> String!
{
return "Foo"
}
}
Bar1
import Foundation
class Bar1 : Foo {}
Bar2
import Foundation
class Bar2 : Foo {}
AppDelegate didFinishLaunching
Foo.registerSubclass()
Bar1.registerSubclass()
Bar2.registerSubclass()
Parse.setApplicationId("APP_ID", clientKey: "CLIENT_KEY")
I then get this error at runtime:
Tried to register both _TtC9CardForge4Bar1 and _TtC9CardForge4Bar2 as the native
PFObject subclass of Foo. Cannot determine the right class to use because neither
inherits from the other.
It appears that I can't have multiple subclasses of one PFObject subclass, but I see nothing for that in the documentation. What is going on here? What is a native subclass?

I don't think you can do this. I don't understand where do you want to go by doing this.
I think the best approach for subclassing in parse is : one subclass for one table. The most important goal of subclassing for Parse is managing your properties. So copy ALL your custom properties to your custom class. And just register this subclass. After that you can create other subclass (not registered to parse) for methods or protecting your properties.

As of Parse iOS SDK 1.6.2, I can confirm that you can have subclasses of subclasses of PFObject and they work just fine.
i.e. it's possible to have class Foo : Bar and class Bar : PFObject and you can save objects of type Foo and query for them as well. It just works.

Related

swift subclasses used in generics don't get called when inheriting from NSObject

Partial Solution Update at the end!
Attached is code that produces odd behavior. I copied it out of a swift playground so it should run in one fine.
I created a subclass in my project and passed it to my generic class as the concrete type. However, I quickly noticed that only the base class methods are called. This is shown with myBase and mySub below. Despite the generic class being instantiated as <mySub>, only the base methods are called. The print lines for the subclass are never shown.
Well, I found a simple way around that and that is to not inherit from NSObject. When I used swift native classes, the subclass methods are in fact called. These are secondBase and secondSub.
How do I pass a subclass into a generic class and get the actual subclass to receive calls when inheriting from NSObject?
And why would behavior be different?
import Foundation
// The Protocol
protocol P {
init ()
func doWork() -> String
}
// Generic Class
class G<T: P> {
func doThing() -> String {
let thing = T()
return thing.doWork()
}
}
// NSObject Base Class with Protocol
class A1: NSObject, P {
override required init() {
super.init()
}
func doWork() -> String {
return "A1"
}
}
// NSObject Sub Class
class B1: A1 {
required init() {
super.init()
}
override func doWork() -> String {
return "B1"
}
}
// Swift Base Class
class A2: P {
required init() {
}
func doWork() -> String {
return "A2"
}
}
// Swift Sub Class
class B2: A2 {
required init() {
super.init()
}
override func doWork() -> String {
return "B2"
}
}
print ("Sub class failure with NSObject")
print ("Recieved: " + G<B1>().doThing() + " Expected: B1 - NSObject Sub Class Generic (FAILS)")
print ("\nSub class success with Swift Native")
print ("Recieved: " + G<B2>().doThing() + " Expected: B2 - Swift Sub Class Generic (SUCCEEDS)")
print("")
#if swift(>=5.0)
print("Hello, Swift 5.0")
#elseif swift(>=4.1)
print("Hello, Swift 4.1")
#elseif swift(>=4.0)
print("Hello, Swift 4.0")
#elseif swift(>=3.0)
print("Hello, Swift 3.x")
#else
print("Hello, Swift 2.2")
#endif
Output:
Sub class failure with NSObject
Recieved: A1 Expected: B1 - NSObject Sub Class Generic (FAILS)
Sub class success with Swift Native
Recieved: B2 Expected: B2 - Swift Sub Class Generic (SUCCEEDS)
Hello, Swift 5.0
Partial solution update:
Moving the protocol conformance from the base class to the sub class allows the sub class to behave correctly. Definitions become:
class A1: NSObject
class B1: A1, P
The problem is the base class can no longer be used directly at all when no functionality beyond it is needed. This is mostly an issue if the protocol being conformed to has an associated type. When this is true, you must have a concrete class that conforms to the protocol for use in generics.
One use case here is expecting a base class in the generics (with a protocol involving an associated type) which allows something to function without caring what actual sub class was passed in. This actually ends up being a poor man's form of type erasure in some cases. And you can still use the same generic with the subclass.
G<A1>()
G<B1>()
This was derived from a similar question here: Generic Class does not forward delegate calls to concrete subclass
Partial options are:
remove NSObject and use swift native classes only
when NSObject is required, try to separate protocol conformance from inheritance of NSObject
UPDATE ON THE BELOW IDEA: Doesn't Work
I am going to test if providing an additional layer changes the behavior. Basically have 3 layers, Base class inheriting from NSObject, base Protocol class adding the protocol but inheriting from base and then specific classes. If it can distinguish between the base protocol class and the specific subclass in that case, that would be a functional workaround in all use cases. (and may explain why Apple's NSManagedObject works fine)
Still seems like a bug though.
I was able to confirm your results and submitted it as a bug, https://bugs.swift.org/browse/SR-10617. Turns out this is a known issue! I was informed (by good old Hamish) that I was duplicating https://bugs.swift.org/browse/SR-10285.
In my bug submission, I created a clean compact reduction of your example, suitable for sending to Apple:
protocol P {
init()
func doThing()
}
class Wrapper<T:P> {
func go() {
T().doThing()
}
}
class A : NSObject, P {
required override init() {}
func doThing() {
print("A")
}
}
class B : A {
required override init() {}
override func doThing() {
print("B")
}
}
Wrapper<B>().go()
On Xcode 9.2, we get "B". On Xcode 10.2, we get "A". That alone is enough to warrant a bug report.
In my report I listed three ways to work around the issue, all of which confirm that this is a bug (because none of them should make any difference):
make the generic parameterized type's constraint be A instead of P
or, mark the protocol P as #objc
or, don't have A inherit from NSObject
UPDATE: And it turns out (from Apple's own release notes) there's yet another way:
mark A's init as #nonobjc
This is not an answer so much as a way to avoid the problem.
In most of my code, I did not have to conform to NSObjectProtocol only Equatable and/or Hashable. I have implemented those protocols on the objects that needed it.
I then went through my code, removed all NSObject inheritance except on those classes which inherit from an Apple protocol or object that requires it (Like UITableViewDataSource).
The classes that are required to inherit from NSObject are Generic but they are not typically handed into other Generic classes. Therefore the inheritance works fine. In my MVVM pattern these tend to be the intermediate classes that work with view controllers to make logic like table views reusable. I have a tableController class that conforms to the UITableView protocols and accepts 3 generic viewModel types allowing it to provide the table logic for 95% of my views with no modifications. And when it needs it, subclasses easily provide alternate logic.
This is a better strategy as I am no longer randomly using NSObject for no reason.
This is a second way to avoid the problem.
#matt originally suggested this but then deleted his answer. It is a good way to avoid the problem. His answer was simple. Mark the protocol with objc like this:
// The Protocol
#objc protocol P {
init ()
func doWork() -> String
}
This solves the above sample code and you now get the expected results. But doing this has side effects for swift. At least one of those is here:
How to use #objc protocol with optional and extensions at the same time?
For me, it began a chain of having to make all my protocols objc compatible. That made the change not worth it for my code base. I was also using extensions.
I decided to stay with my original answer at least until Apple fixes this bug or there is a less invasive solution.
I thought this one should be documented in case it helps someone else facing this problem.

Cast T to generic superclass with unknown type

Let's say I have an array of type Foo with FooBar objects in it.
Bar <T> class is a generic class that inherits from Foo.
Then I have multiple FooBar classes, that inherits from Bar<T>, each with different generic type. (Bar<Int>, Bar<String>).
The problem is that I need to walk through the array of type Foo, check if na object is of type Bar<T>, cast it to Bar and set it's property.
Buty Swift won't allow me to cast to unknown type <T> and Bar<Any> doesn't match Bar<Int> or another...
I came up with two possible solutions - add new class to the hierarchy that inherits from Foo and is parent of Bar, but is not generic, and cast to this one or implement a protocol (which seems as much better solution).
What I'm really not sure about is if protocols are meant for this kind of stuff. In C-like lang, I would use an interface probably.
//Edit: Sample code
class Foo {
}
class Bar<T> : Foo {
var delegate : SomeDelegate
}
class FooBar: Bar<Int> {
}
class FooBar2: Bar<String> {
}
let arr : [Foo] = [ FooBar(), FooBar2(), Foo() ]
for item in arr {
// this is the problem - I need to match both String,Int or whathever the type is
if let b = item as? Bar<Any> {
b.delegate = self
}
}

Instantiating a nested class using NSClassFromString in swift

I have a nested class defined like this:
#objc class A {
#objc class B{
}
}
and I need to instantiate A.B using NSClassFromString. I was able to do it for the simple class A but when I attach to the NSClassFromString parameter the .B string it just returns nil.
NSClassFromString("\(appName).A") // works...
NSClassFromString("\(appName).A.B") //doesn't work.
I suppose that since nested class are not available in Objective-c the NSClassFromString just doesn't work for nested classes... in that case is there another way to initialize a nested class from a string?
// EDIT
is curious how the inverse function NSStringFromClassreturns different format when executed for a standard class and a nested class:
"myApp.A" <---- STANDARD CLASS (A)
"_TtCC15myApp13A6B" <----- NESTED CLASS (A.B)
As you can see the format is completely different. What is that _TtCC15? Why the "." has been removed? I suppose that passing the class in that format to NSClassFromString should work.
I find that the following works in a playground (Xcode 8.2 / Swift 3):
// inheriting NSObject is required for `#objc`, at which point `#objc` is optional
class A: NSObject {
class B: NSObject {
override var description: String { return "foo" }
}
}
let str = NSStringFromClass(A.B.self)
guard let anyClass = NSClassFromString(str)
else { fatalError("no class") }
// cast to a type that defines `init()` so we can instantiate
guard let nsClass = anyClass as? NSObject.Type
else { fatalError("class isn't NSObject") }
// call `.init()`, not `nsClass()`; constructor syntax is for static types only
let instance = nsClass.init()
print(instance) // -> "foo"
The oddball class "name" string is because the ObjC runtime doesn't understand nested classes (or other kinds of types that Swift can define but ObjC can't) -- within Swift itself, such mangled names are how types, functions and such get uniquely defined. (For example, name mangling is also how function overloading works: func foo() and func foo(num: Int) -> Bool have different mangled names internally.)
The bridging machinery can still be made to dynamically resolve a class given its properly mangled Swift name, but Swift name mangling is an implementation detail and subject to change. (At least until Swift 4 locks down the ABI.) So it's safe to pass the result of NSStringFromClass to NSClassFromString within the same program, but not safe to (e.g.) record a mangled name once in testing, ship that mangled name as a string literal or resource in your app, and expect it to work with NSClassFromString later.
Instead, if you want your nested class (or any other Swift-defined class) to have a reliably known name for use in the ObjC runtime, give it an #objc(name) (as described in the docs):
#objc class A: NSObject {
#objc(A_B) class B: NSObject { /*...*/ }
}
guard let anyClass = NSClassFromString("A_B")
else { fatalError("no class") }
(Note this snippet won't run by itself -- the class A.B has to be referenced at least once somewhere in your process in order to be registered with the ObjC runtime. This could be as simple as putting let t = A.B.self somewhere in your app's startup code.)

How can I conform to a protocol if a class has no superclass? [duplicate]

This question already has answers here:
Why in swift we cannot adopt a protocol without inheritance a class from NSObject?
(2 answers)
Closed 7 years ago.
I have a controller class (not viewController) that I want to conform to a protocol.
This controller class is not a subclass of any other class, so it has no superclass.
How can I conform to a protocol if a class has no superclass?
This is NOT working:
class xxxController , yyyDelegate
and this is NOT what is desired:
class xxxController : zzzSuperClasss, yyyDelegate
Anybody knows how it works in Swift 2.1?
UPDATE:
I forgot to write that I have tried:
class xxxController : yyyDelegate
When I do this, I get
Type xxxController does not conform to protocol NSObjectProtocol
so I'm guessing yyyDelegate requires (in this case) that xxxController is subclass of NSObject?
If a class has a superclass, then the superclass must be listed first among the base items. When class has no superclass, protocols are listed starting from the first item.
In other words, if there is no zzzSuperClasss, then you could simply write
class xxxController : yyyDelegate
Swift realizes that yyyDelegate is a protocol, and does what you expect.
Documentation provides an example of such class:
protocol FullyNamed {
var fullName: String { get }
}
class Starship: FullyNamed {
...
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
You do it either by directly declaring the conformance:
protocol Foo { }
class Bar: Foo { }
.. or via extension (e.g. if you do not have the control over the class implementation, like in the cases when you get it from some library):
protocol Foo { }
class Bar { }
extension Bar: Foo { }

Swift protocol allows unimplemented function when it exists in base class

Assume the below playground:
protocol MyProtocol
{
func MyFunc()
}
class MyBase : MyProtocol
{
func MyFunc()
{
}
}
class MyClass : MyBase, MyProtocol
{
}
I was expecting this to not compile, and nag about not implementing my protocol. But unlike other languages, Swift seems to think its fine if the protocol conforms to the base class.
How can I get MyClass to force my method to be overridden?
I'm using Swift 1.1, tia.
Your code is definitely valid. MyClass inherits from MyBase which already conforms to MyProtocol, so having the subclass also conform to it is completely redundant. That being said, you are basically looking for an abstract function which does not really exist in Swift, the closest I think you can get to achieving what you want is recommended in this answer. So in your base class just give a warning if not overriden:
class MyBase : MyProtocol {
func MyFunc() {
fatalError("This method must be overridden")
}
}
As recommended in the question comments, a different approach may work better for what you are attempting to do (e.g. composition or using a struct), but it is a bit hard to recommend a different solution without knowing more about what you are trying to achieve (rather than just the expected solution). If you do provide additional details you may receive a more suitable answer.