diff --git a/CHANGELOG.md b/CHANGELOG.md index 94077f23..a114707c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,7 @@ _None._ ### New Features -_None._ +- The library can now be used in tvOS targets. [#336] ### Bug Fixes diff --git a/Package.swift b/Package.swift index 95fe755d..d77f6959 100644 --- a/Package.swift +++ b/Package.swift @@ -4,7 +4,7 @@ import PackageDescription let package = Package( name: "AutomatticTracksiOS", - platforms: [.macOS(.v10_14), .iOS(.v15)], + platforms: [.macOS(.v10_14), .iOS(.v15), .tvOS(.v15)], products: [ .library( name: "AutomatticTracks", diff --git a/Sources/Model/ObjC/Common/TracksDeviceInformation.m b/Sources/Model/ObjC/Common/TracksDeviceInformation.m index 0142c538..4347907d 100644 --- a/Sources/Model/ObjC/Common/TracksDeviceInformation.m +++ b/Sources/Model/ObjC/Common/TracksDeviceInformation.m @@ -2,6 +2,11 @@ #if TARGET_OS_WATCH #import +#elif TARGET_OS_TV +@import UIDeviceIdentifier; +#import +#import +#import "UIApplication+Extensions.h" #elif TARGET_OS_IPHONE #import @import UIDeviceIdentifier; @@ -23,9 +28,10 @@ @interface TracksDeviceInformation () @property (nonatomic, assign) BOOL isReachableByWiFi; @property (nonatomic, assign) BOOL isReachableByWWAN; -#if TARGET_OS_IPHONE && !TARGET_OS_WATCH -@property (nonatomic, assign) UIDeviceOrientation lastKnownDeviceOrientation; @property (nonatomic, strong) NSString *lastKnownPreferredContentSizeCategory; + +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV +@property (nonatomic, assign) UIDeviceOrientation lastKnownDeviceOrientation; #endif @end @@ -44,7 +50,7 @@ - (instancetype)init - (void)preloadDeviceProperties { -#if TARGET_OS_IPHONE && !TARGET_OS_WATCH +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV void (^preload)(void) = ^(void) { self.lastKnownDeviceOrientation = UIDevice.currentDevice.orientation; self.lastKnownPreferredContentSizeCategory = UIApplication.sharedIfAvailable.preferredContentSizeCategory; @@ -73,7 +79,7 @@ - (NSString *)currentNetworkOperator { #if TARGET_OS_SIMULATOR return @"Carrier (Simulator)"; -#elif TARGET_OS_WATCH +#elif TARGET_OS_WATCH || TARGET_OS_TV return @"Not Applicable"; #elif TARGET_OS_IPHONE CTTelephonyNetworkInfo *netInfo = [CTTelephonyNetworkInfo new]; @@ -168,6 +174,8 @@ - (NSString *)version -(BOOL)isAppleWatchConnected{ #if TARGET_OS_WATCH return YES; // We're running on the watch itself +#elif TARGET_OS_TV + return NO; // We're running on tvOS #elif TARGET_OS_IPHONE return [[WatchSessionManager shared] hasBeenPreviouslyPaired]; #else // Mac @@ -194,7 +202,7 @@ -(BOOL)isVoiceOverEnabled{ } -(NSString *)orientation{ -#if TARGET_OS_WATCH +#if TARGET_OS_WATCH || TARGET_OS_TV return @"Unknown"; #elif TARGET_OS_IPHONE UIDeviceOrientation orientation = [self deviceOrientation]; @@ -213,7 +221,7 @@ -(NSString *)orientation{ #pragma mark - Calls that need to run on the main thread -#if TARGET_OS_IPHONE && !TARGET_OS_WATCH +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV // This method was created because UIDevice.currentDevice.orientation should only // be called from the main thread. // diff --git a/Sources/Model/ObjC/Common/WatchSessionManager.m b/Sources/Model/ObjC/Common/WatchSessionManager.m index 8892d636..243f76c7 100644 --- a/Sources/Model/ObjC/Common/WatchSessionManager.m +++ b/Sources/Model/ObjC/Common/WatchSessionManager.m @@ -1,6 +1,6 @@ #import "WatchSessionManager.h" -#if TARGET_OS_IPHONE && !TARGET_OS_WATCH +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV #import @interface WatchSessionManager() @@ -34,7 +34,7 @@ - (instancetype)init { return self; } -#if TARGET_OS_IPHONE && !TARGET_OS_WATCH +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV if([WCSession isSupported]){ self.session = [WCSession defaultSession]; @@ -54,7 +54,7 @@ - (instancetype)init { return self; } -#if TARGET_OS_IPHONE && !TARGET_OS_WATCH +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV - (void)setHasBeenPairedIfPossibleWithSession:(nonnull WCSession *)session { if(session.paired){ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"watch-has-been-previously-paired"]; @@ -69,7 +69,7 @@ - (BOOL)hasBeenPreviouslyPaired{ #pragma mark – WCSessionDelegate -#if TARGET_OS_IPHONE && !TARGET_OS_WATCH +#if TARGET_OS_IPHONE && !TARGET_OS_WATCH && !TARGET_OS_TV - (void)session:(nonnull WCSession *)session activationDidCompleteWithState:(WCSessionActivationState)activationState error:(nullable NSError *)error { [self setHasBeenPairedIfPossibleWithSession:session]; diff --git a/Sources/Remote Logging/Crash Logging/CrashLogging.swift b/Sources/Remote Logging/Crash Logging/CrashLogging.swift index 5c602607..46356513 100644 --- a/Sources/Remote Logging/Crash Logging/CrashLogging.swift +++ b/Sources/Remote Logging/Crash Logging/CrashLogging.swift @@ -75,7 +75,7 @@ public class CrashLogging { // input `SamplingContext` down the chain. NSNumber(value: self.dataProvider.tracesSampler()) } - #if !os(watchOS) + #if !os(watchOS) && !os(tvOS) options.configureProfiling = { [weak self] in guard let self else { return } $0.sessionSampleRate = Float(self.dataProvider.profilingRate) diff --git a/Sources/UI/Crash Logging/CrashLoggingView.swift b/Sources/UI/Crash Logging/CrashLoggingView.swift index a3305426..6d9fb70c 100644 --- a/Sources/UI/Crash Logging/CrashLoggingView.swift +++ b/Sources/UI/Crash Logging/CrashLoggingView.swift @@ -5,7 +5,7 @@ import Sentry import AutomatticRemoteLogging #endif -@available(iOS 13.0, OSX 10.15, *) +@available(iOS 13.0, OSX 10.15, tvOS 15.0, *) public struct CrashLoggingView: View { @State diff --git a/TracksDemo/TracksDemo Mac/Base.lproj/Main.storyboard b/TracksDemo/TracksDemo Mac/Base.lproj/Main.storyboard index 6ebf46b6..aa72bd1c 100644 --- a/TracksDemo/TracksDemo Mac/Base.lproj/Main.storyboard +++ b/TracksDemo/TracksDemo Mac/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + @@ -746,7 +746,7 @@ - + @@ -759,7 +759,7 @@ - + - + diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/AccentColor.colorset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..2e003356 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json new file mode 100644 index 00000000..de59d885 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..2e003356 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..2e003356 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,11 @@ +{ + "images" : [ + { + "idiom" : "tv" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..795cce17 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json new file mode 100644 index 00000000..de59d885 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json @@ -0,0 +1,17 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "layers" : [ + { + "filename" : "Front.imagestacklayer" + }, + { + "filename" : "Middle.imagestacklayer" + }, + { + "filename" : "Back.imagestacklayer" + } + ] +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..795cce17 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json new file mode 100644 index 00000000..795cce17 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json new file mode 100644 index 00000000..f47ba43d --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json @@ -0,0 +1,32 @@ +{ + "assets" : [ + { + "filename" : "App Icon - App Store.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "1280x768" + }, + { + "filename" : "App Icon.imagestack", + "idiom" : "tv", + "role" : "primary-app-icon", + "size" : "400x240" + }, + { + "filename" : "Top Shelf Image Wide.imageset", + "idiom" : "tv", + "role" : "top-shelf-image-wide", + "size" : "2320x720" + }, + { + "filename" : "Top Shelf Image.imageset", + "idiom" : "tv", + "role" : "top-shelf-image", + "size" : "1920x720" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json new file mode 100644 index 00000000..795cce17 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json new file mode 100644 index 00000000..795cce17 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "idiom" : "tv", + "scale" : "1x" + }, + { + "idiom" : "tv", + "scale" : "2x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/Assets.xcassets/Contents.json b/TracksDemo/TracksDemo tvOS/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/TracksDemo/TracksDemo tvOS/ContentView.swift b/TracksDemo/TracksDemo tvOS/ContentView.swift new file mode 100644 index 00000000..f7522e37 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/ContentView.swift @@ -0,0 +1,54 @@ +import SwiftUI +import AutomatticTracks + +struct ContentView: View { + @StateObject var viewModel = TracksViewModel() + + var crashLogging: CrashLogging = { + let crashLogging = try! CrashLogging(dataProvider: CrashLoggingDataSource()).start() + return crashLogging + }() + + var body: some View { + TabView { + Tab("Track Events", systemImage: "tray.and.arrow.down.fill") { + VStack { + Picker("User Type", selection: $viewModel.userType) { + Text("Anonymous").tag(0) + Text("WordPress").tag(1) + } + + Toggle("Automatically Send Events", isOn: $viewModel.automaticallySendEvents) + + Button("Track Event") { + viewModel.trackEvent() + } + Button("Track Event With Properties") { + viewModel.trackEventWithCustomProperties() + } + Button("Crash Application") { + viewModel.crashApplication() + } + ProgressView(value: viewModel.progress, total: 1.0) + Text(viewModel.queuedEventsLabel) + } + .padding() + .onAppear() { + viewModel.load() + } + } + Tab("Crash Logging", systemImage: "tray.and.arrow.down.fill") { + CrashLoggingView(crashLogging: crashLogging) + .padding() + .onAppear() { + viewModel.load() + } + } + + } + } +} + +#Preview { + ContentView() +} diff --git a/TracksDemo/TracksDemo tvOS/TracksDemo_tvOSApp.swift b/TracksDemo/TracksDemo tvOS/TracksDemo_tvOSApp.swift new file mode 100644 index 00000000..67c2a9ed --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/TracksDemo_tvOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct TracksDemo_tvOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/TracksDemo/TracksDemo tvOS/TracksViewModel.swift b/TracksDemo/TracksDemo tvOS/TracksViewModel.swift new file mode 100644 index 00000000..a6283484 --- /dev/null +++ b/TracksDemo/TracksDemo tvOS/TracksViewModel.swift @@ -0,0 +1,139 @@ +import Foundation +import AutomatticTracks +import Combine + +class TracksViewModel: NSObject, ObservableObject { + + private lazy var contextManager = TracksContextManager() + private lazy var tracksService = TracksService(contextManager: contextManager) + private var fetchedResultsController: NSFetchedResultsController! + + @Published var progress: Double = 0.5 + @Published var queuedEventsLabel: String = "" + + var sendInterval: TimeInterval { + get { return tracksService?.queueSendInterval ?? 10 } + set { tracksService?.queueSendInterval = newValue} + } + + func load() { + // Do any additional setup after loading the view. + tracksService?.queueSendInterval = 10.0 + tracksService?.eventNamePrefix = "tracks_tvos_demo" + + resetTimer() + setupFetchedResultsController() + addTimerEventListeners() + switchToAnonymousUser() + } + + deinit { + NotificationCenter.default.removeObserver(self) + } + + func trackEvent() { + tracksService?.trackEventName("test_event") + } + + func trackEventWithCustomProperties() { + tracksService?.trackEventName("test_event_with_properties", withCustomProperties: [ + "custom_prop_1": "valuetew" + ]) + } + + func crashApplication() { + abort() + } + + @Published var userType: Int = 0 { + didSet { + if userType == 0 { + switchToAnonymousUser() + } + else { + switchToWordPressDotComUser() + } + } + } + + @Published var automaticallySendEvents: Bool = true { + didSet { + tracksService?.remoteCallsEnabled = automaticallySendEvents + + if automaticallySendEvents { + resetTimer() + } else { + timer.invalidate() + } + } + } + + // MARK: – Fetched Results Controller + private func setupFetchedResultsController() { + let fetchRequest = NSFetchRequest(entityName: "TracksEvent") + fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "date", ascending: true) ] + + self.fetchedResultsController = NSFetchedResultsController( + fetchRequest: fetchRequest, + managedObjectContext: contextManager.managedObjectContext, + sectionNameKeyPath: nil, + cacheName: nil) + + self.fetchedResultsController.delegate = self + + do { + try self.fetchedResultsController.performFetch() + } + catch let err { + debugPrint("Error fetching Tracks Events: \(err.localizedDescription)") + } + } + + // MARK: - Timer + private lazy var timer = Timer() + private var startTime: Date! + + private func addTimerEventListeners() { + NotificationCenter.default.addObserver(self, selector: #selector(resetTimer), name: NSNotification.Name.TrackServiceWillSendQueuedEvents, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(resetTimer), name: NSNotification.Name.TrackServiceDidSendQueuedEvents, object: nil) + } + + @objc private func resetTimer() { + timer.invalidate() + startTime = Date() + progress = 0 + + timer = Timer.scheduledTimer(timeInterval: sendInterval / 100, target: self, selector: #selector(fireTimer(_:)), userInfo: nil, repeats: true) + } + + @objc func fireTimer(_ sender: Timer) { + if sender.fireDate.timeIntervalSince(startTime) > sendInterval { + timer.invalidate() + } + + let progress = max(0, min(sender.fireDate.timeIntervalSince(startTime) / sendInterval, 1.0)) + self.progress = progress + } + + // MARK: - Helpers + private func updateObjectCountLabel() { + DispatchQueue.main.async { [weak self] in + let count = self?.fetchedResultsController.fetchedObjects?.count ?? 0 + self?.queuedEventsLabel = "Number of events queued: \(count)" + } + } + + private func switchToAnonymousUser() { + tracksService?.switchToAnonymousUser(withAnonymousID: NSUUID().uuidString) + } + + private func switchToWordPressDotComUser() { + tracksService?.switchToAuthenticatedUser(withUsername: "astralbodies", userID: "67137", skipAliasEventCreation: false) + } +} + +extension TracksViewModel: NSFetchedResultsControllerDelegate { + func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { + self.updateObjectCountLabel() + } +} diff --git a/TracksDemo/TracksDemo.xcodeproj/project.pbxproj b/TracksDemo/TracksDemo.xcodeproj/project.pbxproj index f374f78f..8caf2070 100644 --- a/TracksDemo/TracksDemo.xcodeproj/project.pbxproj +++ b/TracksDemo/TracksDemo.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 70; objects = { /* Begin PBXBuildFile section */ @@ -27,6 +27,9 @@ F93676FD21DFFDC700323458 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F93676FC21DFFDC700323458 /* ViewController.swift */; }; F93676FF21DFFDC800323458 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F93676FE21DFFDC800323458 /* Assets.xcassets */; }; F936770221DFFDC800323458 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F936770021DFFDC800323458 /* Main.storyboard */; }; + FF3A365F2F961AE9008D7F03 /* AutomatticTracks in Frameworks */ = {isa = PBXBuildFile; productRef = FF3A365E2F961AE9008D7F03 /* AutomatticTracks */; }; + FF3A36602F9620F2008D7F03 /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FB4D4125B2AF0C006E4031 /* Secrets.swift */; }; + FF3A36612F9620F7008D7F03 /* CrashLoggingDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24FB4D3C25B2AF02006E4031 /* CrashLoggingDataSource.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -68,8 +71,13 @@ F936770121DFFDC800323458 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; F936770321DFFDC800323458 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F936770421DFFDC800323458 /* TracksDemo_Mac.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TracksDemo_Mac.entitlements; sourceTree = ""; }; + FF3A36502F961502008D7F03 /* TracksDemo tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "TracksDemo tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + FF3A36512F961502008D7F03 /* TracksDemo tvOS */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = "TracksDemo tvOS"; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 9376328B1AC2026900086BC6 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -95,6 +103,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FF3A364D2F961502008D7F03 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FF3A365F2F961AE9008D7F03 /* AutomatticTracks in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -115,6 +131,7 @@ F93676F921DFFDC700323458 /* TracksDemo Mac */, 24FB4D3B25B2AEEA006E4031 /* Shared */, 937632AA1AC2026900086BC6 /* TracksDemoTests */, + FF3A36512F961502008D7F03 /* TracksDemo tvOS */, 9376328F1AC2026900086BC6 /* Products */, B71639B226AF298600C9FDAA /* Frameworks */, ); @@ -126,6 +143,7 @@ 9376328E1AC2026900086BC6 /* TracksDemo.app */, 937632A71AC2026900086BC6 /* TracksDemoTests.xctest */, F93676F821DFFDC700323458 /* TracksDemo Mac.app */, + FF3A36502F961502008D7F03 /* TracksDemo tvOS.app */, ); name = Products; sourceTree = ""; @@ -268,13 +286,36 @@ productReference = F93676F821DFFDC700323458 /* TracksDemo Mac.app */; productType = "com.apple.product-type.application"; }; + FF3A364F2F961502008D7F03 /* TracksDemo tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = FF3A365A2F961503008D7F03 /* Build configuration list for PBXNativeTarget "TracksDemo tvOS" */; + buildPhases = ( + FF3A364C2F961502008D7F03 /* Sources */, + FF3A364D2F961502008D7F03 /* Frameworks */, + FF3A364E2F961502008D7F03 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + FF3A36512F961502008D7F03 /* TracksDemo tvOS */, + ); + name = "TracksDemo tvOS"; + packageProductDependencies = ( + FF3A365E2F961AE9008D7F03 /* AutomatticTracks */, + ); + productName = "TracksDemo tvOS"; + productReference = FF3A36502F961502008D7F03 /* TracksDemo tvOS.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ 937632861AC2026900086BC6 /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1010; + LastSwiftUpdateCheck = 2620; LastUpgradeCheck = 1410; ORGANIZATIONNAME = "Automattic Inc."; TargetAttributes = { @@ -291,6 +332,9 @@ CreatedOnToolsVersion = 10.1; LastSwiftMigration = ""; }; + FF3A364F2F961502008D7F03 = { + CreatedOnToolsVersion = 26.2; + }; }; }; buildConfigurationList = 937632891AC2026900086BC6 /* Build configuration list for PBXProject "TracksDemo" */; @@ -312,6 +356,7 @@ 9376328D1AC2026900086BC6 /* TracksDemo */, 937632A61AC2026900086BC6 /* TracksDemoTests */, F93676F721DFFDC700323458 /* TracksDemo Mac */, + FF3A364F2F961502008D7F03 /* TracksDemo tvOS */, ); }; /* End PBXProject section */ @@ -343,6 +388,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FF3A364E2F961502008D7F03 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -422,6 +474,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + FF3A364C2F961502008D7F03 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FF3A36612F9620F7008D7F03 /* CrashLoggingDataSource.swift in Sources */, + FF3A36602F9620F2008D7F03 /* Secrets.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -724,6 +785,93 @@ }; name = Release; }; + FF3A36582F961503008D7F03 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.sergioestevao.TracksDemo-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 26.2; + }; + name = Debug; + }; + FF3A36592F961503008D7F03 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UIUserInterfaceStyle = Automatic; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.sergioestevao.TracksDemo-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + STRING_CATALOG_GENERATE_SYMBOLS = YES; + SWIFT_APPROACHABLE_CONCURRENCY = YES; + SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 26.2; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -763,6 +911,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + FF3A365A2F961503008D7F03 /* Build configuration list for PBXNativeTarget "TracksDemo tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FF3A36582F961503008D7F03 /* Debug */, + FF3A36592F961503008D7F03 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ @@ -790,6 +947,10 @@ package = B76FDFDC26BC5E150059054B /* XCRemoteSwiftPackageReference "CocoaLumberjack" */; productName = CocoaLumberjack; }; + FF3A365E2F961AE9008D7F03 /* AutomatticTracks */ = { + isa = XCSwiftPackageProductDependency; + productName = AutomatticTracks; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 937632861AC2026900086BC6 /* Project object */; diff --git a/TracksDemo/TracksDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/TracksDemo/TracksDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..0c67376e --- /dev/null +++ b/TracksDemo/TracksDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/TracksDemo/TracksDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/TracksDemo/TracksDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7ed1ff85..78584ca1 100644 --- a/TracksDemo/TracksDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/TracksDemo/TracksDemo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/jedisct1/swift-sodium", "state": { "branch": null, - "revision": "4f9164a0a2c9a6a7ff53a2833d54a5c79c957342", - "version": "0.9.1" + "revision": "e7e799cd1eaa4d0f6d3eab56832e7f4b377f4a4f", + "version": "0.10.0" } }, { diff --git a/TracksDemo/TracksDemo.xcodeproj/xcshareddata/xcschemes/TracksDemo tvOS.xcscheme b/TracksDemo/TracksDemo.xcodeproj/xcshareddata/xcschemes/TracksDemo tvOS.xcscheme new file mode 100644 index 00000000..28a83c11 --- /dev/null +++ b/TracksDemo/TracksDemo.xcodeproj/xcshareddata/xcschemes/TracksDemo tvOS.xcscheme @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TracksDemo/TracksDemo.xcodeproj/xcshareddata/xcschemes/TracksDemo.xcscheme b/TracksDemo/TracksDemo.xcodeproj/xcshareddata/xcschemes/TracksDemo.xcscheme index 722cc66b..b4999f38 100644 --- a/TracksDemo/TracksDemo.xcodeproj/xcshareddata/xcschemes/TracksDemo.xcscheme +++ b/TracksDemo/TracksDemo.xcodeproj/xcshareddata/xcschemes/TracksDemo.xcscheme @@ -48,6 +48,16 @@ ReferencedContainer = "container:.."> + + + +