1

I am new to both Swing and Objective-C and I was trying to work with AVCaptureDevice.

I tried to implement the following code that gives me back a private member of an AVCaptureDevice instance in Objective-C. But I am no able to transfer the same instruction to Swift:

CMIOObjectID connectionID;
AVCaptureDevice *main_cam = (AVCaptureDevice*)[cameras objectAtIndex:0];
connectionID = [main_cam performSelector:NSSelectorFromString(@"connectionID") withObject:nil];

Mainly because the

main_cam.perform(NSSelectorFromString("connectionID"), with: nil) 

in Swift returns an

Unmanaged<AnyObject>  

and does not accept a forced cast to CMIOObjectID.

Is it possible to perform the operation in Swift?

Ivan_nn2
  • 469
  • 7
  • 20

2 Answers2

1

Using @convention(c)

let selector = NSSelectorFromString("connectionID")
let methodIMP: IMP! = main_cam.method(for: selector)
let result: CMIOObjectID? = unsafeBitCast(methodIMP,to:(@convention(c)(AVCaptureDevice?,Selector)-> CMIOObjectID?).self)(main_cam,selector) 

More details of this approach in my answer here

Kamil.S
  • 5,205
  • 2
  • 22
  • 51
  • I get a compiler error in the last line: *(Any?, Selector) -> CMIOObjectID?' (aka '(Optional, Selector) -> Optional') is not representable in Objective-C, so it cannot be used with '@convention(c)* – Kukiwon Jul 07 '20 at 08:53
  • @Kukiwon corrected, should have been `AnyObject` instead of `Any`. Or the exact symbol in this case `AVCaptureDevice` which I eventually have put in the answer. Thanks for spotting this. – Kamil.S Jul 07 '20 at 09:13
  • Changed the last line to the following and now it works: `let cameraId = unsafeBitCast(methodIMP,to:(@convention(c)(Any?,Selector,Any?)->CMIOObjectID).self)(captureDevice,selector,nil)` – Kukiwon Jul 07 '20 at 09:57
0

You can try:

let unmanaged = main_cam.perform(Selector(("connectionID")), with: nil)
if let connectionID = unmanaged?.takeUnretainedValue() as? CMIOObjectID {
    // do your thing
}

You can read about how to use Unmanaged here.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Thank you. I tried this solution. print(connectionID) returns 0010C1C-93BF-11D8-8B5B-000A95AF9C6A, while inspecting the main_cam object, the value should be (unisgned int) 33. – Ivan_nn2 Mar 31 '20 at 13:51
  • `0010C1C-93BF-11D8-8B5B-000A95AF9C6A` is not how a `UInt32` should be printed. Are you sure you are printing the right thing? @Ivan_nn2 – Sweeper Mar 31 '20 at 13:56
  • No I am not sure. I would like to get that 33 value (that is the right one). And I don't how to get it. The perform selector in the Objective-c code returns 33. And they are CMIOObjectID objects both in Swift and in Objective-c – Ivan_nn2 Mar 31 '20 at 14:34
  • @Ivan_nn2 I'm guessing you put the print statement inside the `if let`? Put a break point there. Does it get run? What is the type and value of `connectionID` as viewed from the debugger? Also try doing `po connectionID` in the debugger. – Sweeper Mar 31 '20 at 14:39
  • You are right. There is some error in that if statement. If I put print(main_cam.manufacturer) after the if (and a breakpoint after this print) I cannot see the manufacturer printed inside the console. As soon as i take away the if statement the manufacturer string is printed in the console log. In both cases (since the program stops for the breakpoint i guess) the variable inspector shows me the main_cam as an AVCaptureDevices with all the private and public elements (among which there is also _connectionID: (unsigned int) 33 – Ivan_nn2 Mar 31 '20 at 17:49