Skip to content

Commit f198987

Browse files
Firebase user agent: additional fields for platform logging (#6429)
* Move firebase user agent logic to a separate class. * FIRAppTest: prefix some tests with the tested method names * FIRAppTest: user agent new fields tests * New user agent field added. * Headers * API docs * ./scripts/style.sh * nullability fix * Move environment info methods to GoogleUtilities * GULAppEnvironmentUtilTest * GULAppEnvironmentUtil swift tests * FirebaseApp user agent swift flag test * ./scripts/style.sh * Cleanup CoreDiagnostics * Nullable device model * FIRCoreDiagnosticsTest fix * Merge fixes * Fix import * rename * Cleanup GUL imports (since all headers are public now)
1 parent 2cd0c6e commit f198987

File tree

12 files changed

+335
-105
lines changed

12 files changed

+335
-105
lines changed

Example/CoreDiagnostics/Tests/FIRCoreDiagnosticsTest.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ - (void)populateProto:(logs_proto_mobilesdk_ios_ICoreConfiguration *)config {
193193
config->has_pod_name = 1;
194194
config->app_id = FIREncodeString(kGoogleAppID);
195195
config->bundle_id = FIREncodeString(kBundleID);
196-
config->device_model = FIREncodeString([FIRCoreDiagnostics deviceModel]);
196+
config->device_model = FIREncodeString([GULAppEnvironmentUtil deviceModel]);
197197
config->os_version = FIREncodeString([GULAppEnvironmentUtil systemVersion]);
198198
config->app_count = 1;
199199
config->has_app_count = 1;

Firebase/CoreDiagnostics/FIRCDLibrary/FIRCoreDiagnostics.m

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -192,24 +192,6 @@ - (instancetype)initWithTransport:(GDTCORTransport *)transport
192192
return self;
193193
}
194194

195-
#pragma mark - Metadata helpers
196-
197-
/** Returns the model of iOS device. Sample platform strings are @"iPhone7,1" for iPhone 6 Plus,
198-
* @"iPhone7,2" for iPhone 6, etc. Refer to the Hardware strings at
199-
* https://en.wikipedia.org/wiki/List_of_iOS_devices
200-
*
201-
* @return The device model as an NSString.
202-
*/
203-
+ (NSString *)deviceModel {
204-
static NSString *deviceModel = nil;
205-
if (deviceModel == nil) {
206-
struct utsname systemInfo;
207-
uname(&systemInfo);
208-
deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
209-
}
210-
return deviceModel;
211-
}
212-
213195
#pragma mark - nanopb helper functions
214196

215197
/** Callocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
@@ -359,7 +341,7 @@ void FIRPopulateProtoWithCommonInfoFromApp(logs_proto_mobilesdk_ios_ICoreConfigu
359341
config->icore_version = FIREncodeString(libraryVersionID);
360342
}
361343

362-
NSString *deviceModel = [FIRCoreDiagnostics deviceModel];
344+
NSString *deviceModel = [GULAppEnvironmentUtil deviceModel];
363345
if (deviceModel.length) {
364346
config->device_model = FIREncodeString(deviceModel);
365347
}

FirebaseCore/Sources/FIRApp.m

Lines changed: 14 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
#import "FirebaseCore/Sources/FIRBundleUtil.h"
2929
#import "FirebaseCore/Sources/FIRComponentContainerInternal.h"
3030
#import "FirebaseCore/Sources/FIRConfigurationInternal.h"
31+
#import "FirebaseCore/Sources/FIRFirebaseUserAgent.h"
32+
3133
#import "FirebaseCore/Sources/Private/FIRAppInternal.h"
3234
#import "FirebaseCore/Sources/Private/FIRCoreDiagnosticsConnector.h"
3335
#import "FirebaseCore/Sources/Private/FIRLibrary.h"
@@ -121,8 +123,6 @@ @implementation FIRApp
121123

122124
static NSMutableDictionary *sAllApps;
123125
static FIRApp *sDefaultApp;
124-
static NSMutableDictionary *sLibraryVersions;
125-
static dispatch_once_t sFirebaseUserAgentOnceToken;
126126

127127
+ (void)configure {
128128
FIROptions *options = [FIROptions defaultOptions];
@@ -282,9 +282,7 @@ + (void)resetApps {
282282
sDefaultApp = nil;
283283
[sAllApps removeAllObjects];
284284
sAllApps = nil;
285-
[sLibraryVersions removeAllObjects];
286-
sLibraryVersions = nil;
287-
sFirebaseUserAgentOnceToken = 0;
285+
[[self userAgent] reset];
288286
}
289287
}
290288

@@ -510,12 +508,7 @@ + (void)registerLibrary:(nonnull NSString *)name withVersion:(nonnull NSString *
510508
// add the name/version pair to the dictionary.
511509
if ([name rangeOfCharacterFromSet:disallowedSet].location == NSNotFound &&
512510
[version rangeOfCharacterFromSet:disallowedSet].location == NSNotFound) {
513-
@synchronized(self) {
514-
if (!sLibraryVersions) {
515-
sLibraryVersions = [[NSMutableDictionary alloc] init];
516-
}
517-
sLibraryVersions[name] = version;
518-
}
511+
[[self userAgent] setValue:version forComponent:name];
519512
} else {
520513
FIRLogError(kFIRLoggerCore, @"I-COR000027",
521514
@"The library name (%@) or version number (%@) contain invalid characters. "
@@ -556,72 +549,18 @@ + (void)registerInternalLibrary:(nonnull Class<FIRLibrary>)library
556549
[self registerLibrary:name withVersion:version];
557550
}
558551

559-
+ (NSString *)firebaseUserAgent {
560-
@synchronized(self) {
561-
dispatch_once(&sFirebaseUserAgentOnceToken, ^{
562-
// Report Firebase version for useragent string
563-
[FIRApp registerLibrary:@"fire-ios" withVersion:FIRFirebaseVersion()];
564-
565-
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
566-
NSString *xcodeVersion = info[@"DTXcodeBuild"];
567-
NSString *sdkVersion = info[@"DTSDKBuild"];
568-
if (xcodeVersion) {
569-
[FIRApp registerLibrary:@"xcode" withVersion:xcodeVersion];
570-
}
571-
if (sdkVersion) {
572-
[FIRApp registerLibrary:@"apple-sdk" withVersion:sdkVersion];
573-
}
574-
575-
NSString *swiftFlagValue = [self hasSwiftRuntime] ? @"true" : @"false";
576-
[FIRApp registerLibrary:@"swift" withVersion:swiftFlagValue];
577-
578-
[FIRApp registerLibrary:kFIRAppDiagnosticsApplePlatformPrefix
579-
withVersion:[self applePlatform]];
580-
});
581-
582-
NSMutableArray<NSString *> *libraries =
583-
[[NSMutableArray<NSString *> alloc] initWithCapacity:sLibraryVersions.count];
584-
for (NSString *libraryName in sLibraryVersions) {
585-
[libraries addObject:[NSString stringWithFormat:@"%@/%@", libraryName,
586-
sLibraryVersions[libraryName]]];
587-
}
588-
[libraries sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
589-
return [libraries componentsJoinedByString:@" "];
590-
}
591-
}
592-
593-
+ (BOOL)hasSwiftRuntime {
594-
// The class
595-
// [Swift._SwiftObject](https://github.com/apple/swift/blob/5eac3e2818eb340b11232aff83edfbd1c307fa03/stdlib/public/runtime/SwiftObject.h#L35)
596-
// is a part of Swift runtime, so it should be present if Swift runtime is available.
597-
598-
BOOL hasSwiftRuntime =
599-
objc_lookUpClass("Swift._SwiftObject") != nil ||
600-
// Swift object class name before
601-
// https://github.com/apple/swift/commit/9637b4a6e11ddca72f5f6dbe528efc7c92f14d01
602-
objc_getClass("_TtCs12_SwiftObject") != nil;
603-
604-
return hasSwiftRuntime;
552+
+ (FIRFirebaseUserAgent *)userAgent {
553+
static dispatch_once_t onceToken;
554+
static FIRFirebaseUserAgent *_userAgent;
555+
dispatch_once(&onceToken, ^{
556+
_userAgent = [[FIRFirebaseUserAgent alloc] init];
557+
[_userAgent setValue:FIRFirebaseVersion() forComponent:@"fire-ios"];
558+
});
559+
return _userAgent;
605560
}
606561

607-
+ (NSString *)applePlatform {
608-
NSString *applePlatform = @"unknown";
609-
610-
// When a Catalyst app is run on macOS then both `TARGET_OS_MACCATALYST` and `TARGET_OS_IOS` are
611-
// `true`, which means the condition list is order-sensitive.
612-
#if TARGET_OS_MACCATALYST
613-
applePlatform = @"maccatalyst";
614-
#elif TARGET_OS_IOS
615-
applePlatform = @"ios";
616-
#elif TARGET_OS_TV
617-
applePlatform = @"tvos";
618-
#elif TARGET_OS_OSX
619-
applePlatform = @"macos";
620-
#elif TARGET_OS_WATCH
621-
applePlatform = @"watchos";
622-
#endif
623-
624-
return applePlatform;
562+
+ (NSString *)firebaseUserAgent {
563+
return [[self userAgent] firebaseUserAgent];
625564
}
626565

627566
- (void)checkExpectedBundleID {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import <Foundation/Foundation.h>
18+
19+
NS_ASSUME_NONNULL_BEGIN
20+
21+
@interface FIRFirebaseUserAgent : NSObject
22+
23+
/** Returns the firebase user agent which consists of environment part and the components added via
24+
* `setValue:forComponent` method. */
25+
- (NSString *)firebaseUserAgent;
26+
27+
/** Sets value associated with the specified component. If value is `nil` then the component is
28+
* removed. */
29+
- (void)setValue:(nullable NSString *)value forComponent:(NSString *)componentName;
30+
31+
/** Resets manually added components. */
32+
- (void)reset;
33+
34+
@end
35+
36+
NS_ASSUME_NONNULL_END
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#import "FirebaseCore/Sources/FIRFirebaseUserAgent.h"
18+
19+
#import <GoogleUtilities/GULAppEnvironmentUtil.h>
20+
21+
@interface FIRFirebaseUserAgent ()
22+
23+
@property(nonatomic, readonly) NSMutableDictionary<NSString *, NSString *> *valuesByComponent;
24+
@property(nonatomic, readonly) NSString *firebaseUserAgent;
25+
26+
@end
27+
28+
@implementation FIRFirebaseUserAgent
29+
30+
@synthesize firebaseUserAgent = _firebaseUserAgent;
31+
32+
- (instancetype)init {
33+
self = [super init];
34+
if (self) {
35+
_valuesByComponent = [[[self class] environmentComponents] mutableCopy];
36+
}
37+
return self;
38+
}
39+
40+
- (NSString *)firebaseUserAgent {
41+
@synchronized(self) {
42+
if (_firebaseUserAgent == nil) {
43+
__block NSMutableArray<NSString *> *components =
44+
[[NSMutableArray<NSString *> alloc] initWithCapacity:self.valuesByComponent.count];
45+
[self.valuesByComponent
46+
enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull name, NSString *_Nonnull value,
47+
BOOL *_Nonnull stop) {
48+
[components addObject:[NSString stringWithFormat:@"%@/%@", name, value]];
49+
}];
50+
[components sortUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
51+
_firebaseUserAgent = [components componentsJoinedByString:@" "];
52+
}
53+
return _firebaseUserAgent;
54+
}
55+
}
56+
57+
- (void)setValue:(nullable NSString *)value forComponent:(NSString *)componentName {
58+
@synchronized(self) {
59+
self.valuesByComponent[componentName] = value;
60+
// Reset cached user agent string.
61+
_firebaseUserAgent = nil;
62+
}
63+
}
64+
65+
- (void)reset {
66+
@synchronized(self) {
67+
// Reset components.
68+
_valuesByComponent = [[[self class] environmentComponents] mutableCopy];
69+
// Reset cached user agent string.
70+
_firebaseUserAgent = nil;
71+
}
72+
}
73+
74+
#pragma mark - Environment components
75+
76+
+ (NSDictionary<NSString *, NSString *> *)environmentComponents {
77+
NSMutableDictionary<NSString *, NSString *> *components = [NSMutableDictionary dictionary];
78+
79+
NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
80+
NSString *xcodeVersion = info[@"DTXcodeBuild"];
81+
NSString *appleSdkVersion = info[@"DTSDKBuild"];
82+
83+
NSString *swiftFlagValue = [GULAppEnvironmentUtil hasSwiftRuntime] ? @"true" : @"false";
84+
NSString *isFromAppstoreFlagValue = [GULAppEnvironmentUtil isFromAppStore] ? @"true" : @"false";
85+
86+
components[@"apple-platform"] = [GULAppEnvironmentUtil applePlatform];
87+
components[@"apple-sdk"] = appleSdkVersion;
88+
components[@"appstore"] = isFromAppstoreFlagValue;
89+
components[@"deploy"] = [GULAppEnvironmentUtil deploymentType];
90+
components[@"device"] = [GULAppEnvironmentUtil deviceModel];
91+
components[@"os-version"] = [GULAppEnvironmentUtil systemVersion];
92+
components[@"swift"] = swiftFlagValue;
93+
components[@"xcode"] = xcodeVersion;
94+
95+
return [components copy];
96+
}
97+
98+
@end

FirebaseCore/Tests/SwiftUnit/FirebaseAppTests.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,12 @@ class FirebaseAppTests: XCTestCase {
329329
waitForExpectations(timeout: 1)
330330
}
331331

332+
// MARK: - Firebase User Agent
333+
334+
func testFirebaseUserAgent_SwiftRuntime() {
335+
XCTAssertTrue(FirebaseApp.firebaseUserAgent().contains("swift/true"))
336+
}
337+
332338
// MARK: - Helpers
333339

334340
private func expectAppConfigurationNotification(appName: String, isDefaultApp: Bool) {

0 commit comments

Comments
 (0)