Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can you support objective-c #37

Open
BinBear opened this issue Dec 3, 2018 · 25 comments
Open

Can you support objective-c #37

BinBear opened this issue Dec 3, 2018 · 25 comments

Comments

@BinBear
Copy link

BinBear commented Dec 3, 2018

Can you support objective-c

@moozzyk
Copy link
Owner

moozzyk commented Dec 3, 2018

Will need to figure this out. Looks like a similar issue to #13

@lexiaoyao20
Copy link

lexiaoyao20 commented Jan 25, 2019

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];

@moozzyk
Copy link
Owner

moozzyk commented Jan 25, 2019

@lexiaoyao20 - this is cool!

@moozzyk
Copy link
Owner

moozzyk commented May 29, 2019

@lexiaoyao20 - do you mind if I use your code as part of this project?

@lexiaoyao20
Copy link

@lexiaoyao20 - do you mind if I use your code as part of this project?

NO problem.

@moozzyk
Copy link
Owner

moozzyk commented May 30, 2019 via email

@jaragones
Copy link

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!

@moozzyk
Copy link
Owner

moozzyk commented Aug 5, 2019

Seems like a data race in the WebSocket library. The stack trace points to this code:

func nextId() -> Int {
pthread_mutex_lock(&mutex)
defer { pthread_mutex_unlock(&mutex) }
_nextId += 1
return _nextId
}

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:
$s18SwiftSignalRClient7Manager33_7B38D54E3FC021D04F957FB8C00AFD21LLCADycfcyycfU_ (SwiftSignalRClient:x86_64+0xd8692)

@jaragones
Copy link

jaragones commented Aug 6, 2019

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".

@jaragones
Copy link

If anyone interested, there is a pull request for the WebSocket.swift file on its original repo, that solves this issue. Pull request: tidwall/SwiftWebSocket#141

@moozzyk
Copy link
Owner

moozzyk commented Aug 26, 2019

@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.

@jaragones
Copy link

Cool! Thanks for the library by the way! :)

moozzyk added a commit that referenced this issue Sep 8, 2019
SwiftWebSocket has a threading issue described in
#37 (comment).
There is a PR in the SwiftWebSocket (tidwall/SwiftWebSocket#141) repo to fix the problem but it does
not seem like it is going to be merged anytime soon so applying the fix
locally.
A nice post describing the problem: http://www.russbishop.net/the-law
@moozzyk
Copy link
Owner

moozzyk commented Sep 8, 2019

I applied the fix from the PR so if you try master you should no longer see the TSAN issue.

@moozzyk
Copy link
Owner

moozzyk commented Sep 30, 2019

@jaragones - the version on CocoaPods now has the TSAN fix.

@jaragones
Copy link

Cool, I was importing the library manually on my project! Thanks to let me know.

@navneet-g
Copy link

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.

@moozzyk
Copy link
Owner

moozzyk commented Oct 12, 2019

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.

@jaragones
Copy link

jaragones commented Oct 13, 2019 via email

@moozzyk
Copy link
Owner

moozzyk commented Oct 13, 2019 via email

@navneet-g
Copy link

Has anyone tried https://github.com/DyKnow/SignalR-ObjC

@moozzyk
Copy link
Owner

moozzyk commented Oct 14, 2019 via email

@aysw7331447
Copy link

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'

@lexiaoyao20
Copy link

@aysw7331447 Encodable 无法转成OC的代码。
你只能用 SwiftSignalRClient 低版本的代码,我目前是用的 0.4.2 版本

pod 'SwiftSignalRClient', '0.4.2'

@zangyang
Copy link

Can you support objective-c

Demo for adding an adapter to support objective-c via SwiftSignalRClient
https://github.com/zangyang/SignalR-ObjC-Bridge-Swift

@govi2010
Copy link

@moozzyk any update on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants