Open BinBear opened 5 years ago
Will need to figure this out. Looks like a similar issue to #13
you can add an adapter to support objective-c, like this.
//
// SignalRClient.h
// Followme
//
// Created by Subo on 2018/12/25.
// Copyright © 2018年 com.followme. All rights reserved.
//
import Foundation
import SwiftSignalRClient
@objc public protocol SignalRClientDelegate : class {
func clientDidConnectSuccess(client: SignalRClient)
func clientDidConnectFailed(client: SignalRClient, error: Error)
func clientDidClosed(client: SignalRClient, error: Error?)
}
/// 因为SwiftSignalRClient没有做OC的支持,所以这里多包装了一层来支持OC
@objcMembers public class SignalRClient : NSObject {
var url: String
var headers: [String: String]?
private var connection: HubConnection?
public weak var delegate: SignalRClientDelegate?
public init(url: String, headers: [String: String]?) {
self.url = url
self.headers = headers
super.init()
}
public func start() {
self.connection = HubConnectionBuilder(url: URL(string: self.url)!)
.withLogging(minLogLevel: .debug)
.withHttpConnectionOptions(configureHttpOptions: { (httpConnectionOptions) in
if let header = self.headers {
for (key, value) in header {
httpConnectionOptions.headers[key] = value
}
}
})
.build()
self.connection?.delegate = self
self.connection?.start()
}
public func stop() {
self.connection?.stop()
}
public func on(method: String, callback: @escaping (_ jsonString: String?) -> Void) {
self.connection?.on(method: method, callback: {args, typeConverter in
if let json = args.first as? String {
callback(json)
return
}
callback(nil)
})
}
public func send(method: String, arguments:[AnyObject], sendDidComplete: @escaping (_ error: Error?) -> Void) {
self.connection?.send(method: method, arguments: arguments, sendDidComplete: sendDidComplete)
}
public func invoke(method: String, arguments: [AnyObject], invocationDidComplete: @escaping (_ error: Error?) -> Void) {
self.connection?.invoke(method: method, arguments: arguments, invocationDidComplete: invocationDidComplete)
}
}
extension SignalRClient: HubConnectionDelegate {
public func connectionDidOpen(hubConnection: HubConnection!) {
self.delegate?.clientDidConnectSuccess(client: self)
}
public func connectionDidFailToOpen(error: Error) {
self.delegate?.clientDidConnectFailed(client: self, error: error)
}
public func connectionDidClose(error: Error?) {
self.delegate?.clientDidClosed(client: self, error: error)
}
}
You can use it like this.
#import "YourTarget-Swift.h"
NSString *url = @"https://xxxxxxxxxxx";
NSDictionary *headers = @{@"token" : @"xxxxxxx"};
self.client = [[SignalRClient alloc] initWithUrl:url headers:headers];
self.client.delegate = self;
__weak __typeof(self)weakSelf = self;
[self.client onMethod:@"YourMethod" callback:^(NSString * _Nullable jsonString) {
NSLog(@"arguments: %@", jsonString);
}];
[self.client start];
@lexiaoyao20 - this is cool!
@lexiaoyao20 - do you mind if I use your code as part of this project?
@lexiaoyao20 - do you mind if I use your code as part of this project?
NO problem.
Thanks!
On Wed, May 29, 2019 at 7:07 AM lexiaoyao20 notifications@github.com wrote:
@lexiaoyao20 https://github.com/lexiaoyao20 - do you mind if I use your code as part of this project?
NO problem.
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/moozzyk/SignalR-Client-Swift/issues/37?email_source=notifications&email_token=AAK7JJDWHIXOOHYKXQJQ4YDPX2E2NA5CNFSM4GHXUP22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODWPOGTA#issuecomment-496952140, or mute the thread https://github.com/notifications/unsubscribe-auth/AAK7JJCHWHPWF3UUU5USZGDPX2E2NANCNFSM4GHXUP2Q .
I am trying to use SignalR-Client-Swift library on an objective C project when calling "start()". I'm getting next error.
2019-08-05T14:02:42.027Z debug: Negotiate response: {"connectionId":"9zjIAJuJmXkN9xLaGrwm3w","availableTransports":[{"transport":"WebSockets","transferFormats":["Text","Binary"]},{"transport":"ServerSentEvents","transferFormats":["Text"]},{"transport":"LongPolling","transferFormats":["Text","Binary"]}]}
2019-08-05T14:02:42.031Z debug: Negotation response received
2019-08-05T14:02:42.033Z info: Starting WebSocket transport
atos[69262]: atos cannot examine process 69257 (ProjectName) for unknown reasons, even though it appears to exist; try running with `sudo`.
==69257==WARNING: Can't read from symbolizer at fd 31
atos[69263]: atos cannot examine process 69257 (ProjectName) for unknown reasons, even though it appears to exist; try running with `sudo`.
==69257==WARNING: Can't read from symbolizer at fd 31
atos[69264]: atos cannot examine process 69257 (ProjectName) for unknown reasons, even though it appears to exist; try running with `sudo`.
==69257==WARNING: Can't read from symbolizer at fd 31
atos[69265]: atos cannot examine process 69257 (ProjectName) for unknown reasons, even though it appears to exist; try running with `sudo`.
==69257==WARNING: Can't read from symbolizer at fd 31
==69257==WARNING: Failed to use and restart external symbolizer!
==================
WARNING: ThreadSanitizer: Swift access race (pid=69257)
Modifying access of Swift variable at 0x7b280002a280 by thread T10:
#0 $s18SwiftSignalRClient7Manager33_7B38D54E3FC021D04F957FB8C00AFD21LLCADycfcyycfU_ <null> (SwiftSignalRClient:x86_64+0xd8692)
#1 $s18SwiftSignalRClient7Manager33_7B38D54E3FC021D04F957FB8C00AFD21LLCADycfcyycfU_TA <null> (SwiftSignalRClient:x86_64+0xe474d)
#2 $sIeg_IeyB_TR <null> (SwiftSignalRClient:x86_64+0x72460)
#3 __tsan::invoke_and_release_block(void*) <null> (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x676ab)
#4 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x3db4)
Previous modifying access of Swift variable at 0x7b280002a280 by thread T11:
#0 $s18SwiftSignalRClient7Manager33_7B38D54E3FC021D04F957FB8C00AFD21LLC6nextIdSiyF <null> (SwiftSignalRClient:x86_64+0xda53b)
#1 $s18SwiftSignalRClient9WebSocketC7request12subProtocolsAC10Foundation10URLRequestV_SaySSGtcfc <null> (SwiftSignalRClient:x86_64+0xdc2a8)
#2 $s18SwiftSignalRClient9WebSocketC7request12subProtocolsAC10Foundation10URLRequestV_SaySSGtcfC <null> (SwiftSignalRClient:x86_64+0xdbf92)
#3 $s18SwiftSignalRClient19WebsocketsTransportC5start3url7optionsy10Foundation3URLV_AA21HttpConnectionOptionsCtF <null> (SwiftSignalRClient:x86_64+0xe9f72)
#4 $s18SwiftSignalRClient19WebsocketsTransportCAA0E0A2aDP5start3url7optionsy10Foundation3URLV_AA21HttpConnectionOptionsCtFTW <null> (SwiftSignalRClient:x86_64+0xeeaa8)
#5 $s18SwiftSignalRClient14HttpConnectionC14startTransport33_1386C1C9AB3A1F54B5B7C070283F4473LL19negotiationResponseyAA011NegotiationR0C_tF <null> (SwiftSignalRClient:x86_64+0xffc7)
#6 $s18SwiftSignalRClient14HttpConnectionC5startyyFyAA19NegotiationResponseCcfU_ <null> (SwiftSignalRClient:x86_64+0xbddf)
#7 $s18SwiftSignalRClient14HttpConnectionC5startyyFyAA19NegotiationResponseCcfU_TA <null> (SwiftSignalRClient:x86_64+0x15e78)
#8 $s18SwiftSignalRClient14HttpConnectionC9negotiate33_1386C1C9AB3A1F54B5B7C070283F4473LL11accessToken0F11DidCompleteySSSg_yAA19NegotiationResponseCctFyAA0dU0CSg_s5Error_pSgtcfU0_ <null> (SwiftSignalRClient:x86_64+0xd558)
#9 $s18SwiftSignalRClient14HttpConnectionC9negotiate33_1386C1C9AB3A1F54B5B7C070283F4473LL11accessToken0F11DidCompleteySSSg_yAA19NegotiationResponseCctFyAA0dU0CSg_s5Error_pSgtcfU0_TA <null> (SwiftSignalRClient:x86_64+0x18dbc)
#10 $s18SwiftSignalRClient17DefaultHttpClientC04sendE7Request3url6method17completionHandlery10Foundation3URLV_SSyAA0E8ResponseCSg_s5Error_pSgtctFyAH4DataVSg_So13NSURLResponseCSgAOtcfU_ <null> (SwiftSignalRClient:x86_64+0x3c47)
#11 $s18SwiftSignalRClient17DefaultHttpClientC04sendE7Request3url6method17completionHandlery10Foundation3URLV_SSyAA0E8ResponseCSg_s5Error_pSgtctFyAH4DataVSg_So13NSURLResponseCSgAOtcfU_TA <null> (SwiftSignalRClient:x86_64+0x47e8)
#12 $s10Foundation4DataVSgSo13NSURLResponseCSgs5Error_pSgIegggg_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR <null> (SwiftSignalRClient:x86_64+0x3e18)
#13 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke <null> (CFNetwork:x86_64+0xc177)
#14 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x3db4)
Location is heap block of size 160 at 0x7b280002a260 allocated by thread T11:
#0 malloc <null> (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x4a4fa)
#1 swift_slowAlloc <null> (libswiftCore.dylib:x86_64+0x2ce3c8)
#2 globalinit_33_7B38D54E3FC021D04F957FB8C00AFD21_func5 <null> (SwiftSignalRClient:x86_64+0xdab37)
#3 dispatch_once <null> (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x682d4)
#4 dispatch_once_f <null> (libclang_rt.tsan_iossim_dynamic.dylib:x86_64+0x683c0)
#5 swift_once <null> (libswiftCore.dylib:x86_64+0x2f0578)
#6 $s18SwiftSignalRClient9WebSocketC7request12subProtocolsAC10Foundation10URLRequestV_SaySSGtcfc <null> (SwiftSignalRClient:x86_64+0xdc267)
#7 $s18SwiftSignalRClient9WebSocketC7request12subProtocolsAC10Foundation10URLRequestV_SaySSGtcfC <null> (SwiftSignalRClient:x86_64+0xdbf92)
#8 $s18SwiftSignalRClient19WebsocketsTransportC5start3url7optionsy10Foundation3URLV_AA21HttpConnectionOptionsCtF <null> (SwiftSignalRClient:x86_64+0xe9f72)
#9 $s18SwiftSignalRClient19WebsocketsTransportCAA0E0A2aDP5start3url7optionsy10Foundation3URLV_AA21HttpConnectionOptionsCtFTW <null> (SwiftSignalRClient:x86_64+0xeeaa8)
#10 $s18SwiftSignalRClient14HttpConnectionC14startTransport33_1386C1C9AB3A1F54B5B7C070283F4473LL19negotiationResponseyAA011NegotiationR0C_tF <null> (SwiftSignalRClient:x86_64+0xffc7)
#11 $s18SwiftSignalRClient14HttpConnectionC5startyyFyAA19NegotiationResponseCcfU_ <null> (SwiftSignalRClient:x86_64+0xbddf)
#12 $s18SwiftSignalRClient14HttpConnectionC5startyyFyAA19NegotiationResponseCcfU_TA <null> (SwiftSignalRClient:x86_64+0x15e78)
#13 $s18SwiftSignalRClient14HttpConnectionC9negotiate33_1386C1C9AB3A1F54B5B7C070283F4473LL11accessToken0F11DidCompleteySSSg_yAA19NegotiationResponseCctFyAA0dU0CSg_s5Error_pSgtcfU0_ <null> (SwiftSignalRClient:x86_64+0xd558)
#14 $s18SwiftSignalRClient14HttpConnectionC9negotiate33_1386C1C9AB3A1F54B5B7C070283F4473LL11accessToken0F11DidCompleteySSSg_yAA19NegotiationResponseCctFyAA0dU0CSg_s5Error_pSgtcfU0_TA <null> (SwiftSignalRClient:x86_64+0x18dbc)
#15 $s18SwiftSignalRClient17DefaultHttpClientC04sendE7Request3url6method17completionHandlery10Foundation3URLV_SSyAA0E8ResponseCSg_s5Error_pSgtctFyAH4DataVSg_So13NSURLResponseCSgAOtcfU_ <null> (SwiftSignalRClient:x86_64+0x3c47)
#16 $s18SwiftSignalRClient17DefaultHttpClientC04sendE7Request3url6method17completionHandlery10Foundation3URLV_SSyAA0E8ResponseCSg_s5Error_pSgtctFyAH4DataVSg_So13NSURLResponseCSgAOtcfU_TA <null> (SwiftSignalRClient:x86_64+0x47e8)
#17 $s10Foundation4DataVSgSo13NSURLResponseCSgs5Error_pSgIegggg_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR <null> (SwiftSignalRClient:x86_64+0x3e18)
#18 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke <null> (CFNetwork:x86_64+0xc177)
#19 _dispatch_client_callout <null> (libdispatch.dylib:x86_64+0x3db4)
Thread T10 (tid=8726874, running) is a GCD worker thread
Thread T11 (tid=8726875, running) is a GCD worker thread
SUMMARY: ThreadSanitizer: Swift access race (/Users/jordi/Library/Developer/CoreSimulator/Devices/B4868C29-CDA6-4EDE-BF04-03708A1A4B93/data/Containers/Bundle/Application/82AD19DA-C3E3-424F-80FF-8507A25D3134/ProjectName.app/Frameworks/SwiftSignalRClient.framework/SwiftSignalRClient:x86_64+0xd8692) in $s18SwiftSignalRClient7Manager33_7B38D54E3FC021D04F957FB8C00AFD21LLCADycfcyycfU_
==================
atos[69266]: atos cannot examine process 69257 (ProjectName) for unknown reasons, even though it appears to exist; try running with `sudo`.
@lexiaoyao20 or @moozzyk did you find a similar issue? Any tip would be appreciated!
Seems like a data race in the WebSocket library. The stack trace points to this code: https://github.com/moozzyk/SignalR-Client-Swift/blob/9e235dcea5e48128c78654db67c4fbd68b59307d/Sources/SignalRClient/WebSocket.swift#L1651-L1656
but I don't immediately see anything there. I think the problem is that I don't know what code the other thread is running:
$s18SwiftSignalRClient7Manager337B38D54E3FC021D04F957FB8C00AFD21LLCADycfcyycfU
I tried with a new obj-c project with just this pod, so no "external" code is involved, and either worked. From some checking what it comes to my mind is that some of the calls/structures (for example, mutex) are not fully supported by obj-c and that it is what is causing the "Swift access race".
If anyone interested, there is a pull request for the WebSocket.swift file on its original repo, that solves this issue. Pull request: https://github.com/tidwall/SwiftWebSocket/pull/141
@jaragones - this is a great find. My experience is that the SwiftWebSocket project is not actively maintained and PRs linger for a long time before they are merged (if ever). Fortunately, I forked the code so can take the fix without waiting for it to be merged in the SwiftWebSocket repo.
Cool! Thanks for the library by the way! :)
I applied the fix from the PR so if you try master you should no longer see the TSAN issue.
@jaragones - the version on CocoaPods now has the TSAN fix.
Cool, I was importing the library manually on my project! Thanks to let me know.
Is there any remaining work to support Objective-C? I am asking as the issue is still open, but from the conversation above it seems that @lexiaoyao20 @moozzyk and @jaragones have a working solution.
I have not tried but I am not sure how well Objective-C works with Codable
. The real interop should allow the same flexibility as the Swift version and a sane way of installing it into the project. What @lexiaoyao20 provided is great because it unblocks the scenario but require at least some work to turn it into a reusable framework solution. I started looking into it some time ago but given that I do this in my free time I barely made any progress.
You need to extend it on bridge with codable if response is not simple. But apart from that on my case works like a charm. 😊
On Sat, 12 Oct 2019 at 23:00, Pawel Kadluczka notifications@github.com wrote:
I have not tried but I am not sure how well Objective-C works with Codable. The real interop should allow the same flexibility as the Swift version and a sane way of installing it into the project. What @lexiaoyao20 https://github.com/lexiaoyao20 provided is great because it unblocks the scenario but require at least some work to turn it into a reusable framework solution. I started looking into it some time ago but given that I do this in my free time I barely made any progress.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/moozzyk/SignalR-Client-Swift/issues/37?email_source=notifications&email_token=AAQVYS5PIWG5HIE4XXQ27ETQOI3IBA5CNFSM4GHXUP22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBCIHSA#issuecomment-541361096, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAQVYSY6NASVO7CBNHKOQCTQOI3IBANCNFSM4GHXUP2Q .
-- Sent from Gmail Mobile
I am not sure if this is simple to do in a generic way but good to hear that it’s at least possible.
On Sun, Oct 13, 2019 at 11:50 AM Jordi Aragones-Vilella < notifications@github.com> wrote:
You need to extend it on bridge with codable if response is not simple. But apart from that on my case works like a charm. 😊
On Sat, 12 Oct 2019 at 23:00, Pawel Kadluczka notifications@github.com wrote:
I have not tried but I am not sure how well Objective-C works with Codable. The real interop should allow the same flexibility as the Swift version and a sane way of installing it into the project. What @lexiaoyao20 https://github.com/lexiaoyao20 provided is great because it unblocks the scenario but require at least some work to turn it into a reusable framework solution. I started looking into it some time ago but given that I do this in my free time I barely made any progress.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub < https://github.com/moozzyk/SignalR-Client-Swift/issues/37?email_source=notifications&email_token=AAQVYS5PIWG5HIE4XXQ27ETQOI3IBA5CNFSM4GHXUP22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBCIHSA#issuecomment-541361096 , or unsubscribe < https://github.com/notifications/unsubscribe-auth/AAQVYSY6NASVO7CBNHKOQCTQOI3IBANCNFSM4GHXUP2Q
.
-- Sent from Gmail Mobile
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/moozzyk/SignalR-Client-Swift/issues/37?email_source=notifications&email_token=AAK7JJC5FGYTV753TXUGIJ3QONUWPA5CNFSM4GHXUP22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBC5B7Q#issuecomment-541446398, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAK7JJFS5BTG6GDH5ROOCK3QONUWPANCNFSM4GHXUP2Q .
Has anyone tried https://github.com/DyKnow/SignalR-ObjC
I believe the client you pointed to is for the old (i.e. non-Asp.Net Core) version of SignalR.
On Mon, Oct 14, 2019 at 9:11 AM Navneet Gupta notifications@github.com wrote:
Has anyone tried https://github.com/DyKnow/SignalR-ObjC
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/moozzyk/SignalR-Client-Swift/issues/37?email_source=notifications&email_token=AAK7JJGSKIFKFYAJS7YBS4DQOSK3DA5CNFSM4GHXUP22YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEBFMNII#issuecomment-541771425, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAK7JJBLHKUZZWSEVMRA52LQOSK3DANCNFSM4GHXUP2Q .
you can add an adapter to support objective-c, like this.
// // SignalRClient.h // Followme // // Created by Subo on 2018/12/25. // Copyright © 2018年 com.followme. All rights reserved. // import Foundation import SwiftSignalRClient @objc public protocol SignalRClientDelegate : class { func clientDidConnectSuccess(client: SignalRClient) func clientDidConnectFailed(client: SignalRClient, error: Error) func clientDidClosed(client: SignalRClient, error: Error?) } /// 因为SwiftSignalRClient没有做OC的支持,所以这里多包装了一层来支持OC @objcMembers public class SignalRClient : NSObject { var url: String var headers: [String: String]? private var connection: HubConnection? public weak var delegate: SignalRClientDelegate? public init(url: String, headers: [String: String]?) { self.url = url self.headers = headers super.init() } public func start() { self.connection = HubConnectionBuilder(url: URL(string: self.url)!) .withLogging(minLogLevel: .debug) .withHttpConnectionOptions(configureHttpOptions: { (httpConnectionOptions) in if let header = self.headers { for (key, value) in header { httpConnectionOptions.headers[key] = value } } }) .build() self.connection?.delegate = self self.connection?.start() } public func stop() { self.connection?.stop() } public func on(method: String, callback: @escaping (_ jsonString: String?) -> Void) { self.connection?.on(method: method, callback: {args, typeConverter in if let json = args.first as? String { callback(json) return } callback(nil) }) } public func send(method: String, arguments:[AnyObject], sendDidComplete: @escaping (_ error: Error?) -> Void) { self.connection?.send(method: method, arguments: arguments, sendDidComplete: sendDidComplete) } public func invoke(method: String, arguments: [AnyObject], invocationDidComplete: @escaping (_ error: Error?) -> Void) { self.connection?.invoke(method: method, arguments: arguments, invocationDidComplete: invocationDidComplete) } } extension SignalRClient: HubConnectionDelegate { public func connectionDidOpen(hubConnection: HubConnection!) { self.delegate?.clientDidConnectSuccess(client: self) } public func connectionDidFailToOpen(error: Error) { self.delegate?.clientDidConnectFailed(client: self, error: error) } public func connectionDidClose(error: Error?) { self.delegate?.clientDidClosed(client: self, error: error) } }
You can use it like this.
#import "YourTarget-Swift.h" NSString *url = @"https://xxxxxxxxxxx"; NSDictionary *headers = @{@"token" : @"xxxxxxx"}; self.client = [[SignalRClient alloc] initWithUrl:url headers:headers]; self.client.delegate = self; __weak __typeof(self)weakSelf = self; [self.client onMethod:@"YourMethod" callback:^(NSString * _Nullable jsonString) { NSLog(@"arguments: %@", jsonString); }]; [self.client start];
public func send(method: String, arguments:[AnyObject], sendDidComplete: @escaping (_ error: Error?) -> Void) { self.connection?.send(method: method, arguments: arguments as! [Encodable], sendDidComplete: sendDidComplete) }
public func invoke(method: String, arguments: [AnyObject], invocationDidComplete: @escaping (_ error: Error?) -> Void) {
self.connection?.invoke(method: method, arguments: arguments as! [Encodable], invocationDidComplete: invocationDidComplete)
}
我按照你写的额,在oc里调用,但是不知道这个Encodable不知道对应OC里的什么修饰符号,最后报错 Could not cast value of type '__NSCFConstantString' (0x10a8e4ae0) to 'Swift.Encodable'
@aysw7331447 Encodable
无法转成OC的代码。
你只能用 SwiftSignalRClient
低版本的代码,我目前是用的 0.4.2 版本
pod 'SwiftSignalRClient', '0.4.2'
Can you support objective-c
Demo for adding an adapter to support objective-c via SwiftSignalRClient https://github.com/zangyang/SignalR-ObjC-Bridge-Swift
@moozzyk any update on this?
Can you support objective-c