I'm trying to bind a SWIFT library to Xamarin iOS.
Source code has class:
#objc(ClassA)
public class ClassA: NSObject
{
<...>
public init(param1: StructA, param2: EnumStringA = .defaultValueA) {
<...>
}
}
but in *-Swift.h file it looks like this:
SWIFT_CLASS_NAMED("ClassA")
#interface ClassA: NSObject
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
+ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable");
#end
So, of course, Sharpie tool make the interface empty, without constructor:
[BaseType(typeof(NSObject))]
[DisableDefaultCtor]
interface ClassA
{
}
I thought maybe its because both StructA and EnumStringA are not exposed, but #objc attribute is not applicable for struct and for enum : String
How to make the constructor is visible?
Related
TL:DR
How can I make this compile so the protocol can be applied to any concrete implementations of BaseObject, specifically so it can access someFuncUnrelatedToTypeConstraint (that's the real goal)?
#interface BaseObject <__covariant TypeConstraint> : NSObject
- (TestObject*)someFuncUnrelatedToTypeConstraint;
#end
protocol ProtocolOnlyApplicableToBaseObject : BaseObject {}
Note: This won't compile because it complains I need to specify the type parameter for BaseObject, but that's just it... I want this to apply to any/all BaseObject<T> types regardless of what T is.
Now the full code...
In our ObjC codebase, we have a concrete object and a generic defined like so (oversimplified for the example). This says only NSObject or one of its subclasses can satisfy the type requirement.
#interface TestObject : NSObject
#end
#interface BaseObject <__covariant TypeConstraint> : NSObject
- (TestObject*)someFuncUnrelatedToTypeConstraint;
#end
We also have these (again, oversimplified) ObjC subclasses, the last which omits the type, so it defaults to NSObject implicitly.
#interface ConcreteObjectA : BaseObject<UIView>
#end
#interface ConcreteObjectB : BaseObject<UIWindow>
#end
#interface ConcreteObjectC : BaseObject
#end
Now we're trying to write a Swift protocol and a paired extension that can only be applied to instances of BaseObject because the extension needs to call the function someFuncUnrelatedToTypeConstraint().
protocol ProtocolForBaseObject : BaseObject {
associatedtype ReturnType:SomeTypeAcceptingTestObject
}
extension ProtocolForBaseObject {
func someTest() -> ReturnType {
let testObject = self.someFuncUnrelatedToTypeConstraint()
return ReturnType(testObject)
}
}
The goal is so we can call this in Swift...
extension ConcreteObjectA : ProtocolForBaseObject {
typealias ReturnType = ReturnTypeA
}
let objA = ConcreteObjectA()
let returnTypeA = objA.someTest()
extension ConcreteObjectB : ProtocolForBaseObject {
typealias ReturnType = ReturnTypeB
}
let objB = ConcreteObjectB()
let returnTypeB = objB.someTest()
extension ConcreteObjectC : ProtocolForBaseObject {
typealias ReturnType = ReturnTypeC
}
let objC = ConcreteObjectC()
let returnTypeC = objC.someTest()
The issue is ProtocolForBaseObject won't compile because it says I have to specify the generic type constraint...
Reference to generic type 'BaseObject' requires arguments in <...>
But the issue is I don't care what the type constraint for the generic is. I want this to apply to all instances of the generic as the function I'm trying to reach doesn't use that type anyway so there's no reason for the protocol to care what that type is.
To get around it, I tried adding NSObject as the type constraint, but this doesn't seem to work either since BaseObject<NSObject> != BaseObject<UIView> even though UIView is a subclass of NSObject.
protocol ProtocolForBaseObject : BaseObject<NSObject> {}
So how can you structure the protocol to apply to all concrete instances of the BaseObject generic, regardless of the type constraint?
I guess the compiler complains because BaseObject<__covariant TypeConstraint> requires a type but :
protocol ProtocolForBaseObject : BaseObject {
associatedtype ReturnType:SomeTypeAcceptingTestObject
}
A swift protocol can't inherit an Objective-C class
Edit (I hate it too)
I would not call that inheritance, and I'm surprised it compiles:
#objc class Foo: NSObject {
let foo: Int
init(foo: Int) {
self.foo = foo
}
}
protocol Bar: Foo {
}
class Baz: Bar { // 'Bar' requires that 'Baz' inherit from 'Foo'
}
does not, the compiler complains about :
'Bar' requires that 'Baz' inherit from 'Foo'
It seems the compiler treats protocol ProtocolForBaseObject : BaseObject like protocol ProtocolForBaseObject where Self: BaseObject
Edit Again
class Baz: Foo, Bar {
}
Compiles which makes me say that class Baz inherits from Foo and conforms to Bar but maybe it's just nitpicking
You will need to create macro:
#interface _BaseObject <__covariant TypeConstraint> : NSObject
#end
#protocol _ProtocolOnlyApplicableToBaseObject
#end
#define BaseObject(Type) _BaseObject<Type><_ProtocolOnlyApplicableToBaseObject>
Usage:
#interface ConcreteObjectA : BaseObject(UIView *)
#end
#interface ConcreteObjectB : BaseObject(UIWindow *)
#end
#interface ConcreteObjectC : BaseObject(id)
#end
I would suggest using composition instead of inheritance:
#protocol ProtocolOnlyApplicableToBaseObject
#interface BaseObject <__covariant TypeConstraint> : NSObject
#property (nonatomic, weak) id<ProtocolOnlyApplicableToBaseObject> delegate
#end
Then your's subclasses will be able to set self as delegate or will be able to reuse some other implementation
Ok, so apparently I wasn't thinking 'Swifty' enough here. As outlined in my question, I have been trying to restrict the protocol ProtocolForBaseObject to only be applicable to BaseObject<T> instances regardless of what T is. The problem is Swift doesn't let you target a non-typed generic, as shown by the compiler message above.
But then I took a step back and asked myself 'What are you actually trying to solve here?' When I asked that question, it clarified the real reason is I needed access to someFuncUnrelatedToTypeConstraint from within an extension to the protocol.
Ok, so if you can't do that by restricting the protocol to an untyped generic, is there another way to solve that specific issue?
When I asked that, I'm almost embarrassed to see how obvious the solution was. It was right in front of me all along.
Forget about BaseObject<T>. Just have the protocol define that method as a requirement!
So now, instead of this (which again doesn't compile...)
protocol ProtocolForBaseObject : BaseObject { // <-- This line won't compile
associatedtype ReturnType:SomeTypeAcceptingTestObject
}
extension ProtocolForBaseObject {
func someTest() -> ReturnType {
let testObject = self.someFuncUnrelatedToTypeConstraint()
return ReturnType(testObject)
}
}
I now have this, which works exactly like I had hoped, and definitely is more 'Swifty'.
protocol ProtocolForBaseObject {
associatedtype ReturnType:SomeTypeAcceptingTestObject
func someFuncUnrelatedToTypeConstraint()
}
extension ProtocolForBaseObject {
func someTest() -> ReturnType {
let testObject = self.someFuncUnrelatedToTypeConstraint()
return ReturnType(testObject)
}
}
Of course there is one slight downside to this approach. While this solves my particular need, that's because I only needed access to that one function. If however the extension needed access to the entire BaseClass object, that may require breaking BaseObject<T> into two and moving all non-generic-related methods into BaseObjectBase, then making BaseObject<T> inherit from that. Then I could restrict the protocol to BaseObjectBase since no type constraints are needed.
If I declare
public class A: NSObject {
public class X { }
public init?(x: X? = nil) { }
}
all is fine. When using it like let a = A(), the initializer is called as expected.
Now, I'd like to have the nested class X private, and the parameterized init as well (has to be, of course). But a simple init?() should stay publicly available as it was before. So I write
public class B: NSObject {
private class X { }
private init?(x: X?) { }
public convenience override init?() { self.init(x: nil) }
}
But this gives an error with the init?() initializer: failable initializer 'init()' cannot override a non-failable initializer with the overridden initializer being the public init() in NSObject.
How comes I can effectively declare an initializer A.init?() without the conflict but not B.init?()?
Bonus question: Why am I not allowed to override a non-failable initializer with a failable one? The opposite is legal: I can override a failable initializer with a non-failable, which requires using a forced super.init()! and thus introduces the risk of a runtime error. To me, letting the subclass have the failable initializer feels more sensible since an extension of functionality introduces more chance of failure. But maybe I am missing something here – explanation greatly appreciated.
This is how I solved the problem for me:
I can declare
public convenience init?(_: Void) { self.init(x: nil) }
and use it like
let b = B(())
or even
let b = B()
— which is logical since its signature is (kind of) different, so no overriding here. Only using a Void parameter and omitting it in the call feels a bit strange… But the end justifies the means, I suppose. :-)
After a bit of fiddling I think I understand. Let's consider a protocol requiring this initializer and a class implementing it:
protocol I {
init()
}
class A : I {
init() {}
}
This gives the error: "Initializer requirement 'init()' can only be satisfied by a required initializer in non-final class 'A'". This makes sense, as you could always declare a subclass of A that doesn't inherit that initializer:
class B : A {
// init() is not inherited
init(n: Int) {}
}
So we need to make our initializer in A required:
class A : I {
required init() {}
}
Now if we look at the NSObject interface we can see that the initializer is not required:
public class NSObject : NSObjectProtocol {
[...]
public init()
[...]
}
We can confirm this by subclassing it, adding a different initializer and trying to use the normal one:
class MyObject : NSObject {
init(n: Int) {}
}
MyObject() // Error: Missing argument for parameter 'n:' in call
Now here comes the weird thing: We can extend NSObject to conform to the I protocol, even though it doesn't require this initializer:
extension NSObject : I {} // No error (!)
I honestly think this is either a bug or a requirement for ObjC interop to work (EDIT: It's a bug and already fixed in the latest version). This error shouldn't be possible:
extension I {
static func get() -> Self { return Self() }
}
MyObject.get()
// Runtime error: use of unimplemented initializer 'init()' for class '__lldb_expr_248.MyObject'
Now to answer your actual question:
In your second code sample, the compiler is right in that you cannot override a non-failable with a failable initializer.
In the first one, you aren't actually overriding the initializer (no override keyword either), but instead declaring a new one by which the other one can't be inherited.
Now that I wrote this much I'm not even sure what the first part of my answer has to do with your question, but it's nice to find a bug anyways.
I suggest you to do this instead:
public convenience override init() { self.init(x: nil)! }
Also have a look at the Initialization section of the Swift reference.
I have read the Swift docs and searched here, but I still am not sure about how to implement a class hierarchy where each subclass sets custom value for an inherited static property; that is:
Base class defines a static property: all instances share the same value.
Subclass overrides the static property: all instances share the same value, which is different form that of the base class.
Can the property be stored?
Also, How should I access the value of the property from within an instance method (regardless of the particular class), and get the correct value everytime? will the following code work?
class BaseClass
{
// To be overridden by subclasses:
static var myStaticProperty = "Hello"
func useTheStaticProperty()
{
// Should yield the value of the overridden property
// when executed on instances of a subclass:
let propertyValue = self.dynamicType.myStaticProperty
// (do something with the value)
}
You are so close to being there, except that you can't override a static property in a subclass — that is what it means to be static. So you'd have to use a class property, and that means it will have to be a computed property — Swift lacks stored class properties.
So:
class ClassA {
class var thing : String {return "A"}
func doYourThing() {
print(type(of:self).thing)
}
}
class ClassB : ClassA {
override class var thing : String {return "B"}
}
And let's test it:
ClassA().doYourThing() // A
ClassB().doYourThing() // B
In Swift 2.0, how can I do the equivalent of #property (nonatomic, strong) NSManagedObject*<SomeProtocol> model?
Basically, I’m trying to define a property on my class that must both be a subclass of NSManagedObject AND conform to SomeProtocol (I will be calling methods defined by both).
I saw this: https://stackoverflow.com/a/25826948/363789, but I'm not sure how I can apply this syntax to property definition...
Swift 4
This is now possible in Swift 4 using the following syntax:
var myObject: NSManagedObject & SomeProtocol
Unfortunately Swift doesn't support such type composition yet.
Three reasonable good solutions as workaround (the third one is probably the best):
1.
You can make another type with has those properties and all types have to inherit from it in order to be used as model.
class SomeManagedObject: NSManagedObject, SomeProtocol {
// conforming to protocol
func someMethod()
}
// type declaration
var model: SomeManagedObject
2.
A more static way to solve this problem is to use generics:
class Aclass<T: NSManagedObject where T: SomeProtocol > {
var model: T
}
Note: You don't have to care about another type which has to be the superclass but this solution is not as dynamic and abstract as the one before.
3.
You could also make your own protocol and make NSManagedObject conform though an extension:
protocol ManagedProtocol {
// if you want to access some methods/properties directly from the type
func method()
var variable: Int { get }
// otherwise call all methods/properties through "managedObject"
// this property also reduces casting if you want to have the object itself
var managedObject: NSManagedObject { get }
}
extension NSManagedObject: ManagedProtocol {
var managedObject: NSManagedObject { return self }
}
Now the type is abstract and can be written as:
var model: protocol<ManagedProtocol, SomeProtocol>
In Objective-C I can do this:
#interface MyManagedObjectSuperClass : NSManagedObject
+(NSString*)entityName;
#end
#implementation
+(NSString*)entityName
{
return NSStringFromClass([self class])
}
#end
With this base class, all my other NSManagedObjects can inherit from MyManagedObjectSuperClass. I can get the entity name by calling +entityName, since there is polymorphism, in subclasses, NSStringFromClass([self class]) returns the class name of subclass.
So my question is, can I do this in Swift?
In a class method of an NSObject subclass, both
toString(self)
NSStringFromClass(self)
return a string containing the class name (including product module name)
of the actual subclass on which the method is called.
See How can I create instances of managed object subclasses in a NSManagedObject Swift extension?
for an example how to extract the Core Data entity name from the full
class name.
Is this straightforward approach what you need?
class Base {
class func typeName() -> String {
return "\(self)"
}
}
class X: Base {}
print(Base.typeName()) // Base
print(X.typeName()) // X
You can use dynamicType to obtain the class name (inclusive of the module name), and string interpolation to convert it to a string:
class Class1 {
var entityName: String {
return "\(self.dynamicType)"
}
}
The most obvious difference is that it's an instance and not a static property/method - that's probably a limitation in your case, as I presume you want to obtain the name from the type and not an instance of it.