Objective-C class property type not exposed to Swift - objective-c

I got a Swift Project with an Objective-C library linked via CocoaPods. It works fine, I can call all methods etc. Also my Bridging-Header is existing and working.
But I got a problem with some properties of my Objective-C classes.
Here is my class:
#import <Foundation/Foundation.h>
#import "OAIObject.h"
#import "OAILayerTreeGroupAllOf.h"
#import "OAILayerTreeItem.h"
#protocol OAILayerTreeGroupAllOf;
#class OAILayerTreeGroupAllOf;
#protocol OAILayerTreeItem;
#class OAILayerTreeItem;
#protocol OAILayerTreeGroup
#end
#interface OAILayerTreeGroup : OAILayerTreeItem
#property(nonatomic) NSArray<OAILayerTreeItem>* children;
#end
I can create this object from Swift code.
But if I try to access the children I get the type "Any", so I can't access the property children.
Is there a way to access the property type of my Objective-C class?

I suspect the problem is that your Objective-C generics declaration does not contain a *.
Can you try to change your property declaration to
#property(nonatomic) NSArray<OAILayerTreeItem *> *children;
Your NSArray instance contains objects that are of type OAILayerTreeItem, that's why you need the *.
Please note that I was unable to try this.

Related

Type arguments cannot be applied to non-class type 'id'

I am new to ObjectiveC and have been working for few years in Swift. Therefore, I don't understand the below explained error in Xcode:
Type arguments cannot be applied to non-class type 'id'
My Protocol:
#protocol ExampleProtocol<NSObject>
#required
-(NSString *)title;
-(NSString *)album;
#end
My implementation in the MyService.h file:
#interface MyService : NSObject
#property (nonatomic, assign, readwrite) id<ExampleProtocol> delegate;
#end
The error occurs in the line:
> #property (nonatomic, assign, readwrite) id<ExampleProtocol> delegate;
Additionally:
I have imported the required file .h in which the ExampleProtocol code is located in the MyService.m file
Added the #class ExampleProtocol; in my MyService.h file at the top.
Also tried:
Creating a Swift protocol with #objc and : class imported over the app-Bridging.h gives me the same result with the same error message.
Clean build
Clean build folder (removed derived data)
The only thing that did work was to remove the line from the public interface to the private. This doesn't make sense. I wan't to set the delegate from another class and creating a public setter which set the private delegate is ugly workaround.
Any suggestion would be helpfull. I would like to understand why this happens. There are a lot of other protocols in my project written in ObjectiveC which work fine.
ExampleProtocol is a protocol, not a class. You don't need it if you import the header. If you don't import the header, it should be #protocol ExampleProtocol;

getting Swift Object from Objective C class using another Swift File

I have been trying with no success the following structure:
ClassA.swift
class ClassA:NSObject{
var varA = ""
}
then I have a ClassB.h and ClassB.m (Objective c) and I am not able to define "Project-Swift.h" in the .h file so i import it into the .m
#interface ClassB()
#property ClassA *myClassA;
#end
and
#synthesize myClassA = theAClass;
The problem comes when I try
class ClassC:NSObject{
let theClassAFromC = ClassB.myAClass
}
I get an error message Value of type 'ClassB' has no member 'theClassA'
Add your objc header to bridging header
In your objc header use #class notation for your swift class, e.g. #class ClassA;, instead of import "Project-Swift.h" file. You can import that one in your objc implementation file.
Don't forget that you cannot access non-static property of objc class the way you're declaring. Instead, initialize your ClassB object in your swift class and access it's property when needed. For instantiating the ClassA object you can use dependency injection in ClassB
As part of the convenience, use #objc declaration for your Swift classes accesible to objc runtime
try to search with keyword "Bridging-Header" and then add
#import "ClassB.h"
That does not work in my case.
adding ClassB.h in my Bridge File makes properties from .h Visible But not the the properties in my .m file. If i add Class.m In my header file then Module-Swift.h is not found

