Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ cmake_minimum_required(VERSION 3.22)

set (CMAKE_CXX_STANDARD 14)
set (CMAKE_CXX_STANDARD_REQUIRED YES) # Don't fall back to an earlier version.
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
add_compile_definitions(_GNU_SOURCE)
endif()

# Turn on virtual folders for visual studio
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
Expand Down Expand Up @@ -96,8 +99,14 @@ set(FIREBASE_XCODE_TARGET_FORMAT "frameworks" CACHE STRING
set(FIREBASE_CPP_SDK_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR})

project (firebase NONE)

set(CMAKE_Swift_LANGUAGE_VERSION 5.9)

enable_language(C)
enable_language(CXX)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS" OR CMAKE_SYSTEM_NAME STREQUAL "tvOS")
enable_language(Swift)
endif()

if(NOT DEFINED CMAKE_CXX_COMPILER_LAUNCHER)
find_program(CCACHE_PROGRAM ccache)
Expand Down Expand Up @@ -152,6 +161,7 @@ if(DESKTOP AND APPLE)
endif()

if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_DISABLE_AVAILABILITY -D_LIBCPP_HAS_NO_VERBOSE_ABORT")
# For iOS and macOS, disable nullability completeness warning, as it makes the
# build output for our Objective-C++ files much too verbose.
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-nullability-completeness")
Expand Down Expand Up @@ -356,6 +366,7 @@ endif()
# Include Firestore's external build early to resolve conflicts on packages.
if(FIRESTORE_USE_EXTERNAL_CMAKE_BUILD)
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_DISABLE_AVAILABILITY -D_LIBCPP_HAS_NO_VERBOSE_ABORT")
# On Apple, set GRPC_BAZEL_BUILD to fix an issue with __thread.
# Setting this define makes gRPC use pthread's thread-local storage
# instead of GCC's.
Expand Down Expand Up @@ -477,6 +488,7 @@ if(DESKTOP)
-DUSE_LIBUV=1
)
elseif(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LIBCPP_DISABLE_AVAILABILITY -D_LIBCPP_HAS_NO_VERBOSE_ABORT")
set(websockets_additional_defines
${websockets_additional_defines}
-DUSE_LIBUV=1
Expand Down
90 changes: 82 additions & 8 deletions analytics/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ set(android_SRCS

# Source files used by the iOS implementation.
set(ios_SRCS
src/analytics_ios.mm)
src/analytics_ios.mm
src/ios/swift/AppleTransactionVerifier.swift)

# Source files used by the desktop / stub implementation.
set(desktop_SRCS
Expand All @@ -88,8 +89,12 @@ if(ANDROID)
set(analytics_platform_SRCS
"${android_SRCS}")
elseif(IOS)
if(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
message(FATAL_ERROR "Swift is not supported by the 'Unix Makefiles' generator on iOS. Please use the Xcode generator (-G Xcode) or Ninja (-G Ninja).")
endif()
set(analytics_platform_SRCS
"${ios_SRCS}")

else()
set(analytics_platform_SRCS
"${desktop_SRCS}")
Expand All @@ -99,6 +104,9 @@ add_library(firebase_analytics STATIC
${common_SRCS}
${analytics_platform_SRCS})


add_dependencies(firebase_analytics FIREBASE_ANALYTICS_GENERATED_HEADERS)

set_property(TARGET firebase_analytics PROPERTY FOLDER "Firebase Cpp")

# Set up the dependency on Firebase App.
Expand All @@ -122,20 +130,86 @@ target_compile_definitions(firebase_analytics
-DINTERNAL_EXPERIMENTAL=1
)
# Automatically include headers that might not be declared.
if(MSVC)
add_definitions(/FI"assert.h" /FI"string.h" /FI"stdint.h")
if(IOS)
if(MSVC)
target_compile_options(firebase_analytics PRIVATE
$<$<NOT:$<COMPILE_LANGUAGE:Swift>>:/FI"assert.h">
$<$<NOT:$<COMPILE_LANGUAGE:Swift>>:/FI"string.h">
$<$<NOT:$<COMPILE_LANGUAGE:Swift>>:/FI"stdint.h">)
else()
target_compile_options(firebase_analytics PRIVATE
$<$<NOT:$<COMPILE_LANGUAGE:Swift>>:SHELL:-include assert.h -include string.h>
)
endif()
else()
add_definitions(-include assert.h -include string.h)
if(MSVC)
target_compile_options(firebase_analytics PRIVATE
/FI"assert.h" /FI"string.h" /FI"stdint.h")
else()
target_compile_options(firebase_analytics PRIVATE
SHELL:-include assert.h -include string.h
)
endif()
endif()

if(ANDROID)
firebase_cpp_proguard_file(analytics)
elseif(IOS)
# Enable Automatic Reference Counting (ARC) and Bitcode.
# Enable Automatic Reference Counting (ARC) and Bitcode specifically for Objective-C++ files.
# Note: -fembed-bitcode is placed here for src/analytics_ios.mm so that it is not passed
# to the Swift compiler, which does not support the flag.
set_source_files_properties(src/analytics_ios.mm PROPERTIES COMPILE_OPTIONS "-fobjc-arc;-fembed-bitcode")

if(CMAKE_GENERATOR STREQUAL "Xcode")
target_include_directories(firebase_analytics PRIVATE "$(DERIVED_FILE_DIR)")
target_compile_options(firebase_analytics PRIVATE
"-I$(OBJECT_FILE_DIR_normal)/$(CURRENT_ARCH)"
)
else()
target_include_directories(firebase_analytics PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")
endif()

# Swift needs to find the FirebaseAnalytics module from CocoaPods
set(pods_dir "${FIREBASE_POD_DIR}/Pods")

# Point to the base directories containing the .xcframework folders.
# Xcode natively handles XCFrameworks and will pick the right slice automatically.
# Determine the xcframework architecture slice based on the target platform
# and if it is running on simulator or device.
string(TOLOWER "${CMAKE_OSX_SYSROOT}" sysroot_lower)
if(CMAKE_SYSTEM_NAME STREQUAL "tvOS")
if(sysroot_lower MATCHES "simulator")
set(analytics_slice "tvos-arm64_x86_64-simulator")
else()
set(analytics_slice "tvos-arm64")
endif()
else()
if(sysroot_lower MATCHES "simulator")
set(analytics_slice "ios-arm64_x86_64-simulator")
else()
set(analytics_slice "ios-arm64")
endif()
endif()

set(analytics_framework_dir "${pods_dir}/FirebaseAnalytics/Frameworks/FirebaseAnalytics.xcframework/${analytics_slice}")
set(measurement_framework_dir "${pods_dir}/GoogleAppMeasurement/Frameworks/GoogleAppMeasurement.xcframework/${analytics_slice}")
Comment on lines +194 to +195
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The paths for FirebaseAnalytics.xcframework and GoogleAppMeasurement.xcframework are hardcoded based on a specific CocoaPods directory structure. This may cause build failures if the environment uses a different version of the Firebase Apple SDK or a different dependency management setup. Consider using CMake variables or a more flexible discovery mechanism to locate these frameworks.


target_compile_options(firebase_analytics
PUBLIC "-fobjc-arc" "-fembed-bitcode")
target_link_libraries(firebase_analytics
PUBLIC "-fembed-bitcode")
PRIVATE
$<$<COMPILE_LANGUAGE:Swift>:-F${analytics_framework_dir}>
$<$<COMPILE_LANGUAGE:Swift>:-F${measurement_framework_dir}>
)

target_link_options(firebase_analytics
PUBLIC
"-F${analytics_framework_dir}"
"-F${measurement_framework_dir}"
)

# Prevent Xcode from trying to build or evaluate headers for unused architectures
set_target_properties(firebase_analytics PROPERTIES
XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH "YES"
)

setup_pod_headers(
firebase_analytics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,8 @@
529226F71C85F68000C89379 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_CPLUSPLUSFLAGS = "$(inherited) -D_LIBCPP_HAS_NO_VERBOSE_ABORT -D_LIBCPP_DISABLE_AVAILABILITY";
OTHER_LDFLAGS = "$(inherited) -Wl,-weak-lswift_Concurrency";
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
Expand Down Expand Up @@ -418,6 +420,8 @@
529226F81C85F68000C89379 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_CPLUSPLUSFLAGS = "$(inherited) -D_LIBCPP_HAS_NO_VERBOSE_ABORT -D_LIBCPP_DISABLE_AVAILABILITY";
OTHER_LDFLAGS = "$(inherited) -Wl,-weak-lswift_Concurrency";
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
Expand Down Expand Up @@ -455,6 +459,8 @@
529226FA1C85F68000C89379 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_CPLUSPLUSFLAGS = "$(inherited) -D_LIBCPP_HAS_NO_VERBOSE_ABORT -D_LIBCPP_DISABLE_AVAILABILITY";
OTHER_LDFLAGS = "$(inherited) -Wl,-weak-lswift_Concurrency";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_MODULES = YES;
Expand Down Expand Up @@ -487,6 +493,8 @@
529226FB1C85F68000C89379 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_CPLUSPLUSFLAGS = "$(inherited) -D_LIBCPP_HAS_NO_VERBOSE_ABORT -D_LIBCPP_DISABLE_AVAILABILITY";
OTHER_LDFLAGS = "$(inherited) -Wl,-weak-lswift_Concurrency";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
CLANG_ENABLE_MODULES = YES;
Expand Down Expand Up @@ -518,6 +526,8 @@
BC1D665F2668D10A005DC2DA /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_CPLUSPLUSFLAGS = "$(inherited) -D_LIBCPP_HAS_NO_VERBOSE_ABORT -D_LIBCPP_DISABLE_AVAILABILITY";
OTHER_LDFLAGS = "$(inherited) -Wl,-weak-lswift_Concurrency";
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ANALYZER_NONNULL = YES;
Expand Down Expand Up @@ -572,6 +582,8 @@
BC1D66602668D10A005DC2DA /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
OTHER_CPLUSPLUSFLAGS = "$(inherited) -D_LIBCPP_HAS_NO_VERBOSE_ABORT -D_LIBCPP_DISABLE_AVAILABILITY";
OTHER_LDFLAGS = "$(inherited) -Wl,-weak-lswift_Concurrency";
ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CLANG_ANALYZER_NONNULL = YES;
Expand Down
31 changes: 31 additions & 0 deletions analytics/integration_test/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,37 @@ Building and Running the sample
"Analytics" tab accessible from
[https://firebase.google.com/console/](https://firebase.google.com/console/).

#### iOS Testing LogAppleTransaction

To test the log apple transaction function, you should use the existing test app and xcode's simulated transactions.
The manual test will involve running the integration test: `firebase_analytics_test/TestLogAppleTransaction` and verifying that it logs a transaction to the console.

- Step 1: Set up the Local Xcode Environment
- In Xcode, go to File > New > File from Template and create a StoreKit Configuration File (.storekit).
- Give the configuration any name.
- Target both integration_test and integration_test_tvos
- Add at least one dummy product to this file.
- Do this by selecting the file in xcode and clicking the + button in the bottom left corner.
- Choose a Non-Consumable in app purchase product.
- Give it a Reference name of your choice (e.g. "ReferenceAppleIapProduct").
- Give it a Product ID of your choice (e.g. "com.example.nonconsumable").
- Make the app use the store kit file. In the top bar go to Product > Scheme > Edit Scheme... >
- In the left hand menu select Run
- Select the Options tab on the right
- Set the StoreKit Configuration dropdown to your new .storekit file.
- Step 2: Validate logging transactions
- Try running the test app with the dummy transaction ID. It should return an error from the
LogAppleTransactions function.
- After runnign the app once you can create a simulated transaction for testing.
- To create a simulated transaction ID:
- Go to Debug > StoreKit > Manage Transactions.
- Click the + button in the bottom left corner.
- Select the Non-Consumable in app purchase product.
- Copy the transaction ID to the test case and replace 'dummy_transaction_id' with your new transaction ID. e.g. '0'
- Make sure to update the testcase to now expect success.
- Then try running the test app again with the simulated transaction ID.
- It should log the transaction to the console. Both the Xcode console and firebase console should show a log for an in app purchase.

### Android
- Register your Android app with Firebase.
- Create a new app on the [Firebase console](https://firebase.google.com/console/), and attach
Expand Down
19 changes: 19 additions & 0 deletions analytics/integration_test/src/integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#include <ctime>
#include <future>

#if defined(__APPLE__)
#include <TargetConditionals.h>
#endif

#include "app_framework.h" // NOLINT
#include "firebase/analytics.h"
#include "firebase/analytics/event_names.h"
Expand Down Expand Up @@ -299,6 +303,21 @@ TEST_F(FirebaseAnalyticsTest, TestDesktopDebugMode) {
firebase::analytics::SetDesktopDebugMode(false);
}

TEST_F(FirebaseAnalyticsTest, TestLogAppleTransaction) {
auto future =
firebase::analytics::LogAppleTransaction("dummy_transaction_id");
WaitForCompletionAnyResult(future, "LogAppleTransaction");
#if defined(__APPLE__) && (TARGET_OS_IOS || TARGET_OS_TV)
// On iOS/tvOS, passing a dummy transaction ID will fail to find a verified
// transaction.
EXPECT_NE(future.error(), 0);
#else
// On Android and Desktop (including macOS), LogAppleTransaction is a no-op
// that returns success.
EXPECT_EQ(future.error(), 0);
#endif
}

TEST_F(FirebaseAnalyticsTest, TestLogEvents) {
// Log an event with no parameters.
firebase::analytics::LogEvent(firebase::analytics::kEventLogin);
Expand Down
24 changes: 24 additions & 0 deletions analytics/src/analytics_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,30 @@ void LogEvent(const char* name) {
LogEvent(name, nullptr, static_cast<size_t>(0));
}

/// Log an Apple StoreKit 2 transaction. This is a no-op on Android and returns
/// success.
Future<void> LogAppleTransaction(const char* transaction_id) {
auto* api = internal::FutureData::Get() ? internal::FutureData::Get()->api()
: nullptr;
if (!api) {
return Future<void>();
}
const auto future_handle =
api->SafeAlloc<void>(internal::kAnalyticsFnLogAppleTransaction);
api->Complete(future_handle, 0, "");
return Future<void>(api, future_handle.get());
}

Future<void> LogAppleTransactionLastResult() {
auto* api = internal::FutureData::Get() ? internal::FutureData::Get()->api()
: nullptr;
if (!api) {
return Future<void>();
}
return static_cast<const Future<void>&>(
api->LastResult(internal::kAnalyticsFnLogAppleTransaction));
}

// Log an event with associated parameters.
void LogEvent(const char* name, const Parameter* parameters,
size_t number_of_parameters) {
Expand Down
5 changes: 3 additions & 2 deletions analytics/src/analytics_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ namespace analytics {
namespace internal {

enum AnalyticsFn {
kAnalyticsFnGetAnalyticsInstanceId,
kAnalyticsFnGetAnalyticsInstanceId = 0,
kAnalyticsFnGetSessionId,
kAnalyticsFnCount
kAnalyticsFnLogAppleTransaction,
kAnalyticsFnCount,
};

// Data structure which holds the Future API for this module.
Expand Down
24 changes: 24 additions & 0 deletions analytics/src/analytics_desktop.cc
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,30 @@ void LogEvent(const char* name) {
LogEvent(name, static_cast<const Parameter*>(nullptr), 0);
}

/// Log an Apple StoreKit 2 transaction. This is a no-op on Desktop and returns
/// success.
Future<void> LogAppleTransaction(const char* transaction_id) {
auto* api = internal::FutureData::Get() ? internal::FutureData::Get()->api()
: nullptr;
if (!api) {
return Future<void>();
}
const auto future_handle =
api->SafeAlloc<void>(internal::kAnalyticsFnLogAppleTransaction);
api->Complete(future_handle, 0, "");
return Future<void>(api, future_handle.get());
}

Future<void> LogAppleTransactionLastResult() {
auto* api = internal::FutureData::Get() ? internal::FutureData::Get()->api()
: nullptr;
if (!api) {
return Future<void>();
}
return static_cast<const Future<void>&>(
api->LastResult(internal::kAnalyticsFnLogAppleTransaction));
}

void LogEvent(const char* name, const char* parameter_name,
const char* parameter_value) {
if (parameter_name == nullptr) {
Expand Down
Loading
Loading