Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Innovation week] Enable compilation for macOS #1711

Merged
merged 16 commits into from
Apr 24, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- [FEATURE] Add support for 128 bit trace IDs. See [#1721][]
- [FEATURE] Fatal App Hangs are tracked in RUM. See [#1763][]
- [FIX] Avoid name collision with Required Reason APIs. See [#1774][]
- [IMPROVEMENT] Make the SDK compile on macOS 12+. See [#1711][]

# 2.9.0 / 11-04-2024

Expand Down Expand Up @@ -627,6 +628,7 @@ Release `2.0` introduces breaking changes. Follow the [Migration Guide](MIGRATIO
[#1696]: https://github.com/DataDog/dd-sdk-ios/pull/1696
[#1697]: https://github.com/DataDog/dd-sdk-ios/pull/1697
[#1707]: https://github.com/DataDog/dd-sdk-ios/pull/1707
[#1711]: https://github.com/DataDog/dd-sdk-ios/pull/1711
[#1722]: https://github.com/DataDog/dd-sdk-ios/pull/1722
[#1724]: https://github.com/DataDog/dd-sdk-ios/pull/1724
[#1741]: https://github.com/DataDog/dd-sdk-ios/pull/1741
Expand Down
8 changes: 6 additions & 2 deletions DatadogCore/Private/ObjcAppLaunchHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
* Copyright 2019-Present Datadog, Inc.
*/

#import <UIKit/UIKit.h>
#import <pthread.h>
#import <sys/sysctl.h>

#import "ObjcAppLaunchHandler.h"

#if TARGET_OS_IOS || TARGET_OS_TV
#import <UIKit/UIKit.h>
#endif

// A very long application launch time is most-likely the result of a pre-warmed process.
// We consider 30s as a threshold for pre-warm detection.
#define COLD_START_TIME_THRESHOLD 30
Expand Down Expand Up @@ -39,7 +42,7 @@ + (void)load {
// This is called at the `DatadogPrivate` load time, keep the work minimal
_shared = [[self alloc] initWithProcessInfo:NSProcessInfo.processInfo
loadTime:CFAbsoluteTimeGetCurrent()];

#if TARGET_OS_IOS || TARGET_OS_TV
NSNotificationCenter * __weak center = NSNotificationCenter.defaultCenter;
id __block __unused token = [center addObserverForName:UIApplicationDidBecomeActiveNotification
object:nil
Expand All @@ -55,6 +58,7 @@ + (void)load {
[center removeObserver:token];
token = nil;
}];
#endif
}

+ (__dd_private_AppLaunchHandler *)shared {
Expand Down
2 changes: 2 additions & 0 deletions DatadogCore/Sources/Core/Context/LaunchTimePublisher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Copyright 2019-Present Datadog, Inc.
*/

#if !os(macOS)
import Foundation
import DatadogInternal

Expand Down Expand Up @@ -42,3 +43,4 @@ internal struct LaunchTimePublisher: ContextValuePublisher {
AppLaunchHandler.shared.setApplicationDidBecomeActiveCallback { _ in }
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,13 @@ extension NetworkConnectionInfo.Reachability {
extension NetworkConnectionInfo.Interface {
@available(iOS 2.0, macCatalyst 13.0, *)
init?(_ flags: SCNetworkReachabilityFlags?) {
#if os(iOS) || os(tvOS)
guard let flags = flags, flags.contains(.isWWAN) else {
return nil
}
self = .cellular
#else
self = nil
#endif
}
}
3 changes: 3 additions & 0 deletions DatadogCore/Sources/Core/DatadogCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,10 @@ extension DatadogContextProvider {
self.init(context: context)

subscribe(\.serverTimeOffset, to: ServerOffsetPublisher(provider: serverDateProvider))

#if !os(macOS)
subscribe(\.launchTime, to: LaunchTimePublisher())
#endif

if #available(iOS 12, tvOS 12, *) {
subscribe(\.networkConnectionInfo, to: NWPathMonitorPublisher())
Expand Down
2 changes: 1 addition & 1 deletion DatadogCore/Sources/Core/Upload/FeatureUpload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ internal struct FeatureUpload {
? UIKitBackgroundTaskCoordinator()
: nil
#else
let backgroundTaskCoordinator = nil
let backgroundTaskCoordinator: BackgroundTaskCoordinator? = nil
#endif

self.init(
Expand Down
6 changes: 5 additions & 1 deletion DatadogCore/Sources/Datadog.swift
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,12 @@ public enum Datadog {
consolePrint("⚠️ Catalyst is not officially supported by Datadog SDK: some features may NOT be functional!", .warn)
#endif

#if os(macOS)
consolePrint("⚠️ macOS is not officially supported by Datadog SDK: some features may NOT be functional!", .warn)
#endif

#if swift(>=5.9) && os(visionOS)
consolePrint("⚠️ VisionOS is not officially supported by Datadog SDK: some features may NOT be functional!", .warn)
consolePrint("⚠️ visionOS is not officially supported by Datadog SDK: some features may NOT be functional!", .warn)
#endif

do {
Expand Down
33 changes: 31 additions & 2 deletions DatadogInternal/Sources/Context/DeviceInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ public struct DeviceInfo: Codable, Equatable, PassthroughAnyCodable {
}
}

#if canImport(UIKit)
import MachO

#if canImport(UIKit)
import UIKit
import MachO

extension DeviceInfo {
/// Creates device info based on UIKit description.
Expand Down Expand Up @@ -96,4 +96,33 @@ extension DeviceInfo {
#endif
}
}
#elseif os(macOS)
/// Creates device info based on Host description.
///
/// - Parameters:
/// - processInfo: The current process information.
extension DeviceInfo {
public init(
processInfo: ProcessInfo = .processInfo
) {
var architecture = "unknown"
if let archInfo = NXGetLocalArchInfo()?.pointee {
architecture = String(utf8String: archInfo.name) ?? "unknown"
}
Host.current().name

let build = (try? Sysctl.osVersion()) ?? ""
let model = (try? Sysctl.model()) ?? ""
Comment on lines +114 to +115
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can they just be nil?

Also, are there high level APIs for such information?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They're required by schema

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, instead empty string some other string would be helpful in querying like <unknown> or <unrecognized>

let systemVersion = processInfo.operatingSystemVersion

self.init(
name: model.components(separatedBy: CharacterSet.letters.inverted).joined(),
model: model,
osName: "macOS",
osVersion: "\(systemVersion.majorVersion).\(systemVersion.minorVersion).\(systemVersion.patchVersion)",
osBuildNumber: build,
architecture: architecture
)
}
}
#endif
2 changes: 1 addition & 1 deletion DatadogInternal/Sources/Upload/DefaultJSONEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extension DatadogExtension where ExtendedType == JSONEncoder {
let formatted = iso8601DateFormatter.string(from: date)
try container.encode(formatted)
}
if #available(iOS 13, tvOS 13, macOS 10.15, *) {
if #available(iOS 13, tvOS 13, *) {
encoder.outputFormatting = [.withoutEscapingSlashes]
}
return encoder
Expand Down
4 changes: 3 additions & 1 deletion DatadogRUM/Sources/RUMVitals/VitalRefreshRateReader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,9 @@ extension FrameInfoProvider {
extension CADisplayLink: FrameInfoProvider {
var maximumDeviceFramesPerSecond: Int {
#if swift(>=5.9) && os(visionOS)
120
// Hardcoded as for now there's no good way of extracting maximum FPS on VisionOS
// https://developer.apple.com/documentation/visionos/analyzing-the-performance-of-your-visionos-app#Inspect-frame-rendering-performance
90
#else
UIScreen.main.maximumFramesPerSecond
#endif
Expand Down
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ let package = Package(
name: "Datadog",
platforms: [
.iOS(.v11),
.tvOS(.v11)
.tvOS(.v11),
.macOS(.v12)
],
products: [
.library(
Expand Down
22 changes: 15 additions & 7 deletions SUPPORTED-VERSIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@
| **iOS** | ✅ | `11+` |
| **tvOS** | ✅ | `11+` |
| **iPadOS** | ✅ | `11+` |
| **VisionOS** | ⚠️ | `1.1+` |
| **macOS (Designed for iPad)** | ✅ | `11+` |
| **macOS (Catalyst)** | ⚠️ | `12+` |
| **macOS** | ⚠️ | `12+` |
| **visionOS** | ⚠️ | `1.0+` |
| **watchOS**| ❌ | `n/a` |
| **macOS** | ❌ | `n/a` |
| **Linux** | ❌ | `n/a` |

## VisionOS
VisionOS is not officially supported by Datadog SDK. Some features may not be fully functional.

VisionOS is not officially supported by Datadog SDK. Some features may not be fully functional. Note that `DatadogCrashReporting` is not supported on VisionOS, due to lack of support on the [PLCrashReporter side](https://github.com/microsoft/plcrashreporter/issues/288).

## MacOS

MacOS is not officially supported by Datadog SDK. Some features may not be fully functional. Note that `DatadogRUM`, `DatadogSessionReplay` and `DatadogObjc` which heavily depend on `UIKit` do not build on macOS.

## Catalyst

We support Catalyst in build mode only, which means that macOS target will build, but functionalities for the SDK might not work for this target.

## Xcode

Expand Down Expand Up @@ -47,9 +58,6 @@ We currently support integration of the SDK using following dependency managers.

*Note: Third party networking libraries can be instrumented by implementing custom `DDURLSessionDelegate`.*

## Catalyst
We support Catalyst in build mode only, which means that macOS target will build, but functionalities for the SDK won't work for this target.

## Dependencies
The Datadog SDK depends on the following third-party library:
- [PLCrashReporter](https://github.com/microsoft/plcrashreporter) 1.11.1
- [PLCrashReporter](https://github.com/microsoft/plcrashreporter) 1.11.1
12 changes: 12 additions & 0 deletions bitrise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,18 @@ workflows:
-project "$BITRISE_SOURCE_DIR/dependency-manager-tests/spm/SPMProject.xcodeproj" \
-destination "platform=macOS,variant=Mac Catalyst" \
| xcpretty
- script:
title: Check macOS compatibility (build SPMProject for macOS)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


Only SPM?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It follows what we did for Mac Catalyst. It's minimal effort for the smoke test, as the support is not official

run_if: '{{enveq "DD_RUN_SMOKE_TESTS" "1"}}'
inputs:
- content: |-
#!/usr/bin/env bash
set -euxo pipefail

xcodebuild build -scheme "App macOS" \
-project "$BITRISE_SOURCE_DIR/dependency-manager-tests/spm/SPMProject.xcodeproj" \
-destination "platform=macOS" \
| xcpretty
- xcode-test:
title: Run SPMProject iOS tests
run_if: '{{enveq "DD_RUN_SMOKE_TESTS" "1"}}'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<key>com.apple.security.app-sandbox</key>
<true/>
20 changes: 20 additions & 0 deletions dependency-manager-tests/spm/App macOS/App_macOSApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-Present Datadog, Inc.
*/

import SwiftUI

@main
struct App_macOSApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}

init() {
DatadogSetup.initialize()
}
}
18 changes: 18 additions & 0 deletions dependency-manager-tests/spm/App macOS/ContentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-Present Datadog, Inc.
*/

import SwiftUI

struct ContentView: View {
var body: some View {
VStack {
Image(systemName: "globe")
.imageScale(.large)
Text("Testing...")
}
.padding()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
* This product includes software developed at Datadog (https://www.datadoghq.com/).
* Copyright 2019-Present Datadog, Inc.
*/

import XCTest

final class App_macOSUITests: XCTestCase {
func testExample() throws {
let app = XCUIApplication()
app.launch()
XCTAssert(app.staticTexts["Testing..."].exists)
}
}
33 changes: 3 additions & 30 deletions dependency-manager-tests/spm/App/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,46 +5,19 @@
*/

import UIKit
import DatadogCore
import DatadogLogs
import DatadogTrace
import DatadogRUM
import DatadogCrashReporting
import DatadogSessionReplay // it should compile for iOS and tvOS, but APIs are only available on iOS
import DatadogObjc
import DatadogSessionReplay // it should compile for iOS and tvOS, but APIs are only available on iOS

internal class ViewController: UIViewController {
private var logger: LoggerProtocol! // swiftlint:disable:this implicitly_unwrapped_optional

override func viewDidLoad() {
super.viewDidLoad()

Datadog.initialize(
with: Datadog.Configuration(clientToken: "abc", env: "tests"),
trackingConsent: .granted
)

Logs.enable()

CrashReporting.enable()

self.logger = Logger.create(
with: Logger.Configuration(
remoteSampleRate: 0,
consoleLogFormat: .short
)
)
DatadogSetup.initialize()

// RUM APIs must be visible:
RUM.enable(with: .init(applicationID: "app-id"))
RUMMonitor.shared().startView(viewController: self)

// Trace APIs must be visible:
Trace.enable()

logger.info("It works")
_ = Tracer.shared().startSpan(operationName: "this too")


#if os(iOS)
// Session Replay API must be visible:
SessionReplay.enable(with: .init(replaySampleRate: 0))
Expand Down
Loading