Skip to content

Commit

Permalink
Add Windows support (#348)
Browse files Browse the repository at this point in the history
* Everything compiles

* Remove SSH agent

* Add windows vendor and tests

* Update windows ci and leave comment

* Disable testing and leave comment

* Use spaces

* Update json encoder version

* Make `shared()` public.

* disable windows test suite temporarily

* Updated tests for windows compat

* another test update

* turn win test runner back on

* try dynamic lib on windows.

* try a different build runner

* try some settings-fu

* and again ...

* disable tests for Windows.

* Added link to issue.

* Update gitignore.

---------

Co-authored-by: Brian Michel <[email protected]>
Co-authored-by: Alan <[email protected]>
  • Loading branch information
3 people authored May 16, 2024
1 parent 944c3c4 commit 44cab1e
Show file tree
Hide file tree
Showing 25 changed files with 633 additions and 519 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@ jobs:
- name: Run tests
run: swift test --enable-test-discovery

build_and_test_spm_windows:
needs: cancel_previous
runs-on: windows-latest
steps:
- uses: SwiftyLab/setup-swift@latest
with:
swift-version: "5.10"
- uses: actions/checkout@v2
- name: Build
run: swift build
#
# Disable tests right now. There's an SPM issue where link errors generate
# a bad exit code even though the tests run/work properly.
#
# See: https://forums.swift.org/t/linker-warnings-on-windows-with-swift-argument-parser/71443/2
#
# - name: Run tests
# run: swift test --enable-test-discovery

build_and_test_ios:
needs: cancel_previous
runs-on: macos-14
Expand Down
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,13 @@ iOSInjectionProject/
Package.resolved
*.xcuserdatad
/.swiftpm/xcode/xcshareddata

# XCFramework
Segment-Package_XCFramework
*.zip
Segment.xcframework.zip
Segment.xcframework
XCFrameworkOutput
*.sha256
.fleet
.idea
.vscode
.editorconfig
5 changes: 2 additions & 3 deletions Examples/other_plugins/NotificationTracking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@

// MARK: Common

#if !os(Linux) && !os(macOS)
#if !os(Linux) && !os(macOS) && !os(Windows)

import Foundation
import Segment

class NotificationTracking: Plugin {
var type: PluginType = .utility
weak var analytics: Analytics?

func trackNotification(_ properties: [String: Codable], fromLaunch launch: Bool) {
if launch {
analytics?.track(name: "Push Notification Tapped", properties: properties)
Expand Down Expand Up @@ -95,4 +95,3 @@ extension NotificationTracking: iOSLifecycle {
}

#endif

4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
{
"identity" : "sovran-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/segmentio/Sovran-Swift.git",
"location" : "https://github.com/segmentio/sovran-swift.git",
"state" : {
"revision" : "a342b905f6baa64499cabdf61ccc185ec476b7b2",
"version" : "1.1.1"
}
}
],
"version" : 2
}
}
6 changes: 4 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ let package = Package(
.product(name: "Sovran", package: "sovran-swift"),
.product(name: "JSONSafeEncoding", package: "jsonsafeencoding-swift")
],
resources: [.process("Resources")]),
resources: [.process("Resources")]
),
.testTarget(
name: "Segment-Tests",
dependencies: ["Segment"]),
dependencies: ["Segment"]
),
]
)
78 changes: 39 additions & 39 deletions Sources/Segment/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,23 @@ public class Analytics {
}
internal var store: Store
internal var storage: Storage

/// Enabled/disables debug logging to trace your data going through the SDK.
public static var debugLogsEnabled = false

public var timeline: Timeline

static internal let deadInstance = "DEADINSTANCE"
static internal weak var firstInstance: Analytics? = nil

@Atomic static internal var activeWriteKeys = [String]()

/**
This method isn't a traditional singleton implementation. It's provided here
to ease migration from analytics-ios to analytics-swift. Rather than return a
singleton, it returns the first instance of Analytics created, OR an instance
who's writekey is "DEADINSTANCE".

In the case of a dead instance, an assert will be thrown when in DEBUG builds to
assist developers in knowning that `shared()` is being called too soon.
*/
Expand All @@ -45,16 +46,16 @@ public class Analytics {
return a
}
}

#if DEBUG
if isUnitTesting == false {
assert(true == false, "An instance of Analytice does not exist!")
}
#endif

return Analytics(configuration: Configuration(writeKey: deadInstance))
}

/// Initialize this instance of Analytics with a given configuration setup.
/// - Parameters:
/// - configuration: The configuration to use
Expand All @@ -75,40 +76,40 @@ public class Analytics {
operatingMode: configuration.values.operatingMode
)
timeline = Timeline()

// provide our default state
store.provide(state: System.defaultState(configuration: configuration, from: storage))
store.provide(state: UserInfo.defaultState(from: storage, anonIdGenerator: configuration.values.anonymousIdGenerator))

storage.analytics = self

checkSharedInstance()

// Get everything running
platformStartup()
}

deinit {
Self.removeActiveWriteKey(configuration.values.writeKey)
}

