From fb53d3b006d5c64f50124d2fe46b90c534b5b0b2 Mon Sep 17 00:00:00 2001 From: Ioannis J Date: Tue, 4 Feb 2025 08:11:31 +0200 Subject: [PATCH] fix: identify mac catalyst (#287) * fix: identify mac catalyst * fix: build * fix: build tvos * fix: add properties for mac catalyst and ios running on mac * fix: same values for catalyst and ios running on mac --- CHANGELOG.md | 2 + PostHog.xcodeproj/project.pbxproj | 6 +- PostHog/PostHogContext.swift | 110 +++++++++++++++++++++++------- 3 files changed, 91 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4bd6eee3..c2bd9991f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Next +- fix: identify macOS when running Mac Catalyst or iOS on Mac ([#287](https://github.com/PostHog/posthog-ios/pull/287)) + ## 3.19.2 - 2025-01-30 - fix: XCFramework builds failing ([#288](https://github.com/PostHog/posthog-ios/pull/288)) diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 31e84641a..476d5dbab 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -2021,11 +2021,11 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited)"; MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.posthog.PostHogExample.yj; + PRODUCT_BUNDLE_IDENTIFIER = com.posthog.PostHogExample; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; + SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; @@ -2060,7 +2060,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.posthog.PostHogExample; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; - SUPPORTS_MACCATALYST = NO; + SUPPORTS_MACCATALYST = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; diff --git a/PostHog/PostHogContext.swift b/PostHog/PostHogContext.swift index b80258134..bf1d1f5e8 100644 --- a/PostHog/PostHogContext.swift +++ b/PostHog/PostHogContext.swift @@ -53,30 +53,77 @@ class PostHogContext { properties["$is_emulator"] = false #endif + // iOS app running in compatibility mode (Designed for iPad/iPhone) + var isiOSAppOnMac = false + #if os(iOS) + if #available(iOS 14.0, *) { + isiOSAppOnMac = ProcessInfo.processInfo.isiOSAppOnMac + } + #endif + + // iPad app running on Mac Catalyst + #if targetEnvironment(macCatalyst) + let isMacCatalystApp = true + #else + let isMacCatalystApp = false + #endif + + properties["$is_ios_running_on_mac"] = isiOSAppOnMac + properties["$is_mac_catalyst_app"] = isMacCatalystApp + #if os(iOS) || os(tvOS) let device = UIDevice.current // use https://github.com/devicekit/DeviceKit - properties["$device_name"] = device.model - properties["$os_name"] = device.systemName - properties["$os_version"] = device.systemVersion - - var deviceType: String? - switch device.userInterfaceIdiom { - case UIUserInterfaceIdiom.phone: - deviceType = "Mobile" - case UIUserInterfaceIdiom.pad: - deviceType = "Tablet" - case UIUserInterfaceIdiom.tv: - deviceType = "TV" - case UIUserInterfaceIdiom.carPlay: - deviceType = "CarPlay" - case UIUserInterfaceIdiom.mac: - deviceType = "Desktop" - default: - deviceType = nil - } - if deviceType != nil { - properties["$device_type"] = deviceType + let processInfo = ProcessInfo.processInfo + + if isMacCatalystApp || isiOSAppOnMac { + let underlyingOS = device.systemName + let underlyingOSVersion = device.systemVersion + let macOSVersion = processInfo.operatingSystemVersionString + + if isMacCatalystApp { + let osVersion = ProcessInfo.processInfo.operatingSystemVersion + properties["$os_version"] = "\(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)" + } else { + let osVersionString = processInfo.operatingSystemVersionString + if let versionRange = osVersionString.range(of: #"\d+\.\d+\.\d+"#, options: .regularExpression) { + properties["$os_version"] = osVersionString[versionRange] + } else { + // fallback to full version string in case formatting changes + properties["$os_version"] = osVersionString + } + } + // device.userInterfaceIdiom reports .pad here, so we use a static value instead + // - For an app deployable on iPad, the idiom type is always .pad (instead of .mac) + // + // Source: https://developer.apple.com/documentation/apple-silicon/adapting-ios-code-to-run-in-the-macos-environment#Handle-unknown-device-types-gracefully + properties["$os_name"] = "macOS" + properties["$device_type"] = "Desktop" + properties["$device_name"] = processInfo.hostName + } else { + // use https://github.com/devicekit/DeviceKit + properties["$os_name"] = device.systemName + properties["$os_version"] = device.systemVersion + properties["$device_name"] = device.model + + var deviceType: String? + switch device.userInterfaceIdiom { + case UIUserInterfaceIdiom.phone: + deviceType = "Mobile" + case UIUserInterfaceIdiom.pad: + deviceType = "Tablet" + case UIUserInterfaceIdiom.tv: + deviceType = "TV" + case UIUserInterfaceIdiom.carPlay: + deviceType = "CarPlay" + case UIUserInterfaceIdiom.mac: + deviceType = "Desktop" + default: + deviceType = nil + } + if deviceType != nil { + properties["$device_type"] = deviceType + } } #elseif os(macOS) let deviceName = Host.current().localizedName @@ -84,7 +131,7 @@ class PostHogContext { properties["$device_name"] = deviceName } let processInfo = ProcessInfo.processInfo - properties["$os_name"] = "macOS \(processInfo.operatingSystemVersionString)" // eg Version 14.2.1 (Build 23C71) + properties["$os_name"] = "macOS" let osVersion = processInfo.operatingSystemVersion properties["$os_version"] = "\(osVersion.majorVersion).\(osVersion.minorVersion).\(osVersion.patchVersion)" properties["$device_type"] = "Desktop" @@ -134,10 +181,25 @@ class PostHogContext { } private func platform() -> String { + var sysctlName = "hw.machine" + + // In case of mac catalyst or iOS running on mac: + // - "hw.machine" returns underlying iPad/iPhone model + // - "hw.model" returns mac model + #if targetEnvironment(macCatalyst) + sysctlName = "hw.model" + #elseif os(iOS) + if #available(iOS 14.0, *) { + if ProcessInfo.processInfo.isiOSAppOnMac { + sysctlName = "hw.model" + } + } + #endif + var size = 0 - sysctlbyname("hw.machine", nil, &size, nil, 0) + sysctlbyname(sysctlName, nil, &size, nil, 0) var machine = [CChar](repeating: 0, count: size) - sysctlbyname("hw.machine", &machine, &size, nil, 0) + sysctlbyname(sysctlName, &machine, &size, nil, 0) return String(cString: machine) }