Swift cannot pass class conforming to protocol as function parameter to a function residing in Objective-C file

Hi I'm new to Swift but experienced with Objective-C.
I have a project that uses both Swift and Objective-C files (bridging and all).
Say I have a protocol called "fooProtocol" and a class "foo" that implements it. I am trying to pass an object of type "fooProtocol" from the Swift file as a parameter to the function inside the Objective-C file.
here is the Objective-C function inside class "tester":
-(void)setWithFoo:(id<fooProtocol>*)_foo{
}
here is the Swift code:
var myObject:fooProtocol = foo.init()
var objcObject:tester = tester.init()
objcObject.setWithFoo(_foo: myObject)
It first says "Cannot convert value of type "fooProtocol" to expected argument type "AutoreleasingUnsafeMutablePointer (obviously because it needs to be passed by reference, so...)
I then tried casting the parameter to this:
tester.setWithFoo(_foo: AutoreleasingUnsafeMutablePointer<fooProtocol>(myObject))
Now the error reads: "Cannot invoke initializer for type 'AutoreleasingUnsafeMutablePointer with an argument list of type '(fooProtocol)'
I have tried many more permutations and variations but I simply cannot stop the compiler error. For such a simple procedure as passing a polymorphic variable to a function in Objective-C file that expects that protocol id, Swift has made it a nightmare.
...Any help would be appreciated, thanks!
=== EDIT ===
Here are the declarations for the classes, now starting properly with caps
In the "FooProtocol.h" file:
#protocol FooProtocol
#end
In the "Foo.h" file:
#import <Foundation/Foundation.h>
#import "FooProtocol.h"
#interface Foo : NSObject <FooProtocol>
#end
In the "Foo.m":
#import "Foo.h"
#implementation Foo
#end
The "FooProtocol.h" file:
#import <Foundation/Foundation.h>
#protocol FooProtocol
#end
The "Tester.h" file:
#import <Foundation/Foundation.h>
#import "FooProtocol.h"
#interface Tester : NSObject
-(void)setWithFoo:(id<FooProtocol>*)_foo;
#end
The "Tester.m" file:
#import "Tester.h"
#implementation Tester
-(void)setWithFoo:(id<FooProtocol>*)_foo{
//do something with _foo
}
#end
And again the Swift code that can't compile:
var myObject:FooProtocol = Foo.init()
var objcObject:Tester = Tester.init()
objcObject.setWithFoo(AutoreleasingUnsafeMutablePointer<FooProtocol>(myObject))
You probably don't mean to say this:
-(void)setWithFoo:(id<FooProtocol>*)_foo;
It is very unusual to see an id* in Objective-C. In fact, it's so unusual that in all my years of programming Cocoa, I have never seen one.
You probably mean this:
-(void)setWithFoo:(id<FooProtocol>)_foo;
And then you will be able to say, on the Swift side:
objcObject.setWithFoo(myObject)

Swift class using Objective-C class using Swift class