internal func process<E: RawEvent>(incomingEvent: E) {
guard enabled == true else { return }
let event = incomingEvent.applyRawEventData(store: store)

_ = timeline.process(incomingEvent: event)

let flushPolicies = configuration.values.flushPolicies
for policy in flushPolicies {
policy.updateState(event: event)

if (policy.shouldFlush() == true) {
flush()
policy.reset()
}
}
}

/// Process a raw event through the system. Useful when one needs to queue and replay events at a later time.
/// - Parameters:
/// - event: An event conforming to RawEvent that will be processed.
Expand Down Expand Up @@ -160,20 +161,20 @@ extension Analytics {
}
return ""
}

/// Returns the userId that was specified in the last identify call.
public var userId: String? {
if let userInfo: UserInfo = store.currentState() {
return userInfo.userId
}
return nil
}

/// Returns the current operating mode this instance was given.
public var operatingMode: OperatingMode {
return configuration.values.operatingMode
}

/// Adjusts the flush interval post configuration.
public var flushInterval: TimeInterval {
get {
Expand All @@ -186,7 +187,7 @@ extension Analytics {
}
}
}

/// Adjusts the flush-at count post configuration.
public var flushAt: Int {
get {
Expand All @@ -199,30 +200,30 @@ extension Analytics {
}
}
}

/// Returns a list of currently active flush policies.
public var flushPolicies: [FlushPolicy] {
get {
configuration.values.flushPolicies
}
}

/// Returns the traits that were specified in the last identify call.
public func traits<T: Codable>() -> T? {
if let userInfo: UserInfo = store.currentState() {
return userInfo.traits?.codableValue()
}
return nil
}

/// Returns the traits that were specified in the last identify call, as a dictionary.
public func traits() -> [String: Any]? {
if let userInfo: UserInfo = store.currentState() {
return userInfo.traits?.dictionaryValue
}
return nil
}

/// Tells this instance of Analytics to flush any queued events up to Segment.com. This command will also
/// be sent to each plugin present in the system. A completion handler can be optionally given and will be
/// called when flush has completed.
Expand All @@ -247,7 +248,7 @@ extension Analytics {
completion?()
}
}

/// Resets this instance of Analytics to a clean slate. Traits, UserID's, anonymousId, etc are all cleared or reset. This
/// command will also be sent to each plugin present in the system.
public func reset() {
Expand All @@ -258,13 +259,13 @@ extension Analytics {
}
}
}

/// Retrieve the version of this library in use.
/// - Returns: A string representing the version in "BREAKING.FEATURE.FIX" format.
public func version() -> String {
return Analytics.version()
}

/// Retrieve the version of this library in use.
/// - Returns: A string representing the version in "BREAKING.FEATURE.FIX" format.
public static func version() -> String {
Expand All @@ -282,7 +283,7 @@ extension Analytics {
}
return settings
}

/// Manually enable a destination plugin. This is useful when a given DestinationPlugin doesn't have any Segment tie-ins at all.
/// This will allow the destination to be processed in the same way within this library.
/// - Parameters:
Expand All @@ -306,25 +307,25 @@ extension Analytics {

return storage.dataStore.hasData
}

/// Provides a list of finished, but unsent events.
public var pendingUploads: [URL]? {
return storage.read(Storage.Constants.events)?.dataFiles
}

/// Purge all pending event upload files.
public func purgeStorage() {
storage.dataStore.reset()
}

/// Purge a single event upload file.
public func purgeStorage(fileURL: URL) {
guard let dataFiles = storage.read(Storage.Constants.events)?.dataFiles else { return }
if dataFiles.contains(fileURL) {
try? FileManager.default.removeItem(at: fileURL)
}
}

/// Wait until the Analytics object has completed startup.
/// This method is primarily useful for command line utilities where
/// it's desirable to wait until the system is up and running
Expand All @@ -344,7 +345,7 @@ extension Analytics {
Call openURL as needed or when instructed to by either UIApplicationDelegate or UISceneDelegate.
This is necessary to track URL referrers across events. This method will also iterate
any plugins that are watching for openURL events.

Example:
```
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
Expand All @@ -359,12 +360,12 @@ extension Analytics {
guard let dict = jsonProperties.dictionaryValue else { return }
openURL(url, options: dict)
}

/**
Call openURL as needed or when instructed to by either UIApplicationDelegate or UISceneDelegate.
This is necessary to track URL referrers across events. This method will also iterate
any plugins that are watching for openURL events.

Example:
```
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
Expand All @@ -375,14 +376,14 @@ extension Analytics {
*/
public func openURL(_ url: URL, options: [String: Any] = [:]) {
store.dispatch(action: UserInfo.SetReferrerAction(url: url))

// let any conforming plugins know
apply { plugin in
if let p = plugin as? OpeningURLs {
p.openURL(url, options: options)
}
}

var jsonProperties: JSON? = nil
if let json = try? JSON(options) {
jsonProperties = json
Expand Down Expand Up @@ -411,7 +412,7 @@ extension Analytics {
Self.firstInstance = self
}
}

/// Determines if an instance is dead.
internal var isDead: Bool {
return configuration.values.writeKey == Self.deadInstance
Expand Down Expand Up @@ -455,4 +456,3 @@ extension OperatingMode {
}
}
}

Loading

0 comments on commit 44cab1e

Please sign in to comment.