Skip to content

Commit

Permalink
resolve merge conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
alanjcharles committed Feb 27, 2024
2 parents 7097903 + 33f07bd commit a970757
Show file tree
Hide file tree
Showing 18 changed files with 289 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/segmentio/sovran-swift.git",
"state" : {
"revision" : "64f3b5150c282a34af4578188dce2fd597e600e3",
"version" : "1.1.0"
"revision" : "a342b905f6baa64499cabdf61ccc185ec476b7b2",
"version" : "1.1.1"
}
}
],
Expand Down
5 changes: 5 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ let package = Package(
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
<<<<<<< HEAD
.package(url: "https://github.com/segmentio/sovran-swift.git", from: "1.1.0"),
.package(url: "https://github.com/segmentio/jsonsafeencoder-swift.git", from: "1.0.1")
=======
.package(url: "https://github.com/segmentio/sovran-swift.git", from: "1.1.1"),
.package(url: "https://github.com/segmentio/jsonsafeencoder-swift.git", from: "1.0.0")
>>>>>>> main
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
5 changes: 5 additions & 0 deletions [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ let package = Package(
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
<<<<<<< HEAD
.package(url: "https://github.com/segmentio/sovran-swift.git", from: "1.1.0"),
.package(url: "https://github.com/segmentio/jsonsafeencoder-swift.git", from: "1.0.1")
=======
.package(url: "https://github.com/segmentio/sovran-swift.git", from: "1.1.1"),
.package(url: "https://github.com/segmentio/jsonsafeencoder-swift.git", from: "1.0.0")
>>>>>>> main
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
![](https://github.com/segmentio/analytics-swift/actions/workflows/swift.yml/badge.svg)
![](https://img.shields.io/github/license/segmentio/analytics-swift)

### 🎉 Flagship 🎉
This library is one of Segment’s most popular Flagship libraries. It is actively maintained by Segment, benefitting from new feature releases and ongoing support.

The hassle-free way to add Segment analytics to your Swift app (iOS/tvOS/visionOS/watchOS/macOS/Linux/iPadOS). Analytics helps you measure your users, product, and business. It unlocks insights into your app's funnel, core business metrics, and whether you have product-market fit.

## How to get started
Expand Down
38 changes: 38 additions & 0 deletions Sources/Segment/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ public class Analytics {

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

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

>>>>>>> main
/**
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
Expand Down Expand Up @@ -58,6 +63,12 @@ public class Analytics {
/// - Parameters:
/// - configuration: The configuration to use
public init(configuration: Configuration) {
if Self.isActiveWriteKey(configuration.values.writeKey) {
fatalError("Cannot initialize multiple instances of Analytics with the same write key")
} else {
Self.addActiveWriteKey(configuration.values.writeKey)
}

store = Store()
storage = Storage(store: self.store, writeKey: configuration.values.writeKey)
timeline = Timeline()
Expand All @@ -73,7 +84,15 @@ public class Analytics {
// Get everything running
platformStartup()
}
<<<<<<< HEAD

=======

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

>>>>>>> main
internal func process<E: RawEvent>(incomingEvent: E) {
guard enabled == true else { return }
let event = incomingEvent.applyRawEventData(store: store)
Expand Down Expand Up @@ -428,11 +447,30 @@ extension Analytics {
Self.firstInstance = self
}
}
<<<<<<< HEAD

=======

>>>>>>> main
/// Determines if an instance is dead.
internal var isDead: Bool {
return configuration.values.writeKey == Self.deadInstance
}

/// Manage active writekeys. It's wrapped in @atomic
internal static func isActiveWriteKey(_ writeKey: String) -> Bool {
Self.activeWriteKeys.contains(writeKey)
}

internal static func addActiveWriteKey(_ writeKey: String) {
Self.activeWriteKeys.append(writeKey)
}

internal static func removeActiveWriteKey(_ writeKey: String) {
Self.activeWriteKeys.removeAll { key in
writeKey == key
}
}
}

// MARK: Operating mode based scheduling
Expand Down
4 changes: 2 additions & 2 deletions Sources/Segment/ObjC/ObjCPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public class ObjCSegmentMixpanel: NSObject, ObjCPlugin, ObjCPluginShim {
@objc(SEGEventPlugin)
public class ObjCEventPlugin: NSObject, EventPlugin, ObjCPlugin {
public var type: PluginType = .enrichment
public var analytics: Analytics? = nil

public weak var analytics: Analytics? = nil
@objc(executeEvent:)
public func execute(event: ObjCRawEvent?) -> ObjCRawEvent? {
#if DEBUG
Expand Down
70 changes: 69 additions & 1 deletion Sources/Segment/Plugins/Platforms/Vendors/AppleUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -341,14 +341,66 @@ extension ConnectionStatus {
#else
self = .online(.wifi)
#endif

} else {
self = .offline
}
}
}


// MARK: -- Connection Status stuff

internal class ConnectionMonitor {
private var timer: QueueTimer? = nil

static let shared = ConnectionMonitor()

@Atomic var connectionStatus: ConnectionStatus = .unknown

init() {
self.timer = QueueTimer(interval: 300, immediate: true) { [weak self] in
guard let self else { return }
self.check()
}
}

internal func check() {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)

guard let defaultRouteReachability = (withUnsafePointer(to: &zeroAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) { zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}) else {
connectionStatus = .unknown
return
}

var flags : SCNetworkReachabilityFlags = []
if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
connectionStatus = .unknown
return
}

connectionStatus = ConnectionStatus(reachabilityFlags: flags)
}
}

internal func connectionStatus() -> ConnectionStatus {
return ConnectionMonitor.shared.connectionStatus
}

/*
/* 5-minute timer to check connection status. Checking this for
every event that comes through seems like overkill. */

private var __segment_connectionStatus: ConnectionStatus = .unknown
private var __segment_connectionStatusTimer: QueueTimer? = nil
private var __segment_connectionStatusLock = NSLock()

internal func __segment_connectionStatusCheck() -> ConnectionStatus {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
Expand All @@ -369,4 +421,20 @@ internal func connectionStatus() -> ConnectionStatus {
return ConnectionStatus(reachabilityFlags: flags)
}

internal func connectionStatus() -> ConnectionStatus {
// the locking may seem like overkill since we're updating it in a queue
// however, it is necessary since we're polling. :(
if __segment_connectionStatusTimer == nil {
__segment_connectionStatusTimer = QueueTimer(interval: 300, immediate: true) {
__segment_connectionStatusLock.lock()
defer { __segment_connectionStatusLock.unlock() }
__segment_connectionStatus = __segment_connectionStatusCheck()
}
}

__segment_connectionStatusLock.lock()
defer { __segment_connectionStatusLock.unlock() }
return __segment_connectionStatus
}
*/
#endif
6 changes: 6 additions & 0 deletions Sources/Segment/Plugins/SegmentDestination.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,12 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion
case .success(_):
storage.remove(file: url)
self.cleanupUploads()

// we don't want to retry events in a given batch when a 400
// response for malformed JSON is returned
case .failure(Segment.HTTPClientErrors.statusCode(code: 400)):
storage.remove(file: url)
self.cleanupUploads()
default:
break
}
Expand Down
24 changes: 22 additions & 2 deletions Sources/Segment/Utilities/JSON.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,37 @@ import Foundation
import JSONSafeEncoder

extension JSONDecoder {
enum JSONDecodingError: Error {
case couldNotDecodeDate(String)
}

static var `default`: JSONDecoder {
let d = JSONDecoder()
d.dateDecodingStrategy = .formatted(DateFormatter.iso8601)

d.dateDecodingStrategy = .custom({ decoder throws -> Date in
let stringDate = try decoder.singleValueContainer().decode(String.self)

guard let date = stringDate.iso8601() else {
throw JSONDecodingError.couldNotDecodeDate(stringDate)
}

return date
})

return d
}
}

extension JSONSafeEncoder {
static var `default`: JSONSafeEncoder {
let e = JSONSafeEncoder()
e.dateEncodingStrategy = .formatted(DateFormatter.iso8601)

e.dateEncodingStrategy = .custom({ date, encoder in
let stringDate = date.iso8601()
var container = encoder.singleValueContainer()
try container.encode(stringDate)
})

e.nonConformingFloatEncodingStrategy = JSON.jsonNonConformingNumberStrategy
return e
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ public class IntervalBasedFlushPolicy: FlushPolicy,
self.analytics?.store.subscribe(self, initialState: true) { [weak self] (state: System) in
guard let self = self else { return }
guard let a = self.analytics else { return }
self.flushTimer = QueueTimer(interval: a.configuration.values.flushInterval) { [weak self] in
guard let system: System = a.store.currentState() else { return }
self.flushTimer = QueueTimer(interval: system.configuration.values.flushInterval) { [weak self] in
self?.analytics?.flush()
}
}
Expand Down
10 changes: 7 additions & 3 deletions Sources/Segment/Utilities/QueueTimer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,22 @@ internal class QueueTimer {

static var timers = [QueueTimer]()

static func schedule(interval: TimeInterval, queue: DispatchQueue = .main, handler: @escaping () -> Void) {
static func schedule(interval: TimeInterval, immediate: Bool = false, queue: DispatchQueue = .main, handler: @escaping () -> Void) {
let timer = QueueTimer(interval: interval, queue: queue, handler: handler)
Self.timers.append(timer)
}

init(interval: TimeInterval, queue: DispatchQueue = .main, handler: @escaping () -> Void) {
init(interval: TimeInterval, immediate: Bool = false, queue: DispatchQueue = .main, handler: @escaping () -> Void) {
self.interval = interval
self.queue = queue
self.handler = handler

timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
timer.schedule(deadline: .now() + self.interval, repeating: self.interval)
if immediate {
timer.schedule(deadline: .now(), repeating: self.interval)
} else {
timer.schedule(deadline: .now() + self.interval, repeating: self.interval)
}
timer.setEventHandler { [weak self] in
self?.handler()
}
Expand Down
11 changes: 0 additions & 11 deletions Sources/Segment/Utilities/iso8601.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,3 @@ internal extension String {
return SegmentISO8601DateFormatter.shared.date(from: self)
}
}

extension DateFormatter {
static let iso8601: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
formatter.calendar = Calendar(identifier: .iso8601)
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter
}()
}
2 changes: 1 addition & 1 deletion Sources/Segment/Version.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
// Use release.sh's automation.

// BREAKING.FEATURE.FIX
internal let __segment_version = "1.5.0"
internal let __segment_version = "1.5.5"
Loading

0 comments on commit a970757

Please sign in to comment.