From 24b695b8fbc1e3ffb773fd59c96652070c4ee2e2 Mon Sep 17 00:00:00 2001 From: kosyloa Date: Tue, 18 Jun 2024 13:57:02 +0200 Subject: [PATCH] add DxFeedReconnectSample --- DXFeedFramework.xcodeproj/project.pbxproj | 2 + README.md | 5 +- .../Contents.swift | 99 +++++++++++++++++++ .../contents.xcplayground | 4 + .../OnDemandSample.playground/Contents.swift | 2 +- 5 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 Samples/Playgrounds/DxFeedReconnectSample.playground/Contents.swift create mode 100644 Samples/Playgrounds/DxFeedReconnectSample.playground/contents.xcplayground diff --git a/DXFeedFramework.xcodeproj/project.pbxproj b/DXFeedFramework.xcodeproj/project.pbxproj index d12c03983..adb4d6efa 100644 --- a/DXFeedFramework.xcodeproj/project.pbxproj +++ b/DXFeedFramework.xcodeproj/project.pbxproj @@ -916,6 +916,7 @@ 64F73BA32B67B28B0088EC37 /* NativePromise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativePromise.swift; sourceTree = ""; }; 64F73BA52B67CD5B0088EC37 /* GraalException+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "GraalException+Ext.swift"; sourceTree = ""; }; 64F9C6C12B4BFD8F003ED014 /* DXFeedconnect.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DXFeedconnect.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 64FDD7B62C21A09000D4469B /* DxFeedReconnectSample.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DxFeedReconnectSample.playground; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 803BAC0D29BFA50700FFAB1C /* DXFeedFramework.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DXFeedFramework.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 803BAC1029BFA50700FFAB1C /* DxFeedSwiftFramework.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DxFeedSwiftFramework.h; sourceTree = ""; }; 803BAC1529BFA50700FFAB1C /* DXFeedFrameworkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DXFeedFrameworkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -1115,6 +1116,7 @@ 6433B1302BCE87D4004EFED7 /* RequestProfile.playground */, 64A631CF2BDFAA27002E1002 /* OnDemandSample.playground */, 643A329C2BD15F2900F6F790 /* LastEventsConsole.playground */, + 64FDD7B62C21A09000D4469B /* DxFeedReconnectSample.playground */, ); path = Playgrounds; sourceTree = ""; diff --git a/README.md b/README.md index d8c44da11..b8b0345c6 100644 --- a/README.md +++ b/README.md @@ -236,7 +236,10 @@ is a simple demonstration of how to get live updates for Instrument Profiles - [x] [ScheduleSample](https://github.com/dxFeed/dxfeed-graal-swift-api/blob/swift/Samples/Playgrounds/ScheduleSample.playground/Contents.swift) is a simple demonstration of how to get various scheduling information for instruments - [x] [FetchDailyCandles](https://github.com/dxFeed/dxfeed-graal-swift-api/blob/swift/Samples/Playgrounds/FetchDailyCandles.playground/Contents.swift) is a simple demonstration of how to fetch last N-days of candles for a specified symbol - +- [x] [DxFeedReconnectSample](https://github.com/dxFeed/dxfeed-graal-swift-api/blob/swift/Samples/Playgrounds/DxFeedReconnectSample.playground/Contents.swift) + is a simple demonstration of how to connect to an endpoint, subscribe to market data events, handle reconnections + and re-subscribing. + ## Current State ### Endpoint Roles diff --git a/Samples/Playgrounds/DxFeedReconnectSample.playground/Contents.swift b/Samples/Playgrounds/DxFeedReconnectSample.playground/Contents.swift new file mode 100644 index 000000000..cd329a93d --- /dev/null +++ b/Samples/Playgrounds/DxFeedReconnectSample.playground/Contents.swift @@ -0,0 +1,99 @@ +import UIKit +import PlaygroundSupport +import DXFeedFramework + +// Empty Event Listener with handler +class Listener: DXEventListener, Hashable { + + static func == (lhs: Listener, rhs: Listener) -> Bool { + lhs === rhs + } + + func hash(into hasher: inout Hasher) { + hasher.combine("\(self):\(stringReference(self))") + } + var callback: ([MarketEvent]) -> Void = { _ in } + + func receiveEvents(_ events: [MarketEvent]) { + self.callback(events) + } + + init(overrides: (Listener) -> Listener) { + _ = overrides(self) + } +} + +// Empty Endpoint Listener with handler +class EndpoointStateListener: DXEndpointListener, Hashable { + func endpointDidChangeState(old: DXFeedFramework.DXEndpointState, new: DXFeedFramework.DXEndpointState) { + callback(old, new) + } + + static func == (lhs: EndpoointStateListener, rhs: EndpoointStateListener) -> Bool { + lhs === rhs + } + + func hash(into hasher: inout Hasher) { + hasher.combine("\(self):\(stringReference(self))") + } + var callback: (DXEndpointState, DXEndpointState) -> Void = { _,_ in } + + init(overrides: (EndpoointStateListener) -> EndpoointStateListener) { + _ = overrides(self) + } +} + + +// Demonstrates how to connect to an endpoint, subscribe to market data events, +// handle reconnections and re-subscribing. +let address = "demo.dxfeed.com:7300" // The address of the DxFeed endpoint. +let symbol = "ETH/USD:GDAX" // The symbol for which we want to receive quotes. + +// Create new endpoint and add a listener for state changes. +let endpoint = try DXEndpoint.getInstance() +let stateListener = EndpoointStateListener { listener in + listener.callback = { old, new in + print("Connection state changed: \(old) -> \(new)") + } + return listener +} +endpoint.add(listener: stateListener) + +// Connect to the endpoint using the specified address. +try endpoint.connect(address) + +// Create a subscription for Quote events. +let subscriptionQuote = try endpoint.getFeed()?.createSubscription(Quote.self) +// Listener must be attached before symbols are added. +let listener = Listener { listener in + listener.callback = { events in + events.forEach { event in + print(event.toString()) + } + } + return listener +} +try subscriptionQuote?.add(listener: listener) + +// Add the specified symbol to the subscription. +try subscriptionQuote?.addSymbols(symbol) + +// Wait for five seconds to allow some quotes to be received. +Thread.sleep(forTimeInterval: 5) + +// Disconnect from the endpoint. +try endpoint.disconnect() + +// Wait for another five seconds to ensure quotes stop coming in. +Thread.sleep(forTimeInterval: 5) + +// Reconnect to the endpoint. +// The subscription is automatically re-subscribed, and quotes start coming into the listener again. +// Another address can also be passed on. +try endpoint.connect(address) + +// infinity execution +PlaygroundPage.current.needsIndefiniteExecution = true + +// to finish execution run this line +PlaygroundPage.current.finishExecution() diff --git a/Samples/Playgrounds/DxFeedReconnectSample.playground/contents.xcplayground b/Samples/Playgrounds/DxFeedReconnectSample.playground/contents.xcplayground new file mode 100644 index 000000000..cf026f228 --- /dev/null +++ b/Samples/Playgrounds/DxFeedReconnectSample.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Samples/Playgrounds/OnDemandSample.playground/Contents.swift b/Samples/Playgrounds/OnDemandSample.playground/Contents.swift index c1502ed67..befeec178 100644 --- a/Samples/Playgrounds/OnDemandSample.playground/Contents.swift +++ b/Samples/Playgrounds/OnDemandSample.playground/Contents.swift @@ -66,7 +66,7 @@ do { let connectedState = (try? onDemand.getEndpoint()?.getState()) ?? .notConnected print("Current state is \(connectedState), on-demand time is \(timeStr)") } - Thread.sleep(forTimeInterval: 1000) + Thread.sleep(forTimeInterval: 1) } // close endpoint completely to release resources try onDemand.getEndpoint()?.closeAndAwaitTermination()