diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index f5e7dc0..5e1696e 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -14,9 +14,9 @@ jobs: steps: - uses: actions/checkout@v3 - - uses: swift-actions/setup-swift@65540b95f51493d65f5e59e97dcef9629ddf11bf + - uses: swift-actions/setup-swift@v2 with: - swift-version: "5.8" + swift-version: "5.9.2" - name: Install swift-format tool run: brew install swift-format - name: Swift-format linting Sources directory diff --git a/.swift-version b/.swift-version index 3659ea2..95ee81a 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -5.8 +5.9 diff --git a/Package.swift b/Package.swift index efd070a..a5e4032 100644 --- a/Package.swift +++ b/Package.swift @@ -1,11 +1,12 @@ -// swift-tools-version:5.7 +// swift-tools-version:5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "FeaturevisorSDK", - products: [ + platforms: [.iOS(.v13), .tvOS(.v13), .macOS(.v10_15), .watchOS(.v6), .macCatalyst(.v13), .visionOS(.v1)], + products: [ // Products define the executables and libraries produced by a package, and make them visible to other packages. .library( name: "FeaturevisorSDK", diff --git a/Sources/FeaturevisorSDK/Instance+Fetch.swift b/Sources/FeaturevisorSDK/Instance+Fetch.swift index f562669..d390f4a 100644 --- a/Sources/FeaturevisorSDK/Instance+Fetch.swift +++ b/Sources/FeaturevisorSDK/Instance+Fetch.swift @@ -11,38 +11,32 @@ extension FeaturevisorInstance { completion: @escaping (Result) -> Void ) throws { + guard let datafileURL = URL(string: url) else { + throw FeaturevisorError.invalidURL(string: url) + } + guard let handleDatafileFetch else { - try fetchDatafileContent(from: url, completion: completion) + fetch(from: datafileURL, completion: completion) return } - completion(handleDatafileFetch(url)) + Task { + completion(await handleDatafileFetch(datafileURL)) + } } } extension FeaturevisorInstance { - fileprivate func fetchDatafileContent( - from url: String, - completion: @escaping (Result) -> Void - ) throws { - - guard let datafileUrl = URL(string: url) else { - throw FeaturevisorError.invalidURL(string: url) - } - - var request = URLRequest(url: datafileUrl) - request.setValue("application/json", forHTTPHeaderField: "Content-Type") - request.setValue("no-cache", forHTTPHeaderField: "Cache-Control") - - fetch(using: request, completion: completion) - } - fileprivate func fetch( - using request: URLRequest, + from url: URL, completion: @escaping (Result) -> Void ) where T: Decodable { + var request = URLRequest(url: url) + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + request.setValue("no-cache", forHTTPHeaderField: "Cache-Control") + let task = urlSession.dataTask(with: request) { (data, response, error) in if let error = error { diff --git a/Sources/FeaturevisorSDK/Instance+Refresh.swift b/Sources/FeaturevisorSDK/Instance+Refresh.swift index 935f199..29c1954 100644 --- a/Sources/FeaturevisorSDK/Instance+Refresh.swift +++ b/Sources/FeaturevisorSDK/Instance+Refresh.swift @@ -19,35 +19,38 @@ extension FeaturevisorInstance { statuses.refreshInProgress = true - try? fetchDatafileContent( - from: datafileUrl, - handleDatafileFetch: handleDatafileFetch - ) { [weak self] result in - guard let self else { - return - } + Task { [weak self] in + try? await self? + .fetchDatafileContent( + from: datafileUrl, + handleDatafileFetch: handleDatafileFetch + ) { [weak self] result in + guard let self else { + return + } - switch result { - case .success(let datafileContent): - let currentRevision = self.getRevision() - let newRevision = datafileContent.revision - let isNotSameRevision = currentRevision != newRevision + switch result { + case .success(let datafileContent): + let currentRevision = self.getRevision() + let newRevision = datafileContent.revision + let isNotSameRevision = currentRevision != newRevision - self.datafileReader = DatafileReader(datafileContent: datafileContent) - logger.info("refreshed datafile") + self.datafileReader = DatafileReader(datafileContent: datafileContent) + logger.info("refreshed datafile") - self.emitter.emit(.refresh) + self.emitter.emit(.refresh) - if isNotSameRevision { - self.emitter.emit(.update) - } + if isNotSameRevision { + self.emitter.emit(.update) + } - self.statuses.refreshInProgress = false + self.statuses.refreshInProgress = false - case .failure(let error): - self.logger.error("failed to refresh datafile", ["error": error]) - self.statuses.refreshInProgress = false - } + case .failure(let error): + self.logger.error("failed to refresh datafile", ["error": error]) + self.statuses.refreshInProgress = false + } + } } } diff --git a/Sources/FeaturevisorSDK/Instance.swift b/Sources/FeaturevisorSDK/Instance.swift index a590546..694ea41 100644 --- a/Sources/FeaturevisorSDK/Instance.swift +++ b/Sources/FeaturevisorSDK/Instance.swift @@ -4,7 +4,7 @@ import Foundation public typealias ConfigureBucketKey = (Feature, Context, BucketKey) -> BucketKey public typealias ConfigureBucketValue = (Feature, Context, BucketValue) -> BucketValue public typealias InterceptContext = (Context) -> Context -public typealias DatafileFetchHandler = (_ datafileUrl: String) -> Result +public typealias DatafileFetchHandler = (_ datafileURL: URL) async -> Result public struct Statuses { public var ready: Bool diff --git a/Tests/FeaturevisorSDKTests/InstanceTests.swift b/Tests/FeaturevisorSDKTests/InstanceTests.swift index 9406e81..2c150ed 100644 --- a/Tests/FeaturevisorSDKTests/InstanceTests.swift +++ b/Tests/FeaturevisorSDKTests/InstanceTests.swift @@ -1111,8 +1111,12 @@ class FeaturevisorInstanceTests: XCTestCase { func testHandleDatafileFetchReturnsValidResponse() { // GIVEN + let expectation = expectation(description: "datafile_error_response_expectation") var options = InstanceOptions.default - options.datafileUrl = "https://dazn.featurevisor.datafilecontent.com" + options.datafileUrl = "https://featurevisor.datafilecontent.com" + options.onReady = { _ in + expectation.fulfill() + } options.handleDatafileFetch = { _ in let datafileContent = DatafileContent( schemaVersion: "2", @@ -1136,15 +1140,17 @@ class FeaturevisorInstanceTests: XCTestCase { let sdk = try! createInstance(options: options) // THEN + waitForExpectations(timeout: 1) XCTAssertEqual(sdk.getRevision(), "6.6.6") } func testHandleDatafileFetchReturnErrorResponse() { // GIVEN + let expectation = expectation(description: "datafile_error_response_expectation") var wasDatafileContentFetchErrorThrown = false var options = InstanceOptions.default - options.datafileUrl = "https://dazn.featurevisor.datafilecontent.com" + options.datafileUrl = "https://featurevisor.datafilecontent.com" options.handleDatafileFetch = { _ in return .failure(FeaturevisorError.unparseableJSON(data: nil, errorMessage: "Error :(")) } @@ -1156,6 +1162,8 @@ class FeaturevisorInstanceTests: XCTestCase { if message.contains("Failed to fetch datafile") { wasDatafileContentFetchErrorThrown = true } + + expectation.fulfill() } options.datafile = DatafileContent( schemaVersion: "1", @@ -1166,9 +1174,10 @@ class FeaturevisorInstanceTests: XCTestCase { ) // WHEN - _ = try! createInstance(options: options) + let sdk = try! createInstance(options: options) // THEN + waitForExpectations(timeout: 1) XCTAssertTrue(wasDatafileContentFetchErrorThrown) }