I am trying to make a swift 3 struct conform to _ObjectiveCBridgeable but I am not sure what else I need to satisfy the protocol. Below is my struct and the _ObjectiveCBridgeable conformance. I am missing something but I am not sure what it is.
struct Box {
let contents: Any
}
extension Box: _ObjectiveCBridgeable {
typealias _ObjectiveCType = thing;
init(fromObjectiveC source: _ObjectiveCType) {
contents = source.contents
}
static func _isBridgedToObjectiveC() -> Bool {
return true
}
static func _getObjectiveCType() -> Any.Type {
return _ObjectiveCType.self
}
func _bridgeToObjectiveC() -> Box._ObjectiveCType {
return thing(contents: self.contents)
}
static func _forceBridgeFromObjectiveC(_ source: Box._ObjectiveCType, result: inout Box?) {
result = Box(contents: source.contents)
}
static func _conditionallyBridgeFromObjectiveC(_ source: Box._ObjectiveCType, result: inout Box?) -> Bool {
_forceBridgeFromObjectiveC(source, result: &result)
return true
}
}
// Objc
#interface thing : NSObject
#property (readonly) id contents;
-(instancetype)initWithContents:(id)contents;
#end
#implementation thing
- (instancetype)initWithContents:(id)contents {
if ((self = [super init])) {
_contents = contents;
}
return self;
}
#end
As the underscore tells you, _ObjectiveCBridgeable is private. Its purpose is "to accommodate the specific needs of bridging Objective-C object types to Swift value types". You cannot adopt it for your own types; it works by means of "compiler magic under the hood".
There is a proposal on the table to provide a public version, but it has not yet been implemented.
Related
How can I call + (BOOL)resolveInstanceMethod:(SEL)aSEL while changing dynamic variable variable name?
E.g.
I've got an ObjC class:
#interface SomeClass: NSObject
///
#end
#implementation SomeClass: NSObject
+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
//// code
}
#end
Swift
#objcMembers
public final class SomeSwiftClass: SomeClass {
public dynamic var value: NSNumber?
public dynamic func mmFunc() {
print("mmFunc")
}
}
code:
let anInstance = SomeSwiftClass()
anInstance.value = NSNumber(10) /// how can I call resolveInstanceMethod? Doesn't work
anInstance.mmFunc() //doesn't work too.
If I implement the same code in ObjC it works.
Any ideas?
All the following works w/o changes in your classes (tested with Xcode 11.2 / iOS 13.2).
func test() {
let anInstance = SomeSwiftClass()
let hasMethod = SomeSwiftClass.resolveInstanceMethod(#selector(SomeSwiftClass.mmFunc))
print("Result: \(hasMethod)")
anInstance.value = NSNumber(10)
anInstance.mmFunc()
}
if not at your side, see if objc-swift bridging is correctly configured and all needed includes are added.
Update: compiler was confused by ambiguous name of value, changing it to other more specific name works as below
#interface SomeClass: NSObject
#property (nonatomic) BOOL isValid;
#end
#objc
public class SomeSwiftClass: SomeClass {
#objc public var specificValue: NSNumber = NSNumber(5)
#objc public func mmFunc() {
print("mmFunc")
}
}
#objc
final class Runner: NSObject {
#objc public static func test() {
let anInstance = SomeSwiftClass()
let hasProperty = SomeSwiftClass.resolveInstanceMethod(#selector(getter: SomeSwiftClass.isValid))
print("Has property: \(hasProperty)")
let swiftProperty = SomeSwiftClass.resolveInstanceMethod(#selector(getter: SomeSwiftClass.specificValue))
print("Swift property: \(swiftProperty)")
let hasMethod = SomeSwiftClass.resolveInstanceMethod(#selector(SomeSwiftClass.mmFunc))
print("Has method: \(hasMethod)")
anInstance.specificValue = NSNumber(10)
anInstance.mmFunc()
}
}
I'm trying to observe a property (which is declared within an Objective-C) in Swift.
Objective-C protocol:
#protocol DemoViewModel <NSObject>
#property (nonatomic, strong) NSString *bla;
#end
Swift observe:
#objc public dynamic var vm: (NSObject & DemoViewModel) {
didSet {
vm.observe(#keyPath(DemoViewModel.bla)) { _,_ in
//do something
}
}
}
Interestingly I receive an error:
Member ‘observe’ cannot be used on value of protocol type ‘NSObject & DemoViewModel’; use a generic constraint instead
Any idea what's going on?
Thanks
There must be something special under the hood about the observe function, since all the functions and methods can be accessed your way. It's asking you to instead create something like this:
class Foo {
#objc public dynamic var vm: (NSObject & DemoViewModel)?
private var observer: NSKeyValueObservation?
func setVM<T>(_ vm: T) where T: NSObject &: DemoViewModel {
self.vm = vm
observer = vm?.observe(\.bla, options: [.old, .new]) { _, _ in
// do something
}
}
}
When you implement DemoViewModel, remember that you need to mark bla as dynamic, otherwise the observer won't be called.
class Bar: NSObject, DemoViewModel {
#objc dynamic var bla: String = ""
}
We are trying to reference Swift methods inside an Objective-C implementation.
Swift class:
import Foundation
#objc class MySwiftClass: NSObject {
override init() {
super.init()
}
func sayHello() -> Void {
print("hello");
}
func addX(x:Int, andY y:Int) -> Int {
return x+y
}
}
Objective-C implementation (Objective-c.m):
#import "ProductModuleName-Swift.h"
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello] //works
[getData addX:5 addY:5] //No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
The last line of code gives the following error:
No visible #interface for 'MySwiftClass' declares selector 'addX:addY'
If you command-click on "ProductModuleName-Swift.h" in the Xcode
source file editor then you can see how the Swift methods are mapped to Objective-C.
In your case that would be
#interface MySwiftClass : NSObject
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
- (void)sayHello;
- (NSInteger)addXWithX:(NSInteger)x andY:(NSInteger)y;
#end
which is called as
MySwiftClass* getData = [[MySwiftClass alloc]init];
[getData sayHello];
NSInteger result = [getData addXWithX:5 andY:5];
A better Swift 3 method name might be
func add(x: Int, y:Int) -> Int
because x is already the argument (external) name of the first
parameter. You can also add an #objc() attribute to the Swift definition
to control the Objective-C name. For example, with
#objc(addX:andY:)
func add(x: Int, y: Int) -> Int {
return x+y
}
it would be called from Objective-C as
NSInteger result = [getData addX:5 andY:5];
As #ekscrypto pointed out, in Swift 4 and later you need to annotate individual functions with #objc. Prior to that, a single, class-level #objc was enough.
Of course in Objective-C class you must add import of NAME_PROJECT-swift.h.
If your project name is Sample then you must add:
#import Sample-swift.h
And then:
Swift 4 or Less
#objc class MySwiftClass: NSObject {
func sayHello(){
//function
}
func addX(){
//function
}
}
Swift 4 or Greater
#objc class MySwiftClass: NSObject {
#objc func sayHello(){
//function
}
#objc func addX(){
//function
}
}
In my case I had forgotten to add:
#import "MyProject-Swift.h"
Into the obj c file.
Assume that I want to skip one level in call super.viewDidLoad(), for example. So I wish to be able to do something like this:
override func viewDidLoad() {
super.super.viewDidLoad()
}
or
-(void)viewDidLoad {
[[super super] viewDidLoad];
}
This code will not compile. Is this possible by some other solution?
For expository purposes only, the following code does as requested. It only fails to crash when compiled without ARC, which means it has a bug in it. It does no error checking.
Never use this code unless you are playing around and trying to learn.
#import Foundation;
#import <objc/runtime.h>
#import <objc/message.h>
#interface M : NSObject
- (void)print;
#end
#implementation M
- (void)print { NSLog(#"M"); }
#end
#interface N : M
#end
#implementation N
- (void)print { NSLog(#"N"); }
#end
#interface O : N
#end
#implementation O
- (void)print {
Class mysupersuper = [[self superclass] superclass];
Method supersuperMethod = class_getInstanceMethod(mysupersuper, _cmd);
method_invoke(self, supersuperMethod);
}
#end
int main(int argc, char **argv)
{
O *o = O.new;
[o print];
return 0;
}
I compiled it using the command: clang -framework Foundation -fno-objc-arc -fmodules test.m.
Here is what you could do in Swift. Suppose you have classes A and B:
class A
{
func fun() { print("called fun() in A") }
}
class B : A
{
override func fun() { print("called fun() in B") }
}
You can't modify their definitions, but want to call fun() from A in a class C derived from B. You could do that as follows:
extension B
{
func funInA()
{
super.fun()
}
}
class C : B
{
override func fun() { funInA() }
}
I think you can pull off something like this in Objective-C using categories.
Just trying to get started with Swift and hit the following issue when upgrading to Swift 1.2:
#protocol MyObjcProtocol <NSObject>
#optional
#property (copy) NSString *optionalString;
- (void) optionalMethod;
#end
...
class MySwiftClass: NSObject {}
extension MySwiftClass: MyObjcProtocol {
var optionalString: NSString {
get { return "Foo" }
set(newValue) { NSLog("Whatever") }
}
// No problem here
func optionalMethod() {
NSLog("Bar")
}
}
The Swift extension implementing the Objc protocol doesn't compile with:
Objective-C method 'optionalString' provided by getter for
'optionalString' conflicts with optional requirement getter for
'optionalString' in protocol 'MyObjcProtocol'...
Objective-C method 'setOptionalString:' provided by setter for
'optionalString' conflicts with optional requirement setter for
'optionalString' in protocol 'MyObjcProtocol'...
So clearly the compiler doesn't realise I'm trying to implement the optionals from the protocol, and thinks I'm stomping on the protocol's expected ObjC symbols. The optional method func optionalMethod() compiles just fine, however. Remove #optional from the protocol and everything compiles just fine, but it's not always possible or desirable to do that as a solution.
So, how does one implement this? Trying to implement the expected ObjC methods explicitly doesn't work either:
func optionalString() {
return "foo"
}
func setOptionalString(newValue: NSString) {
NSLog("")
}
Hope someone can help! Thanks in advance!
MyObjcProtocol protocol is translated to Swift as:
protocol MyObjcProtocol : NSObjectProtocol {
optional var optionalString: String! { get set }
optional func optionalMethod()
}
So you can just use String instead of NSString.
extension MySwiftClass: MyObjcProtocol {
var optionalString: String {
get { return "Foo" }
set(newValue) { NSLog("Whatever") }
}
func optionalMethod() {
NSLog("Bar")
}
}
I guess the problem is that the protocol requires a stored property because it's specified copy, but the swift extension cannot have storage for a property
Optional properties in Objective C is equivalent to Optional Type in Swift. So it would translate like this:
var optionalString: String! // = false
in case it can be nil:
var optionalString: String? // = nil