centrifugal / centrifuge-swift Goto Github PK
View Code? Open in Web Editor NEWSwift client SDK for bidirectional real-time communication with Centrifugo and Centrifuge-based server over WebSocket
License: MIT License
Swift client SDK for bidirectional real-time communication with Centrifugo and Centrifuge-based server over WebSocket
License: MIT License
Hello! We faced multiple crashes in method handleAsyncData(data:)
We're using 0.4.2 version in our app
Stack trace:
Crashed: com.centrifugal.centrifuge-swift.sync<A3640EA9-3604-4193-863B-41F57316740F>
0 SwiftCentrifuge 0x104e0 CentrifugeClient.handleAsyncData(data:) + 4393944288 (<compiler-generated>:4393944288)
1 SwiftCentrifuge 0xc63c closure #1 in CentrifugeClient.onData(data:) + 699 (Client.swift:699)
2 SwiftCentrifuge 0x5384 thunk for @escaping @callee_guaranteed () -> () + 4393898884 (<compiler-generated>:4393898884)
3 libdispatch.dylib 0x2914 _dispatch_call_block_and_release + 32
4 libdispatch.dylib 0x4660 _dispatch_client_callout + 20
5 libdispatch.dylib 0xbde4 _dispatch_lane_serial_drain + 672
6 libdispatch.dylib 0xc958 _dispatch_lane_invoke + 392
7 libdispatch.dylib 0x171a8 _dispatch_workloop_worker_thread + 656
8 libsystem_pthread.dylib 0x10f4 _pthread_wqthread + 288
9 libsystem_pthread.dylib 0xe94 start_wqthread + 8
As the topic says. Thanks!
From here: centrifugal/centrifugo/issues/446
It would be extra great if the centrifuge-swift client libraries could accept an offset and epoch right in subscribe method - automatically retrieving and playing back any messages missed afterwards.
Hello.
I faced this case: after successful connect (onConnect method of the delegate) i'm calling subscribe (method of the CentrifugeSubscription class) and after that i received successful callback (onSubscribeSuccess method of the delegate)
But in the admin-system on the server i dont see this subscription - presence array is empty
(For the server 2.2.0 version is used)
What do you think about the possible reason of that?
Noticed several data races on 0.5.5 release. This doesn't look good :(
(Thread Sanitizer can be used only while running code on simulator ATM).
I will try to investigate this issue when I have time.
WebSocket.fragBuffer
field
<project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:769 Data race in SwiftCentrifuge.WebSocket.fragBuffer.getter : Swift.Optional<Foundation.Data> at 0x125a19e00
Location is a 672-byte heap object at 0x125a19e00
Write of size 8 by thread 314
#0 0x000000010449cce0 in WebSocket.fragBuffer.setter ()
#1 0x00000001044a62bc in WebSocket.cleanupStream() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:742
#2 0x00000001044a6120 in WebSocket.disconnectStream(_:runDelegate:) at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:729
#3 0x00000001044a4824 in WebSocket.initStreamsWithData(_:_:) at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:649
#4 0x00000001044a3744 in WebSocket.createHTTPRequest() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:619
#5 0x000000010449fbfc in WebSocket.connect() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:499
#6 0x00000001044372d4 in closure #1 in closure #1 in closure #1 in CentrifugeClient.scheduleReconnect() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/Client.swift:761
#7 0x0000000104445dc4 in partial apply for closure #1 in closure #1 in closure #1 in CentrifugeClient.scheduleReconnect() ()
#8 0x000000010441bce0 in thunk for @escaping @callee_guaranteed () -> () ()
#9 0x000000011054b694 in __tsan::invoke_and_release_block(void*) ()
#10 0x00000001154a5d5c in _dispatch_client_callout ()
Read of size 8 by thread 309
#0 0x000000010449cbc8 in WebSocket.fragBuffer.getter ()
#1 0x00000001044a68c8 in closure #1 in WebSocket.dequeueInput() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:769
#2 0x00000001044bbaa8 in partial apply for closure #1 in WebSocket.dequeueInput() ()
#3 0x00000001a1b31b24 in autoreleasepool<τ_0_0>(invoking:) ()
#4 0x00000001044a6560 in WebSocket.processInputStream() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:757
#5 0x00000001044a5e30 in WebSocket.newBytesInStream() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:711
#6 0x00000001044b87f0 in protocol witness for WSStreamDelegate.newBytesInStream() in conformance WebSocket ()
#7 0x00000001044905cc in FoundationStream.stream(_:handle:) at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:305
#8 0x0000000104490978 in @objc FoundationStream.stream(_:handle:) ()
#9 0x0000000180396438 in _signalEventSync ()
#10 0x00000001154a5d5c in _dispatch_client_callout ()
WebSocket.connected
field
<project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:777 Data race in SwiftCentrifuge.WebSocket.connected.getter : Swift.Bool at 0x125a19e00
Location is a 672-byte heap object at 0x125a19e00
Read of size 1 by thread 309
#0 0x000000010449b7cc in WebSocket.connected.getter ()
#1 0x00000001044a6b3c in closure #1 in WebSocket.dequeueInput() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:777
#2 0x00000001044bbaa8 in partial apply for closure #1 in WebSocket.dequeueInput() ()
#3 0x00000001a1b31b24 in autoreleasepool<τ_0_0>(invoking:) ()
#4 0x00000001044a6560 in WebSocket.processInputStream() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:757
#5 0x00000001044a5e30 in WebSocket.newBytesInStream() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:711
#6 0x00000001044b87f0 in protocol witness for WSStreamDelegate.newBytesInStream() in conformance WebSocket ()
#7 0x00000001044905cc in FoundationStream.stream(_:handle:) at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:305
#8 0x0000000104490978 in @objc FoundationStream.stream(_:handle:) ()
#9 0x0000000180396438 in _signalEventSync ()
#10 0x00000001154a5d5c in _dispatch_client_callout ()
Heap block allocated by thread 297
Write of size 1 by thread 314
#0 0x000000010449b8b4 in WebSocket.connected.setter ()
#1 0x00000001044a6134 in WebSocket.disconnectStream(_:runDelegate:) at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:730
#2 0x00000001044a4824 in WebSocket.initStreamsWithData(_:_:) at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:649
#3 0x00000001044a3744 in WebSocket.createHTTPRequest() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:619
#4 0x000000010449fbfc in WebSocket.connect() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift:499
#5 0x00000001044372d4 in closure #1 in closure #1 in closure #1 in CentrifugeClient.scheduleReconnect() at <project path>/centrifuge-swift/Sources/SwiftCentrifuge/Client.swift:761
#6 0x0000000104445dc4 in partial apply for closure #1 in closure #1 in closure #1 in CentrifugeClient.scheduleReconnect() ()
#7 0x000000010441bce0 in thunk for @escaping @callee_guaranteed () -> () ()
#8 0x000000011054b694 in __tsan::invoke_and_release_block(void*) ()
#9 0x00000001154a5d5c in _dispatch_client_callout ()
//...
public struct CentrifugeConnectEvent {
public var client: String
}
//...
Specification Connect result fields:
Field | Type | Optional | Description |
---|---|---|---|
data | JSON | yes | a custom data to send to the client in connect command response. |
In Java all is OK ;)
package io.github.centrifugal.centrifuge;
public class ConnectEvent {
public String getClient() {
return client;
}
void setClient(String client) {
this.client = client;
}
public byte[] getData() {
return data;
}
void setData(byte[] data) {
this.data = data;
}
private String client;
private byte[] data;
}
The same situation in dart library, but not in swift
First off, thanks for the great centrifuge package! Really awesome work.
I'm not quite sure what this warning means actually, but after adding centrifuge to my project using Swift Package Manager (thanks for supporting it!) I got the following:
There isn't any additional detail unfortunately. And I can't seem to be able to do import SwiftCentrifuge
after setting it up.
We get onError method error type CentrifugeError.transportError The operation couldn’t be completed. (SwiftCentrifuge.WSError error 1.)
but our app is live but not able to reproduce issue on debug mode which reason to this disconnect issue occurs not able to reconnect. so we are facing issue. did not get proper error reason so it very difficulte how to fix it this issue.
If we log using this event.error.localizedDescription
then we get The operation couldn’t be completed. (SwiftCentrifuge.CentrifugeError error 0.)
if let centrifugeError = event.error as? CentrifugeError {
switch centrifugeError {
case .timeout:
errMsg = "timeout"
case .duplicateSub:
errMsg = "duplicateSub"
return
case .clientDisconnected:
errMsg = "clientDisconnected"
return
case .subscriptionUnsubscribed:
errMsg = "subscriptionUnsubscribed"
return
case .transportError(let err):
errMsg = "transportError: \(err.localizedDescription)"
case .tokenError(let err):
errMsg = "tokenError: \(err.localizedDescription)"
case .connectError(let err):
errMsg = "connectError: \(err.localizedDescription)"
case .refreshError(let err):
errMsg = "refreshError: \(err.localizedDescription)"
case .subscriptionSubscribeError(let err):
errMsg = "subscriptionSubscribeError: \(err.localizedDescription)"
case .subscriptionTokenError(let err):
errMsg = "subscriptionTokenError: \(err.localizedDescription)"
case .subscriptionRefreshError(let err):
errMsg = "subscriptionRefreshError: \(err.localizedDescription)"
case .replyError(let code, let message, _):
errMsg = "replyError: code: \(code) message:\(message)"
}
}
if errMsg.notEmpty {
errMsg += "; localizedDescription: \(event.error.localizedDescription)"
} else {
errMsg = event.error.localizedDescription
}```
Error message got while log on amplitude.
`"transportError: The operation couldn’t be completed. (SwiftCentrifuge.WSError error 1.); localizedDescription: The operation couldn’t be completed. (SwiftCentrifuge.CentrifugeError error 0.)"`
As described in my PR
There is a memory leak in the Client Token getter implementation
The client fails to connect without an initial token.
Steps to reproduce:
CentrifugeClient
with an empty token and a non-nil token getter.connect
The client tries to get a token via the token getter but it fails to proceed because it simultaneously tries to connect with an empty token.
SwiftCentrifuge version: 0.0.3
subscribe
(this step is optional).subscribe
.unsubscribe
on channel B subscription from step 3.subscribe
.Client is successfully resubscribed to channel B, by means of either an existing subscription or a new one.
On step 5 newSubscription
method throws a ".duplicateSub" error. However, there is no way to resubscribe to a given channel having only the channel name, and not having previously created subscription. subscriptions
array of the CentrifugeClient
is private, and there are no accessors to it.
Currently, the only way to overcome this limitation is storing all the subscriptions ever created outside of the CentrifugeClient
. It seems not elegant: such collection will duplicate the contents of subscriptions
array. It also may become inefficient if client subscribes and unsubscribes a lot to some random channels. We'll have to store all the subscriptions, even if quite a few of them will be subscribed for the second time.
Introduce a public getter for existing subscriptions. It may look like
func existingSubscription(channel: String) -> CentrifugeSubscription?
Allow reusing existing subscription after it was unsubscribed already. I've implemented this in our fork as the easiest but still working option, so I can create a PR if you like this option.
Remove unsubscribed subscriptions from subscriptions
array of the CentrifugeClient. That could be done inside of func unsubscribe(sub: CentrifugeSubscription)
I guess. It would solve not only the discussed issue, but also make CentrifugeClient more scalable. Сurrent implementation looks suspiciously: wouldn't the subscription look up time degrade after connecting and disconnecting from lots of channels? At least we could store subscriptions in a map (with channel name used as a key), not in array.
In one side a client keeps strong all it's subscriptions in other side a subscription is keeping the client.
We have a strong reference circle here, which cause a memory leak problems in some cases.
I don't see any reason why the subscription must keep a strong reference on it's client.
May be will be better if a subscription keeps a weak reference on it's client?
Minus one case to catch a memory leak.
On one of my screens, I subscribe to a large number of channels. Currently, this means an HTTPS request for each subscription.
I noticed that the JS library supports batch subscriptions. Are there any plans to implement this here too?
Description:
I would like to inquire about the behavior of the connection in the centrifuge package when the iOS app goes into the background. The package documentation mentions that the connection may be closed shortly after the app enters the background due to OS-specific limitations. However, during my testing, I observed that the connection seemed to be closed immediately, regardless of the available OS resources.
Additional Information:
To ensure a thorough understanding of the package's behavior and any potential workarounds, I would greatly appreciate clarification on the following points:
Does the centrifuge package indeed close the connection immediately when the app goes into the background?
If the closure is immediate, is there any recommended approach or workaround to maintain the connection or receive socket messages while the app is in the background even if it will be for a short/limited time?
Thank you for your assistance in clarifying these questions. I appreciate your time and support.
I made pull request
#11
Now requires manual:
pod trunk push SwiftCentrifuge.podspec
Added in #84
It's not safe to read and write memory at the same time. If it's happened it's end like a crash.
In functions 'waitForReply' and 'waitForConnect' we have two closures which run on difference threads.
And we have an unsafe operations with variable 'groupLeft'. In other hand for variable 'group' it's ok case it threads safe class.
Sadly but we don't have any atomic variables in swift to make our work easy, so we must add here 'NSLock' or just simple jump in 'timeoutTask' to 'syncQueue'.
When I have tried to use the new released protobuf 1.27.0 that broke for me any websocket updates due to I am receiving in the console a lot of [debug] messages like: connected -> reconnected and so on endless loop of this events. Any thought about this issue?
- WARN | xcodebuild: SwiftCentrifuge/Sources/SwiftCentrifuge/WebSocket/NativeWebSocket.swift:203:9: warning: switch covers known cases, but 'URLSessionTask.State' may have additional unknown values, possibly added in future versions
- NOTE | xcodebuild: SwiftCentrifuge/Sources/SwiftCentrifuge/WebSocket/NativeWebSocket.swift:203:9: note: handle unknown values using "@unknown default"
- NOTE | [iOS] xcodebuild: warning: Capabilities for Signing & Capabilities may not function correctly because its entitlements use a placeholder team ID. To resolve this, select a development team in the App editor. (in target 'App' from project 'App')
I am debugging a connection issue and I noticed that there's no event whatsoever when the connection is not succeeding.
My code is almost the same as in README:
let client = CentrifugeClient(url: url, config: CentrifugeClientConfig(), delegate: CentrifugeDelegate())
client.setToken(self.token)
client.connect()
... where CentrifugeDelegate
has the onConnect
and onDisconnect
methods.
What happens is that the code gets executed, it's not connected to the server (log_level=debug doesn't show anything), and neither of the two methods are called. Basically, nothing happens.
I think it is useful both for debugging and for notifying the users that there's some delegate method, error or something else that gets triggered when the initial connection fails.
Hello!
While Swift is generally considered to be a superior option, it may still be necessary to maintain compatibility with older projects written in Obj-C, Unity-generated projects, and KMM. To achieve this, it would be advisable to annotate all public classes and functions with the @objc annotation.
When I try to connect, I am always thrown into onDisconnect. I looked into the connect() and there I came across an error 308 and message "Invalid HTTP upgrade". Do u have any solutions ?
Hi
We're dealing with a scenario where we connect to a centrifuge via a middleware. This middleware verifies our authentication using a value in the headers. The authentication value in the headers has a limited lifetime, so we must update it in the client's headers for the next reconnect.
Is it currently impossible? Perhaps you could suggest a workaround?
Thank you
Currently, the Subscription.delegate
is internal
so it can't be set from outside the library. In order for me to be able to change it so I need to make it public
.
I have a UIViewController
inside a UINavigationController
which is inside a UISplitViewController
. On a large phone when you tap the back button the view controller is not immediately deallocated in case you rotate the phone. In this case the view controller will be displayed in an expanded split view.
What this means is that the deinit
of the old screen is not called until just before the new screen is set up. I am removing subscriptions in deinit
and creating them in viewDidLoad
. These happen quite close to one another. It works most of the time but every so often I get an error that I am already subscribed to the channel. Looking at the code I think I can see where it is going wrong.
In the following method, the unsubscribe
method returns immediately, but its contents run on syncQueue
. If this is backed up the subscriptionsLock
is released before the Client.unsubscribe(sub:)
is performed. For this to be bulletproof it would need to use completion handlers and a DispatchGroup to wait until all unsubscribes are done. Although this could have performance implications.
centrifuge-swift/Sources/SwiftCentrifuge/Client.swift
Lines 268 to 278 in dc4bde7
On a side note, it shouldn't be possible to have more than one subscription with the same channel
. This would suggest that the removeSubscription
could be simplified. In this case, a simple completion handler would do the trick.
I would be happy to put together a demo project if you would let me know of a public server I could connect to.
Hi 👋
There's possible to use this library with SwiftUI ?
centrifuge-swift/Sources/SwiftCentrifuge/WebSocket.swift
Lines 471 to 476 in 6c2a210
This logic is not really obvious and Origin header does not make any sense in terms of security when sent from mobile device. And requires explicit allowed Origin configuration on server side.
Hi,
I'm using the swift SDK of Centerfuge and was wondering why the CentrifugePublicationEvent's info value is not public
Currently it looks like this
public struct CentrifugePublicationEvent {
public var data: Data
public var offset: UInt64
public var tags: [String: String]
var info: CentrifugeClientInfo?
}
Thanks
Write now each Subscription has its own sync queue. I guess it will be better if Subscription will be work on Client sync queue by two reasons:
Is there a way to know the current status of the client? I understand that there's connect/disconnect events, but if I want to know the status of the client at any time I have to keep track of the events myself. Would be great if something like status
can be exposed for public (get) access.
Dear Author:
I found a few problems when using this project.
1.Unsubscribe without going through the protocol callback
2.Create a channel object multiple times, it will kill the app
I am working on your demo.
@FZambia
Crashed: NSOperationQueue 0x107f17910 (QOS: UNSPECIFIED)
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x00000b785f7af4f0
0 libobjc.A.dylib 0x1540 objc_msgSend + 32
1 SwiftCentrifuge 0x4a660 $s15SwiftCentrifuge9WebSocketC12dequeueWrite33_266A61F1477EE162B72F41A784B32A3ALL_4code15writeCompletiony10Foundation4DataV_AC6OpCodeOyycSgtFyycfU_ + 2048
2 SwiftCentrifuge 0x84fc $sIeg_IeyB_TR + 28
3 Foundation 0x50c54 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 24
4 Foundation 0x62de8 -[NSBlockOperation main] + 104
5 Foundation 0x3b774 __NSOPERATION_IS_INVOKING_MAIN__ + 24
6 Foundation 0x4c6f0 -[NSOperation start] + 804
7 Foundation 0x4fc9c __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 24
8 Foundation 0x5dd40 __NSOQSchedule_f + 184
9 libdispatch.dylib 0x13454 _dispatch_block_async_invoke2 + 148
10 libdispatch.dylib 0x4670 _dispatch_client_callout + 20
11 libdispatch.dylib 0x7b44 _dispatch_continuation_pop + 504
12 libdispatch.dylib 0x71a0 _dispatch_async_redirect_invoke + 596
13 libdispatch.dylib 0x15de0 _dispatch_root_queue_drain + 396
14 libdispatch.dylib 0x16608 _dispatch_worker_thread2 + 164
15 libsystem_pthread.dylib 0x10b8 _pthread_wqthread + 228
16 libsystem_pthread.dylib 0xe94 start_wqthread + 8
The application will occasionally crash when opened .I can only view the report from Firebase Analytics.
Currently i using the latest version Swift Centrifuge (0.4.4).
At the moment, the centrifuge-swift only support Protobuf protocol, I have an old Swift codebase with JSON protocol that uses Encodable/Decodable and Because it's too much work for me to change that, is it possible to encode/decode Protobuf with Encodable? Is there any library that implements Encodable and Decodable on Protobuf protocol?
Any suggestions would be helpful.
*** Skipped building centrifuge-swift due to the error:
Dependency "centrifuge-swift" has no shared framework schemes for any of the platforms: iOS
If you believe this to be an error, please file an issue with the maintainers at https://github.com/centrifugal/centrifuge-swift/issues/new
Looking at the source, there's no xcodeproj file (other than the one for example), and so there's no target that builds the framework. I believe that's the reason.
func onError(_ client: CentrifugeClient, _ event: CentrifugeErrorEvent){
}
about onError function, I get following error
▿ CentrifugeError
▿ transportError : 1 element
▿ error : WSError
- type : SwiftCentrifuge.ErrorType.protocolError
- message : "{"reason":"bad request","reconnect":false}"
- code : 3003
Please help me try to understand error:
Thanks
At moment it's always false.
Method presence(completion:)
in CentrifugeSubscription
has completion block with parameter of type [String: CentrifugeClientInfo]?
, but I can not use any property from CentrifugeClientInfo
because it have internal
access modifier.
Question is how should I use CentrifugeClientInfo
object? Is it really needed to use internal
instead of public
?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.