I have an obj-c project to which I successfully added a new Swift class A, which is being used by some existing obj-c class B - the use of the automatically generated "MyProject-Swift.h" header worked as expected.
I also successfully added a new Swift class C that uses some existing obj-c class D - the use of the bridging header also worked as expected.
However, suppose I want to refer from my Swift class C to the existing obj-c class B (which in turn refers to the new Swift class A). In order to do that I need to import "B.h" to the bridging header. However, if I do that I get an error in class B: "'MyProject-Swift.h' file not found" (i.e., the file is no longer generated).
Am I doing something wrong or is this a kind of interaction between Swift and Objective-C that is not allowed? It looks like there is a kind of circular reference that the compiler is unable to solve.
--- EDIT ---
I'll try to make the question clearer by adding some code.
-- PREAMBLE --
I added a new Swift class to an obj-c project:
// SwiftClassA.swift
import Foundation
#objc class SwiftClassA : NSObject {
var myProperty = 0
}
The code compiles correctly and is translated into obj-c stubs in the automatically generated "MyProject-Swift.h" header like so:
// MyProject-Swift.h
...
SWIFT_CLASS("_TtC7MyProject11SwiftClassA")
#interface SwiftClassA : NSObject
#property (nonatomic) NSInteger myProperty;
- (instancetype)init OBJC_DESIGNATED_INITIALIZER;
#end
Now, one obj-c class uses SwiftClassA:
// ObjCClass.h
#import <Foundation/Foundation.h>
#import <MyProject-Swift.h>
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
This also works seamlessly.
-- THE QUESTION --
Can I now create a new Swift class that refers to the obj-c class (ObjCClass) that is using the Swift class SwiftClassA?
This is what I can't do.
If I add the new Swift class:
// SwiftClassB.swift
import Foundation
#objc class SwiftClassB : NSObject {
var aPropertyOfClassB = 1
func someFunc() {
var objCObject = ObjCClass()
var theProperty = objCObject.aProperty
print("The property is \(theProperty)")
}
}
this of course won't compile because of "Use of unresolved identifier 'ObjCClass'". So I need to add that to the bridging header file:
// BridgingHeader.h
#ifndef MyProject_BridgingHeader_h
#define MyProject_BridgingHeader_h
...
#import "ObjCClass.h"
#endif
However, if I do that, the ObjCClass.h file won't compile giving a "'MyProject-Swift.h' file not found".
I've read in several places (with no example, though) that this may mean that there is a circular reference and that a forward reference using #class could solve the problem. However, I'm not sure what needs to be forward referenced and where, and all my attempts failed.
I hope the question is no longer confusing now!
This is a typical cyclical referencing problem.
Be careful to read the docs:
To avoid cyclical references, don’t import Swift into an Objective-C header file. Instead, you can forward declare a Swift class to use it in an Objective-C header. Note that you cannot subclass a Swift class in Objective-C.
So, you should use "forward declare" in .h, and #import in .m:
// ObjCClass.h
#import <Foundation/Foundation.h>
#class SwiftClassA;
#interface ObjCClass : NSObject
#property (nonatomic, strong) SwiftClassA *aProperty;
#property (nonatomic) int *aNumber;
#end
// ObjCClass.m
#import "ObjCClass.h"
#import "MyProject-Swift.h"
#implementation ObjCClass
// your code
#end

Varieties of #interface declarations, some with parentheses

I've noticed a variety of #interface declarations for Objective-c classes. I'd like to understand why developers declare #interface in the following ways:
// in the .h file
#interface MyClass : NSObject
// ...
#end
// in the .m file (what's the purpose of the parens?)
#interface MyClass ()
// more property declarations which seem like they can go in the .h file
#end
// again in the .m file (what's the purpose of private?)
#interface MyClass (Private)
// some method declarations
#end
This is just a normal class interface, inheriting from NSObject, where you declare ivars, properties and methods
// in the .h file
#interface MyClass : NSObject
// ...
#end
The following two are categories, which allow you to add methods to a class. It is not a subclass however (do not declare a method with the same name, as you won't be able to access the original one). If you have a named category of the interface (like #interface MyClass (Private)), then the implementation should be provided in #implementation MyClass (Private), in the case of unnamed categories (also called extensions), the implementation can be provided as usual. Note that extensions also allow you to add ivars to the class while (named) categories do not.
// in the .m file (what's the purpose of the parens?)
#interface MyClass ()
// more property declarations which seem like they can go in the .h file
#end
// again in the .m file (what's the purpose of private?)
#interface MyClass (Private)
// some method declarations
#end
It is used to declared private methods.
This response explain this in details: What are best practices that you use when writing Objective-C and Cocoa?
What ever goes in the .m file is private. the parens are for categories so you can segment your code into categories to make it more readable. because the code is in .m and private, they called the category Private.