Skip to content

Commit

Permalink
feat: SSE / real time flags support #32 (#67)
Browse files Browse the repository at this point in the history
* Update example app with the latest local SDK version

* Checkpoint commit, SSE connection up and running, need to implement the decoding and logic

* Attempt to decode the SSE data

* Initial implementation of the logic from the Kotlin implementation

* Add a SwiftUI view to the example app to show real-time updates to the flags

* Fix an issue with taking too many event stream updated-at times

* Swift lint and tidy up

* Add some unit tests for the SSEManager class and also change the reconnection logic to use a backoff timer

* Tidy and document SSEManager

* A bit of a tidy and swift linting

* Removed some commented-out code and put some back in

* Run swift format and update the swift format package

* Update FlagsmithClient/Tests/SSEManagerTests.swift

Co-authored-by: Matthew Elwell <[email protected]>

* Updated use of API Key -> env

* Remove the unnecessary Podfile.lock from the example app

---------

Co-authored-by: Matthew Elwell <[email protected]>
  • Loading branch information
gazreese and matthewelwell authored Oct 11, 2024
1 parent ba0fb2b commit c7330df
Show file tree
Hide file tree
Showing 24 changed files with 948 additions and 373 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,7 @@ Carthage/Build
# hence it is not needed unless you have added a package configuration file to your project
.swiftpm
.build

# This was causing confusion on PRs and is really just a record of the last time the example
# pods were re-built from scratch. It's not useful to keep in the repo.
Example/Podfile.lock
4 changes: 4 additions & 0 deletions Example/FlagsmithClient.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
7F76D20A2C9852510028470B /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F76D2092C9852510028470B /* SwiftUIView.swift */; };
C87CA15C7294632245730C64 /* Pods_FlagsmithClient_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9F7BEB767BE008D76723DD6 /* Pods_FlagsmithClient_Example.framework */; };
DAED1E94268DBF9100F91DBC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAED1E8C268DBF9100F91DBC /* ViewController.swift */; };
DAED1E95268DBF9100F91DBC /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = DAED1E8D268DBF9100F91DBC /* LaunchScreen.xib */; };
Expand All @@ -18,6 +19,7 @@
/* Begin PBXFileReference section */
5357A0EF6DA00FB7B1E84F8C /* Pods-FlagsmithClient_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlagsmithClient_Example.release.xcconfig"; path = "Target Support Files/Pods-FlagsmithClient_Example/Pods-FlagsmithClient_Example.release.xcconfig"; sourceTree = "<group>"; };
607FACD01AFB9204008FA782 /* FlagsmithClient_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FlagsmithClient_Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
7F76D2092C9852510028470B /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = "<group>"; };
9DFD4B33E10902546A38D90F /* Pods-FlagsmithClient_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-FlagsmithClient_Example.debug.xcconfig"; path = "Target Support Files/Pods-FlagsmithClient_Example/Pods-FlagsmithClient_Example.debug.xcconfig"; sourceTree = "<group>"; };
C4197E729B096AA93F72A537 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
C9F7BEB767BE008D76723DD6 /* Pods_FlagsmithClient_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_FlagsmithClient_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -96,6 +98,7 @@
DAED1E8F268DBF9100F91DBC /* Main.storyboard */,
DAED1E91268DBF9100F91DBC /* Images.xcassets */,
DAED1E92268DBF9100F91DBC /* AppDelegate.swift */,
7F76D2092C9852510028470B /* SwiftUIView.swift */,
);
path = FlagsmithClient;
sourceTree = "<group>";
Expand Down Expand Up @@ -220,6 +223,7 @@
files = (
DAED1E98268DBF9100F91DBC /* AppDelegate.swift in Sources */,
DAED1E94268DBF9100F91DBC /* ViewController.swift in Sources */,
7F76D20A2C9852510028470B /* SwiftUIView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1320"
LastUpgradeVersion = "1540"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
15 changes: 15 additions & 0 deletions Example/FlagsmithClient/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

import UIKit
import FlagsmithClient
#if canImport(SwiftUI)
import SwiftUI
#endif

func isSuccess<T, F>(_ result: Result<T, F>) -> Bool {
if case .success = result { return true } else { return false }
Expand Down Expand Up @@ -43,6 +46,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

// set analytics on or off
Flagsmith.shared.enableAnalytics = true

// Enable real time updates
Flagsmith.shared.enableRealtimeUpdates = true

// set the analytics flush period in seconds
Flagsmith.shared.analyticsFlushPeriod = 10
Expand All @@ -65,6 +71,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

// Flagsmith.shared.setTrait(Trait(key: "<my_key>", value: "<my_value>"), forIdentity: "<my_identity>") { (result) in print(result) }
// Flagsmith.shared.getIdentity("<my_key>") { (result) in print(result) }

// Launch SwiftUIView
if #available(iOS 13.0, *) {
let swiftUIView = SwiftUIView()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UIHostingController(rootView: swiftUIView)
window?.makeKeyAndVisible()
}

return true
}

Expand Down
3 changes: 0 additions & 3 deletions Example/FlagsmithClient/Base.lproj/LaunchScreen.xib
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@
<constraints>
<constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
<constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
<constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
<constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
<constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
<constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
Expand Down
68 changes: 68 additions & 0 deletions Example/FlagsmithClient/SwiftUIView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// SwiftUIView.swift
// FlagsmithClient_Example
//
// Created by Gareth Reese on 16/09/2024.
// Copyright © 2024 CocoaPods. All rights reserved.
//

#if canImport(SwiftUI)
import SwiftUI
#endif
import FlagsmithClient

@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *)
struct SwiftUIView: View {
@State private var flags: [Flag] = []
@State private var isLoading = true

let flagsmith = Flagsmith.shared

var body: some View {
VStack {
if isLoading {
ProgressView()
} else {
Text("Feature Flags")
.font(.title)
.padding()
List(flags, id: \.feature.name) { flag in
HStack {
Text("\(flag.feature.name): \(flag.value)")
Spacer()
Text("\(flag.enabled ? "" : "")")
}
}
}
}
.onAppear {
initializeFlagsmith()
subscribeToFlagUpdates()
}
}

func initializeFlagsmith() {
// Fetch initial flags
flagsmith.getFeatureFlags { result in
DispatchQueue.main.async {
switch result {
case .success(let fetchedFlags):
flags = fetchedFlags
case .failure(let error):
print("Error fetching flags: \(error)")
}
isLoading = false
}
}
}

func subscribeToFlagUpdates() {
Task {
for await updatedFlags in flagsmith.flagStream {
DispatchQueue.main.async {
flags = updatedFlags
}
}
}
}
}
16 changes: 0 additions & 16 deletions Example/Podfile.lock

This file was deleted.

8 changes: 4 additions & 4 deletions Example/Pods/Local Podspecs/FlagsmithClient.podspec.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Example/Pods/Manifest.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit c7330df

Please sign in to comment.