Skip to content

Commit b66ede5

Browse files
committed
Merge branch 'main' into mc/messaging
2 parents e826c9d + d1f7c7e commit b66ede5

File tree

21 files changed

+256
-199
lines changed

21 files changed

+256
-199
lines changed

Crashlytics/UnitTests/FIRCLSUtilityTests.m

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -77,56 +77,60 @@ - (void)testHexToStringWithNonPrintableCharacters {
7777
XCTAssertEqualObjects([NSString stringWithUTF8String:string], @"52d04e1f", @"");
7878
}
7979

80-
- (void)testRedactUUIDWithExpectedPattern {
81-
const char* readonly = "CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
82-
"(45D62CC2-CFB5-4E33-AB61-B0684627F1B6) - Runtime: iOS 13.4 (17E8260) - "
83-
"DeviceType: iPhone SE (2nd generation)";
84-
size_t len = strlen(readonly);
85-
char message[len];
86-
strcpy(message, readonly);
87-
88-
FIRCLSRedactUUID(message);
89-
90-
NSString* actual = [NSString stringWithUTF8String:message];
91-
NSString* expected = @"CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
92-
@"(********-****-****-****-************) - Runtime: iOS 13.4 (17E8260) - "
93-
@"DeviceType: iPhone SE (2nd generation)";
94-
95-
XCTAssertEqualObjects(actual, expected);
96-
}
97-
98-
- (void)testRedactUUIDWithMalformedPattern {
99-
const char* readonly = "CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
100-
"(45D62CC2-CFB5-4E33-AB61-B0684627F1B6";
101-
size_t len = strlen(readonly);
102-
char message[len];
103-
strcpy(message, readonly);
104-
105-
FIRCLSRedactUUID(message);
106-
107-
NSString* actual = [NSString stringWithUTF8String:message];
108-
NSString* expected = @"CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
109-
@"(45D62CC2-CFB5-4E33-AB61-B0684627F1B6";
110-
111-
XCTAssertEqualObjects(actual, expected);
112-
}
113-
114-
- (void)testRedactUUIDWithoutUUID {
115-
const char* readonly = "Fatal error: file /Users/test/src/foo/bar/ViewController.swift, line 25";
116-
size_t len = strlen(readonly);
117-
char message[len];
118-
strcpy(message, readonly);
119-
120-
FIRCLSRedactUUID(message);
121-
122-
NSString* actual = [NSString stringWithUTF8String:message];
123-
NSString* expected = @"Fatal error: file /Users/test/src/foo/bar/ViewController.swift, line 25";
124-
125-
XCTAssertEqualObjects(actual, expected);
126-
}
127-
128-
- (void)testRedactUUIDWithNull {
129-
char* message = NULL;
130-
XCTAssertNoThrow(FIRCLSRedactUUID(message));
80+
- (void)testRedactUUID {
81+
// Define a local struct to hold the test data
82+
struct TestCase {
83+
const char *name; // Name of the test case
84+
const char *readonly; // Input string
85+
const char *expected; // Expected output string
86+
};
87+
88+
// Initialize an array of test cases
89+
struct TestCase tests[] = {
90+
{.name = "Test with valid UUID",
91+
.readonly = "CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
92+
"(45D62CC2-CFB5-4E33-AB61-B0684627F1B6) - Runtime: iOS 13.4 (17E8260) - "
93+
"DeviceType: iPhone SE (2nd generation)",
94+
.expected = "CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
95+
"(********-****-****-****-************) - Runtime: iOS 13.4 (17E8260) - "
96+
"DeviceType: iPhone SE (2nd generation)"},
97+
{.name = "Test with no UUID",
98+
.readonly = "Example with no UUID",
99+
.expected = "Example with no UUID"},
100+
{.name = "Test with only UUID",
101+
.readonly = "(12345678-1234-1234-1234-123456789012)",
102+
.expected = "(********-****-****-****-************)"},
103+
{.name = "Test with malformed UUID pattern",
104+
.readonly = "CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
105+
"(45D62CC2-CFB5-4E33-AB61-B0684627F1B6",
106+
.expected = "CoreSimulator 704.12.1 - Device: iPhone SE (2nd generation) "
107+
"(45D62CC2-CFB5-4E33-AB61-B0684627F1B6"},
108+
{.name = "Test with other error string",
109+
.readonly = "Fatal error: file "
110+
"/Users/test/src/foo/bar/ViewController.swift, line 25",
111+
.expected = "Fatal error: file "
112+
"/Users/test/src/foo/bar/ViewController.swift, line 25"},
113+
{.name = "Test with NULL input", .readonly = NULL, .expected = NULL}};
114+
115+
// Loop over the array of test cases
116+
for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
117+
if (tests[i].readonly == NULL) {
118+
XCTAssertNoThrow(FIRCLSRedactUUID(NULL));
119+
continue;
120+
}
121+
122+
// copy the message because the function modifies the input
123+
// and the input is const
124+
char message[256];
125+
snprintf(message, sizeof(message), "%s", tests[i].readonly);
126+
127+
// Redact the UUID
128+
FIRCLSRedactUUID(message);
129+
130+
// Compare the result with the expected output
131+
NSString *actual = [NSString stringWithUTF8String:message];
132+
NSString *expected = [NSString stringWithUTF8String:tests[i].expected];
133+
XCTAssertEqualObjects(actual, expected, @"Test named: '%s' failed", tests[i].name);
134+
}
131135
}
132136
@end

