Skip to content

Commit

Permalink
Release 6.6.0 (#148)
Browse files Browse the repository at this point in the history
Co-authored-by: embrace-ci <embrace-ci@users.noreply.github.com>
Co-authored-by: Ignacio Tischelman <114942102+NachoEmbrace@users.noreply.github.com>
  • Loading branch information
3 people authored Dec 11, 2024
1 parent a3a7fd0 commit 69ed424
Show file tree
Hide file tree
Showing 24 changed files with 279 additions and 91 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ jobs:
run: |
eval "$(~/.local/bin/mise activate bash)" >> ~/.bashrc
echo "$PATH"
mise doctor
# mise doctor
./bin/build_xcframeworks.sh
# TODO: Finish this step
Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## 6.6.0
*Dec 12th, 2024*
* Features
* Added new instrumentation for the `ViewCaptureService`. Can be enabled through `ViewCaptureService.Options.instrumentFirstRender`.
* Added url blacklist for the `URLSessionCaptureService`. Can be configured through `URLSessionCaptureService.Options.ignoredURLs`.
* Added the ability to auto terminate spans if the session ends while the span is still open.
* Updated the OpenTelemetry dependency to v1.12.1 which fixes some concurrency related crashes.
* Improved logic around Embrace data uploads and retries.
* Deprecated `Span.markAsKeySpan()`.
* Fixes
* Fixed the remote config parse sometimes failing.
* Fixed the remote config cache not working properly.
* Fixed crash logs sometimes not containing the session properties.
* Fixed keychain related crash/hang during startup.
* Fixed issues with the `WebViewCaptureService` that could lead to a crash.
* Fixed issue with the `URLSessionCaptureService` when dealing with `URLSessionDelegate` objects in Objective-C responding to methods without conforming to specific protocols.

## 6.5.2
*Nov 14th, 2024*
* Features
Expand Down
2 changes: 1 addition & 1 deletion EmbraceIO.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |spec|
spec.name = "EmbraceIO"
spec.version = "6.5.2"
spec.version = "6.6.0"
spec.summary = "Visibility into your users that you didn't have before."
spec.description = <<-DESC
Embrace is the only performance monitoring platform focused solely on mobile. We are built
Expand Down
2 changes: 1 addition & 1 deletion Examples/BrandGame/BrandGame/Embrace/App+Embrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ extension BrandGameApp {

private var otelExport: OpenTelemetryExport {
OpenTelemetryExport(
spanExporter: StdoutExporter(isDebug: true),
spanExporter: StdoutSpanExporter(isDebug: true),
logExporter: StdoutLogExporter(isDebug: true)
)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/EmbraceCommonInternal/EmbraceMeta.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@
// Do not edit this file manually

public class EmbraceMeta {
public static let sdkVersion = "6.5.2"
public static let sdkVersion = "6.6.0"
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import EmbraceConfiguration
/// Remote config uses the Embrace Config Service to request config values
public class RemoteConfig {

let logger: InternalLogger

// config requests
@ThreadSafe var payload: RemoteConfigPayload
let fetcher: RemoteConfigFetcher
Expand All @@ -19,6 +21,8 @@ public class RemoteConfig {

@ThreadSafe private(set) var updating = false

let cacheURL: URL?

public convenience init(
options: RemoteConfig.Options,
payload: RemoteConfigPayload = RemoteConfigPayload(),
Expand All @@ -38,6 +42,42 @@ public class RemoteConfig {
self.payload = payload
self.fetcher = fetcher
self.deviceIdHexValue = options.deviceId.intValue(digitCount: Self.deviceIdUsedDigits)
self.logger = logger

if let url = options.cacheLocation {
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
self.cacheURL = options.cacheLocation?.appendingPathComponent("cache")
loadFromCache()
} else {
self.cacheURL = nil
}
}

func loadFromCache() {
guard let url = cacheURL,
FileManager.default.fileExists(atPath: url.path) else {
return
}

do {
let data = try Data(contentsOf: url)
payload = try JSONDecoder().decode(RemoteConfigPayload.self, from: data)
} catch {
logger.error("Error loading cached remote config!")
}
}

func saveToCache(_ data: Data?) {
guard let url = cacheURL,
let data = data else {
return
}

do {
try data.write(to: url, options: .atomic)
} catch {
logger.warning("Error saving remote config cache!")
}
}
}

Expand Down Expand Up @@ -67,7 +107,7 @@ extension RemoteConfig: EmbraceConfigurable {
}

updating = true
fetcher.fetch { [weak self] newPayload in
fetcher.fetch { [weak self] newPayload, data in
defer { self?.updating = false }
guard let strongSelf = self else {
completion(false, nil)
Expand All @@ -82,6 +122,8 @@ extension RemoteConfig: EmbraceConfigurable {
let didUpdate = strongSelf.payload != newPayload
strongSelf.payload = newPayload

strongSelf.saveToCache(data)

completion(didUpdate, nil)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public extension RemoteConfig {
let appVersion: String
let userAgent: String

let cacheLocation: URL?

let urlSessionConfiguration: URLSessionConfiguration

public init(
Expand All @@ -28,6 +30,7 @@ public extension RemoteConfig {
sdkVersion: String,
appVersion: String,
userAgent: String,
cacheLocation: URL?,
urlSessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default
) {
self.apiBaseUrl = apiBaseUrl
Expand All @@ -38,6 +41,7 @@ public extension RemoteConfig {
self.sdkVersion = sdkVersion
self.appVersion = appVersion
self.userAgent = userAgent
self.cacheLocation = cacheLocation
self.urlSessionConfiguration = urlSessionConfiguration
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ class RemoteConfigFetcher {
)
}

func fetch(completion: @escaping (RemoteConfigPayload?) -> Void) {
func fetch(completion: @escaping (RemoteConfigPayload?, Data?) -> Void) {
guard let request = newRequest() else {
completion(nil)
completion(nil, nil)
return
}

Expand All @@ -38,19 +38,19 @@ class RemoteConfigFetcher {

guard let data = data, error == nil else {
self?.logger.error("Error fetching remote config:\n\(String(describing: error?.localizedDescription))")
completion(nil)
completion(nil, nil)
return
}

guard let httpResponse = response as? HTTPURLResponse else {
self?.logger.error("Error fetching remote config - Invalid response:\n\(String(describing: response?.description))")
completion(nil)
completion(nil, nil)
return
}

guard httpResponse.statusCode >= 200 && httpResponse.statusCode < 300 else {
self?.logger.error("Error fetching remote config - Invalid response:\n\(httpResponse.description))")
completion(nil)
completion(nil, nil)
return
}

Expand All @@ -59,11 +59,11 @@ class RemoteConfigFetcher {
let payload = try JSONDecoder().decode(RemoteConfigPayload.self, from: data)

self?.logger.info("Successfully fetched remote config")
completion(payload)
completion(payload, data)
} catch {
self?.logger.error("Error decoding remote config:\n\(error.localizedDescription)")
// if a decoding issue happens, instead of returning `nil`, we provide a default `RemoteConfigPayload`
completion(RemoteConfigPayload())
completion(RemoteConfigPayload(), nil)
}
}

Expand Down
51 changes: 29 additions & 22 deletions Sources/EmbraceCore/Capture/UX/View/UIViewControllerHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,16 @@ class UIViewControllerHandler {
@ThreadSafe var alreadyFinishedUiReadyIds: Set<String> = []

init() {
NotificationCenter.default.addObserver(
Embrace.notificationCenter.addObserver(
self,
selector: #selector(appDidEnterBackground),
name: UIApplication.didEnterBackgroundNotification,
selector: #selector(foregroundSessionDidEnd),
name: .embraceForegroundSessionDidEnd,
object: nil
)
}

deinit {
NotificationCenter.default.removeObserver(self)
Embrace.notificationCenter.removeObserver(self)
}

func parentSpan(for vc: UIViewController) -> Span? {
Expand All @@ -53,16 +53,18 @@ class UIViewControllerHandler {
return parentSpans[id]
}

@objc func appDidEnterBackground() {
@objc func foregroundSessionDidEnd(_ notification: Notification? = nil) {
let now = notification?.object as? Date ?? Date()

// end all parent spans and visibility spans if the app enters the background
// also clear all the cached spans
queue.async {
for span in self.visibilitySpans.values {
span.end()
span.end(time: now)
}

for id in self.parentSpans.keys {
self.forcefullyEndSpans(id: id)
self.forcefullyEndSpans(id: id, time: now)
}

self.parentSpans.removeAll()
Expand Down Expand Up @@ -206,8 +208,10 @@ class UIViewControllerHandler {
}

// end view did appear span
let now = Date()

if let span = self.viewDidAppearSpans.removeValue(forKey: id) {
span.end()
span.end(time: now)
}

guard let parentSpan = self.parentSpans[id] else {
Expand All @@ -216,7 +220,7 @@ class UIViewControllerHandler {

// end time to first render span
if parentSpan.isTimeToFirstRender {
parentSpan.end()
parentSpan.end(time: now)
self.clear(id: id, vc: vc)

// generate ui ready span
Expand All @@ -231,8 +235,8 @@ class UIViewControllerHandler {
// if the view controller was already flagged as ready to interact
// we end the spans right away
if self.alreadyFinishedUiReadyIds.contains(id) {
span.end()
parentSpan.end()
span.end(time: now)
parentSpan.end(time: now)

self.clear(id: id, vc: vc)

Expand All @@ -250,13 +254,16 @@ class UIViewControllerHandler {
return
}

let now = Date()

// end visibility span
if let span = self.visibilitySpans[id] {
span.end()
span.end(time: now)
self.visibilitySpans[id] = nil
}

// force end all spans
self.forcefullyEndSpans(id: id)
self.forcefullyEndSpans(id: id, time: now)
}
}

Expand All @@ -271,8 +278,9 @@ class UIViewControllerHandler {
// if we have a ui ready span it means that viewDidAppear already happened
// in this case we close the spans
if let span = self.uiReadySpans[id] {
span.end()
parentSpan.end()
let now = Date()
span.end(time: now)
parentSpan.end(time: now)
self.clear(id: id, vc: vc)

// otherwise it means the view is still loading, in this case we flag
Expand All @@ -284,26 +292,26 @@ class UIViewControllerHandler {
}
}

private func forcefullyEndSpans(id: String) {
private func forcefullyEndSpans(id: String, time: Date) {

if let viewDidLoadSpan = self.viewDidLoadSpans[id] {
viewDidLoadSpan.end(errorCode: .userAbandon)
viewDidLoadSpan.end(errorCode: .userAbandon, time: time)
}

if let viewWillAppearSpan = self.viewWillAppearSpans[id] {
viewWillAppearSpan.end(errorCode: .userAbandon)
viewWillAppearSpan.end(errorCode: .userAbandon, time: time)
}

if let viewDidAppearSpan = self.viewDidAppearSpans[id] {
viewDidAppearSpan.end(errorCode: .userAbandon)
viewDidAppearSpan.end(errorCode: .userAbandon, time: time)
}

if let uiReadySpan = self.uiReadySpans[id] {
uiReadySpan.end(errorCode: .userAbandon)
uiReadySpan.end(errorCode: .userAbandon, time: time)
}

if let parentSpan = self.parentSpans[id] {
parentSpan.end(errorCode: .userAbandon)
parentSpan.end(errorCode: .userAbandon, time: time)
}

self.clear(id: id)
Expand Down Expand Up @@ -338,7 +346,6 @@ class UIViewControllerHandler {
self.viewDidLoadSpans[id] = nil
self.viewWillAppearSpans[id] = nil
self.viewDidAppearSpans[id] = nil
self.visibilitySpans[id] = nil
self.uiReadySpans[id] = nil
self.alreadyFinishedUiReadyIds.remove(id)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ extension ViewCaptureService {
}

@objc public convenience override init() {
self.init(instrumentVisibility: true, instrumentFirstRender: true)
self.init(instrumentVisibility: true, instrumentFirstRender: false)
}
}
}
14 changes: 13 additions & 1 deletion Sources/EmbraceCore/Embrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ To start the SDK you first need to configure it using an `Embrace.Options` insta
/// Returns the current `MetadataHandler` used to store resources and session properties.
@objc public let metadata: MetadataHandler

var isSDKEnabled: Bool {
if let config = config {
return config.isSDKEnabled
}
return true
}

let config: EmbraceConfig?
let storage: EmbraceStorage
let upload: EmbraceUpload?
Expand Down Expand Up @@ -149,7 +156,8 @@ To start the SDK you first need to configure it using an `Embrace.Options` insta
self.logController = logControllable ?? LogController(
storage: storage,
upload: upload,
controller: sessionController
controller: sessionController,
config: config
)
super.init()

Expand Down Expand Up @@ -258,6 +266,10 @@ To start the SDK you first need to configure it using an `Embrace.Options` insta
@objc private func onConfigUpdated() {
if let config = config {
Embrace.logger.limits = config.internalLogLimits
if !config.isSDKEnabled {
Embrace.logger.debug("SDK was disabled")
captureServices.stop()
}
}
}
}
Loading

0 comments on commit 69ed424

Please sign in to comment.