We are trying to get minimal example for XMPPFramework to run.
Consider this simple class:
import Foundation
import XMPPFramework
class Connection: NSObject, XMPPStreamDelegate {
let stream: XMPPStream
override init() {
self.stream = XMPPStream()!
}
func connectToServer(timeout: TimeInterval) {
stream.addDelegate(self, delegateQueue: DispatchQueue.main)
stream.myJID = XMPPJID(string: "myuser")
stream.hostName = "myserver.tld"
stream.hostPort = 5222
do {
try stream.connect(withTimeout: timeout)
}
catch {
print(error)
}
}
func xmppStreamWillConnect(_ sender: XMPPStream!) {
print("will connect")
}
func xmppStreamDidConnect(_ sender: XMPPStream!) {
print("did connect")
self.stream.disconnect()
}
}
And this simple test class:
import Foundation
import XCTest
@testable import MyModule
class ConnectionTests: XCTestCase {
func testConnect() {
let connection = Connection()
print("Try to connect")
let expectation = self.expectation(description: "connect")
connection.connectToServer(timeout: 3)
self.waitForExpectations(timeout: 5)
}
}
I expect this output:
Try to connect
will connect
did connect
<5s timeout since I don't fulfill>
If, by accident, my XMPP server did not react favorably to my request, I'd like to see:
Try to connect
will connect
<some error message>
If the XMPP server did not reply (quickly), I'd expect:
Try to connect
will connect
<3s timeout reached>
However, I'm getting none of these but instead:
Try to connect
will connect
<5s timeout since I don't fulfill>
What is going on?
Here is what I gather.
DispatchQueue.main
.DispatchQueue.main
or DispatchQueue(label: "test", qos: .userInitiated, attributes: .concurrent)
is used.dispatch_sync(xmppQueue, block);
in XMPPStream.connectWithTimeout
where xmppQueue
is a new queue with label xmpp
; the block seems to be executed completely, too.So I don't understand who blocks whom here, and how to prevent it from happening.
The problem is three-fold:
We implement some additional delegate methods:
func xmppStream(_ sender: XMPPStream!, socketDidConnect socket: GCDAsyncSocket!) {
print("socket did connect")
}
func xmppStreamDidStartNegotiation(_ sender: XMPPStream!) {
print("negotiation started")
}
...
func xmppStream(_ sender: XMPPStream!, didReceiveError error: DDXMLElement!) {
print("error: \(error)")
}
func xmppStreamDidDisconnect(_ sender: XMPPStream!, withError error: Error!) {
print("disconnected")
self.afterConnection()
}
Here, self.afterConnection
is a function passed to connectToServer
as additional parameter.
And we change in the test:
connection.connectToServer(timeout: 3, then: { expectation.fulfill() })
Now the test terminates in an orderly fashion with this output:
Try to connect
will connect
socket did connect
negotiation started
disconnected
error: Optional(<stream:error xmlns:stream="http://etherx.jabber.org/streams"><host-unknown xmlns="urn:ietf:params:xml:ns:xmpp-streams"/></stream:error>)
So it's a problem with the server, after all. We needed to implement the correct delegates to diagnose the issue; we'd have expected stream.connect
to throw an error in such a case!