Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/main'
Browse files Browse the repository at this point in the history
  • Loading branch information
Shahroz16 committed Apr 24, 2024
2 parents 3b96fe7 + f6f3a7f commit d10e8a4
Show file tree
Hide file tree
Showing 28 changed files with 453 additions and 168 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ jobs:
needs: cancel_previous
runs-on: macos-14
steps:
- name: Install yeetd
run: |
wget https://github.com/biscuitehh/yeetd/releases/download/1.0/yeetd-normal.pkg
sudo installer -pkg yeetd-normal.pkg -target /
yeetd &
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "15.2"
Expand Down
14 changes: 10 additions & 4 deletions Examples/other_plugins/IDFACollection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,16 @@ class IDFACollection: Plugin {
extension IDFACollection: iOSLifecycle {
func applicationDidBecomeActive(application: UIApplication?) {
let status = ATTrackingManager.trackingAuthorizationStatus
if status == .notDetermined && !alreadyAsked {
// we don't know, so should ask the user.
alreadyAsked = true
askForPermission()

_alreadyAsked.withValue { alreadyAsked in
if status == .notDetermined && !alreadyAsked {
// we don't know, so should ask the user.
alreadyAsked = true
DispatchQueue.main.async { [weak self] in
guard let self else { return }
askForPermission()
}
}
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"pins" : [
{
"identity" : "jsonsafeencoder-swift",
"identity" : "jsonsafeencoding-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/segmentio/jsonsafeencoder-swift.git",
"location" : "https://github.com/segmentio/jsonsafeencoding-swift.git",
"state" : {
"revision" : "75ad40f07d4e0b938e3afb80811244d6b7acd4ba",
"version" : "1.0.0"
"revision" : "af6a8b360984085e36c6341b21ecb35c12f47ebd",
"version" : "2.0.0"
}
},
{
Expand Down
6 changes: 3 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ let package = Package(
platforms: [
.macOS("10.15"),
.iOS("13.0"),
.tvOS("11.0"),
.tvOS("13.0"),
.watchOS("7.1"),
.visionOS("1.0")
],
Expand All @@ -22,7 +22,7 @@ let package = Package(
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/segmentio/sovran-swift.git", .exact("1.1.1")),
.package(url: "https://github.com/segmentio/jsonsafeencoder-swift.git", .exact("1.0.1"))
.package(url: "https://github.com/segmentio/jsonsafeencoding-swift.git", .exact("2.0.0"))
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand All @@ -31,7 +31,7 @@ let package = Package(
name: "Segment",
dependencies: [
.product(name: "Sovran", package: "sovran-swift"),
.product(name: "JSONSafeEncoder", package: "jsonsafeencoder-swift")
.product(name: "JSONSafeEncoding", package: "jsonsafeencoding-swift")
],
resources: [.process("Resources")]),
.testTarget(
Expand Down
51 changes: 9 additions & 42 deletions Sources/Segment/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public class Analytics {

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

storage.analytics = self

Expand Down Expand Up @@ -228,56 +228,23 @@ extension Analytics {
/// called when flush has completed.
public func flush(completion: (() -> Void)? = nil) {
// only flush if we're enabled.
guard enabled == true else { return }

let flushGroup = DispatchGroup()
// gotta call enter at least once before we ask to be notified.
flushGroup.enter()
guard enabled == true else { completion?(); return }

let completionGroup = CompletionGroup(queue: configuration.values.flushQueue)
apply { plugin in
// we want to enter as soon as possible. waiting to do it from
// another queue just takes too long.
operatingMode.run(queue: configuration.values.flushQueue) {
completionGroup.add { group in
if let p = plugin as? FlushCompletion {
// flush handles the groups enter/leave calls
p.flush(group: flushGroup) { plugin in
// we don't really care about the plugin value .. yet.
}
p.flush(group: group)
} else if let p = plugin as? EventPlugin {
flushGroup.enter()
// we have no idea if this will be async or not, assume it's sync.
group.enter()
p.flush()
flushGroup.leave()
group.leave()
}
}
}

flushGroup.leave() // matches our initial enter().

// if we ARE in sync mode, we need to wait on the group.
// This effectively ends up being a `sync` operation.
if operatingMode == .synchronous {
flushGroup.wait()
// we need to call completion on our own since
// we skipped setting up notify. we don't need to do it on
// .main since we are in synchronous mode.
if let completion { completion() }
} else if operatingMode == .asynchronous {
// if we're not, flip over to our serial queue, tell it to wait on the flush
// group to complete if we have a completion to hit. Otherwise, no need to
// wait on completion.
if let completion {
// NOTE: DispatchGroup's `notify` method on linux ended up getting called
// before the tasks have actually completed, so we went with this instead.
OperatingMode.defaultQueue.async { [weak self] in
let timedOut = flushGroup.wait(timeout: .now() + 15 /*seconds*/)
if timedOut == .timedOut {
self?.log(message: "flush(completion:) timed out waiting for completion.")
}
completion()
//DispatchQueue.main.async { completion() }
}
}
completionGroup.run(mode: operatingMode) {
completion?()
}
}

Expand Down
21 changes: 20 additions & 1 deletion Sources/Segment/Configuration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,21 @@
//

import Foundation
import JSONSafeEncoder
import JSONSafeEncoding
#if os(Linux)
import FoundationNetworking
#endif

// MARK: - Custom AnonymousId generator
/// Conform to this protocol to generate your own AnonymousID
public protocol AnonymousIdGenerator: AnyObject, Codable {
/// Returns a new anonymousId. Segment still manages storage and retrieval of the
/// current anonymousId and will call this method when new id's are needed.
///
/// - Returns: A new anonymousId.
func newAnonymousId() -> String
}

// MARK: - Operating Mode
/// Specifies the operating mode/context
public enum OperatingMode {
Expand Down Expand Up @@ -56,6 +66,7 @@ public class Configuration {
var userAgent: String? = nil
var jsonNonConformingNumberStrategy: JSONSafeEncoder.NonConformingFloatEncodingStrategy = .zero
var storageMode: StorageMode = .disk
var anonymousIdGenerator: AnonymousIdGenerator = SegmentAnonymousId()
}

internal var values: Values
Expand Down Expand Up @@ -248,11 +259,19 @@ public extension Configuration {
return self
}

/// Specify the storage mode to use. The default is `.disk`.
@discardableResult
func storageMode(_ mode: StorageMode) -> Configuration {
values.storageMode = mode
return self
}

/// Specify a custom anonymousId generator. The default is and instance of `SegmentAnonymousId`.
@discardableResult
func anonymousIdGenerator(_ generator: AnonymousIdGenerator) -> Configuration {
values.anonymousIdGenerator = generator
return self
}
}

extension Analytics {
Expand Down
21 changes: 13 additions & 8 deletions Sources/Segment/Events.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ extension Analytics {
// and they need to write a middleware/enrichment now.
// the objc version should accomodate them if it's really needed.

/// Tracks an event performed by a user, including some additional event properties.
/// - Parameters:
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
/// - properties: Properties specific to the named event. For example, an event with
/// the name 'Purchased a Shirt' might have properties like revenue or size.
public func track<P: Codable>(name: String, properties: P?) {
do {
if let properties = properties {
Expand All @@ -29,6 +34,9 @@ extension Analytics {
}
}

/// Tracks an event performed by a user.
/// - Parameters:
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
public func track(name: String) {
track(name: name, properties: nil as TrackEvent?)
}
Expand Down Expand Up @@ -133,15 +141,12 @@ extension Analytics {
// MARK: - Untyped Event Signatures

extension Analytics {
/// Associate a user with their unique ID and record traits about them.
/// Tracks an event performed by a user, including some additional event properties.
/// - Parameters:
/// - userId: A database ID for this user. If you don't have a userId
/// but want to record traits, just pass traits into the event and they will be associated
/// with the anonymousId of that user. In the case when user logs out, make sure to
/// call ``reset()`` to clear the user's identity info. For more information on how we
/// generate the UUID and Apple's policies on IDs, see
/// https://segment.io/libraries/ios#ids
/// - properties: A dictionary of traits you know about the user. Things like: email, name, plan, etc.
/// - name: Name of the action, e.g., 'Purchased a T-Shirt'
/// - properties: A dictionary or properties specific to the named event.
/// For example, an event with the name 'Purchased a Shirt' might have properties
/// like revenue or size.
public func track(name: String, properties: [String: Any]? = nil) {
var props: JSON? = nil
if let properties = properties {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Segment/ObjC/ObjCAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#if !os(Linux)

import Foundation
import JSONSafeEncoder
import JSONSafeEncoding

// MARK: - ObjC Compatibility

Expand Down
2 changes: 1 addition & 1 deletion Sources/Segment/ObjC/ObjCConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#if !os(Linux)

import Foundation
import JSONSafeEncoder
import JSONSafeEncoding

@objc(SEGConfiguration)
public class ObjCConfiguration: NSObject {
Expand Down
2 changes: 1 addition & 1 deletion Sources/Segment/Plugins.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public protocol VersionedPlugin {
}

public protocol FlushCompletion {
func flush(group: DispatchGroup, completion: @escaping (DestinationPlugin) -> Void)
func flush(group: DispatchGroup)
}

// For internal platform-specific bits
Expand Down
6 changes: 6 additions & 0 deletions Sources/Segment/Plugins/Platforms/Vendors/AppleUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ internal class iOSVendorSystem: VendorSystem {
// BKS: It was discovered that on some platforms there can be a delay in retrieval.
// It has to be fetched on the main thread, so we've spun it off
// async and cache it when it comes back.
// Note that due to how the `@Atomic` wrapper works, this boolean check may pass twice or more
// times before the value is updated, fetching the user agent multiple times as the result.
// This is not a big deal as the `userAgent` value is not expected to change often.
if Self.asyncUserAgent == nil {
DispatchQueue.main.async {
Self.asyncUserAgent = WKWebView().value(forKey: "userAgent") as? String
Expand Down Expand Up @@ -248,6 +251,9 @@ internal class MacOSVendorSystem: VendorSystem {
// BKS: It was discovered that on some platforms there can be a delay in retrieval.
// It has to be fetched on the main thread, so we've spun it off
// async and cache it when it comes back.
// Note that due to how the `@Atomic` wrapper works, this boolean check may pass twice or more
// times before the value is updated, fetching the user agent multiple times as the result.
// This is not a big deal as the `userAgent` value is not expected to change often.
if Self.asyncUserAgent == nil {
DispatchQueue.main.async {
Self.asyncUserAgent = WKWebView().value(forKey: "userAgent") as? String
Expand Down
Loading

0 comments on commit d10e8a4

Please sign in to comment.