FirebaseAnalytics.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Pod::Spec.new do |s|
1313
s.authors = 'Google, Inc.'
1414

1515
s.source = {
16-
:http => 'https://dl.google.com/firebase/ios/analytics/f4ae251137d1f29b/FirebaseAnalytics-11.10.0.tar.gz'
16+
:http => 'https://dl.google.com/firebase/ios/analytics/9b3638b3f6db3f4f/FirebaseAnalytics-11.11.0.tar.gz'
1717
}
1818

1919
s.cocoapods_version = '>= 1.12.0'

FirebaseAuth/Sources/Swift/Auth/Auth.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2348,7 +2348,7 @@ extension Auth: AuthInterop {
23482348

23492349
/// The configuration object comprising of parameters needed to make a request to Firebase
23502350
/// Auth's backend.
2351-
var requestConfiguration: AuthRequestConfiguration
2351+
let requestConfiguration: AuthRequestConfiguration
23522352

23532353
let backend: AuthBackend
23542354

FirebaseVertexAI/CHANGELOG.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 11.11.0
2+
- [added] Emits a warning when attempting to use an incompatible model with
3+
`GenerativeModel` or `ImagenModel`. (#14610)
4+
15
# 11.10.0
26
- [feature] The Vertex AI SDK no longer requires `@preconcurrency` when imported in Swift 6.
37
- [feature] The Vertex AI Sample App now includes an image generation example.
@@ -7,12 +11,12 @@
711
are required. (#14558)
812

913
# 11.9.0
10-
- [feature] **Public Preview**: Added support for generating images using the
11-
Imagen 3 model.
14+
- [feature] **Public Preview**: Added support for
15+
[generating images](https://firebase.google.com/docs/vertex-ai/generate-images-imagen?platform=ios)
16+
using the Imagen 3 models.
1217
<br /><br />
13-
Note: This feature is in Public Preview, which means that the it is not
14-
subject to any SLA or deprecation policy and could change in
15-
backwards-incompatible ways.
18+
Note: This feature is in Public Preview, which means that it is not subject to
19+
any SLA or deprecation policy and could change in backwards-incompatible ways.
1620
- [feature] Added support for modality-based token count. (#14406)
1721

1822
# 11.6.0

FirebaseVertexAI/Sources/GenerativeModel.swift

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ import Foundation
2020
/// content based on various input types.
2121
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
2222
public final class GenerativeModel: Sendable {
23+
/// Model name prefix to identify Gemini models.
24+
static let geminiModelNamePrefix = "gemini-"
25+
2326
/// The resource name of the model in the backend; has the format "models/model-name".
2427
let modelResourceName: String
2528

@@ -50,7 +53,8 @@ public final class GenerativeModel: Sendable {
5053
/// Initializes a new remote model with the given parameters.
5154
///
5255
/// - Parameters:
53-
/// - name: The name of the model to use, for example `"gemini-1.0-pro"`.
56+
/// - modelResourceName: The resource name of the model to use, for example
57+
/// `"projects/{project-id}/locations/{location-id}/publishers/google/models/{model-name}"`.
5458
/// - firebaseInfo: Firebase data used by the SDK, including project ID and API key.
5559
/// - apiConfig: Configuration for the backend API used by this model.
5660
/// - generationConfig: The content generation parameters your model should use.
@@ -61,7 +65,7 @@ public final class GenerativeModel: Sendable {
6165
/// only text content is supported.
6266
/// - requestOptions: Configuration parameters for sending requests to the backend.
6367
/// - urlSession: The `URLSession` to use for requests; defaults to `URLSession.shared`.
64-
init(name: String,
68+
init(modelResourceName: String,
6569
firebaseInfo: FirebaseInfo,
6670
apiConfig: APIConfig,
6771
generationConfig: GenerationConfig? = nil,
@@ -71,7 +75,7 @@ public final class GenerativeModel: Sendable {
7175
systemInstruction: ModelContent? = nil,
7276
requestOptions: RequestOptions,
7377
urlSession: URLSession = .shared) {
74-
modelResourceName = name
78+
self.modelResourceName = modelResourceName
7579
self.apiConfig = apiConfig
7680
generativeAIService = GenerativeAIService(
7781
firebaseInfo: firebaseInfo,
@@ -98,7 +102,7 @@ public final class GenerativeModel: Sendable {
98102
`\(VertexLog.enableArgumentKey)` as a launch argument in Xcode.
99103
""")
100104
}
101-
VertexLog.debug(code: .generativeModelInitialized, "Model \(name) initialized.")
105+
VertexLog.debug(code: .generativeModelInitialized, "Model \(modelResourceName) initialized.")
102106
}
103107

104108
/// Generates content from String and/or image inputs, given to the model as a prompt, that are

FirebaseVertexAI/Sources/Types/Public/Imagen/ImagenModel.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ import Foundation
2828
/// could change in backwards-incompatible ways.
2929
@available(iOS 15.0, macOS 12.0, macCatalyst 15.0, tvOS 15.0, watchOS 8.0, *)
3030
public final class ImagenModel {
31+
/// Model name prefix to identify Imagen models.
32+
static let imagenModelNamePrefix = "imagen-"
33+
3134
/// The resource name of the model in the backend; has the format "models/model-name".
3235
let modelResourceName: String
3336

@@ -44,14 +47,14 @@ public final class ImagenModel {
4447
/// Configuration parameters for sending requests to the backend.
4548
let requestOptions: RequestOptions
4649

47-
init(name: String,
50+
init(modelResourceName: String,
4851
firebaseInfo: FirebaseInfo,
4952
apiConfig: APIConfig,
5053
generationConfig: ImagenGenerationConfig?,
5154
safetySettings: ImagenSafetySettings?,
5255
requestOptions: RequestOptions,
5356
urlSession: URLSession = .shared) {
54-
modelResourceName = name
57+
self.modelResourceName = modelResourceName
5558
self.apiConfig = apiConfig
5659
generativeAIService = GenerativeAIService(
5760
firebaseInfo: firebaseInfo,

FirebaseVertexAI/Sources/VertexAI.swift

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,15 @@ public class VertexAI {
7070
systemInstruction: ModelContent? = nil,
7171
requestOptions: RequestOptions = RequestOptions())
7272
-> GenerativeModel {
73+
if !modelName.starts(with: GenerativeModel.geminiModelNamePrefix) {
74+
VertexLog.warning(code: .unsupportedGeminiModel, """
75+
Unsupported Gemini model "\(modelName)"; see \
76+
https://firebase.google.com/docs/vertex-ai/models for a list supported Gemini model names.
77+
""")
78+
}
79+
7380
return GenerativeModel(
74-
name: modelResourceName(modelName: modelName),
81+
modelResourceName: modelResourceName(modelName: modelName),
7582
firebaseInfo: firebaseInfo,
7683
apiConfig: apiConfig,
7784
generationConfig: generationConfig,
@@ -102,8 +109,15 @@ public class VertexAI {
102109
public func imagenModel(modelName: String, generationConfig: ImagenGenerationConfig? = nil,
103110
safetySettings: ImagenSafetySettings? = nil,
104111
requestOptions: RequestOptions = RequestOptions()) -> ImagenModel {
112+
if !modelName.starts(with: ImagenModel.imagenModelNamePrefix) {
113+
VertexLog.warning(code: .unsupportedImagenModel, """
114+
Unsupported Imagen model "\(modelName)"; see \
115+
https://firebase.google.com/docs/vertex-ai/models for a list supported Imagen model names.
116+
""")
117+
}
118+
105119
return ImagenModel(
106-
name: modelResourceName(modelName: modelName),
120+
modelResourceName: modelResourceName(modelName: modelName),
107121
firebaseInfo: firebaseInfo,
108122
apiConfig: apiConfig,
109123
generationConfig: generationConfig,

FirebaseVertexAI/Sources/VertexLog.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,10 @@ enum VertexLog {
3333

3434
// Generative Model Configuration
3535
case generativeModelInitialized = 1000
36+
case unsupportedGeminiModel = 1001
3637

3738
// Imagen Model Configuration
39+
case unsupportedImagenModel = 1200
3840
case imagenInvalidJPEGCompressionQuality = 1201
3941

4042
// Network Errors
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import FirebaseCore
16+
17+
extension FirebaseApp {
18+
/// Configures a Firebase app with the specified name and Google Service Info plist file name.
19+
///
20+
/// - Parameters:
21+
/// - appName: The Firebase app's name; see ``FirebaseAppNames`` for app names with special
22+
/// meanings in the TestApp.
23+
/// - plistName: The file name of the Google Service Info plist, excluding the file extension;
24+
/// for the default app this is typically called `GoogleService-Info` but any file name may be
25+
/// used for other apps.
26+
static func configure(appName: String, plistName: String) {
27+
assert(!plistName.hasSuffix(".plist"), "The .plist file extension must be omitted.")
28+
guard let plistPath =
29+
Bundle.main.path(forResource: plistName, ofType: "plist") else {
30+
fatalError("The file '\(plistName).plist' was not found.")
31+
}
32+
guard let options = FirebaseOptions(contentsOfFile: plistPath) else {
33+
fatalError("Failed to parse options from '\(plistName).plist'.")
34+
}
35+
FirebaseApp.configure(name: appName, options: options)
36+
}
37+
}

FirebaseVertexAI/Tests/TestApp/Sources/TestApp.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ struct TestApp: App {
2424
// Configure default Firebase App
2525
FirebaseApp.configure()
2626

27+
// Configure a Firebase App that is the same as the default app but without App Check.
28+
// This is used for tests that should fail when App Check is not configured.
29+
FirebaseApp.configure(
30+
appName: FirebaseAppNames.appCheckNotConfigured,
31+
plistName: "GoogleService-Info"
32+
)
33+
2734
// Configure a Firebase App without a billing account (i.e., the "Spark" plan).
28-
guard let plistPath =
29-
Bundle.main.path(forResource: "GoogleService-Info-Spark", ofType: "plist") else {
30-
fatalError("The file 'GoogleService-Info-Spark.plist' was not found.")
31-
}
32-
guard let options = FirebaseOptions(contentsOfFile: plistPath) else {
33-
fatalError("Failed to parse options from 'GoogleService-Info-Spark.plist'.")
34-
}
35-
FirebaseApp.configure(name: FirebaseAppNames.spark, options: options)
35+
FirebaseApp.configure(appName: FirebaseAppNames.spark, plistName: "GoogleService-Info-Spark")
3636
}
3737

3838
var body: some Scene {

0 commit comments

Comments
 (0)