Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wip/firebasecmake #5

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
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
83 changes: 55 additions & 28 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,22 @@
# SPDX-License-Identifier: MIT

cmake_minimum_required(VERSION 3.24)
project(FirebaseQt VERSION 0.1.0 LANGUAGES CXX)

include(GNUInstallDirs)

find_package(QT NAMES Qt6 Qt5 COMPONENTS Core Network REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 5.10.0 COMPONENTS Core Network REQUIRED)
if(IOS)
set(LANGUAGES CXX Swift)
else()
set(LANGUAGES CXX)
endif()

include(FetchContent)
FetchContent_Declare(
firebase_cpp_sdk
URL https://dl.google.com/firebase/sdk/cpp/firebase_cpp_sdk_10.3.0.zip
project(FirebaseQt
VERSION 0.1.0
LANGUAGES ${LANGUAGES}
)
FetchContent_MakeAvailable(firebase_cpp_sdk)

set(CMAKE_AUTOMOC ON)

set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)

# Include our cmake modules
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules)

# As moc files are generated in the binary dir, tell CMake
# to always look for includes there:
Expand All @@ -32,14 +27,46 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

include(GNUInstallDirs)

find_package(QT NAMES Qt6 COMPONENTS Core Network REQUIRED)
find_package(Qt${QT_VERSION_MAJOR} 6.4 COMPONENTS Core Network REQUIRED)

if (DEFINED FIREBASE_CPP_SDK_DIR)
# Add Firebase libraries to the target using the function from the SDK.
add_subdirectory(${FIREBASE_CPP_SDK_DIR} libs EXCLUDE_FROM_ALL)
else()
include(FetchContent)
FetchContent_Declare(
firebase_cpp_sdk
URL https://dl.google.com/firebase/sdk/cpp/firebase_cpp_sdk_12.2.0.zip
)

# MakeAvailable will run the CMakeList.txt script that was downloaded
# but firebase_cpp_sdk_SOURCE_DIR that should populate FIREBASE_CPP_SDK_DIR
# before running that script is only defined after MakeAvailable
# so we set it here, in the hope FETCHCONTENT_BASE_DIR is not changed.
set(FIREBASE_CPP_SDK_DIR "${CMAKE_BINARY_DIR}/_deps/firebase_cpp_sdk-src")

FetchContent_MakeAvailable(firebase_cpp_sdk)
endif()
message(STATUS "FIREBASE_CPP_SDK_DIR=${FIREBASE_CPP_SDK_DIR}")

if(IOS)
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
if (NOT DEFINED FIREBASE_IOS_SDK_DIR)
message(FATAL_ERROR "FIREBASE_IOS_SDK_DIR not defined")
endif()
message(STATUS "FIREBASE_IOS_SDK_DIR=${FIREBASE_IOS_SDK_DIR}")
endif()

set(CMAKE_AUTOMOC ON)

if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX
"/usr" CACHE PATH "firebase admin default install prefix" FORCE)
endif()

add_definitions("-DLOCALSTATEDIR=\"${LOCALSTATEDIR}\"")

set(CMAKE_INSTALL_LIBDIR "${CMAKE_INSTALL_PREFIX}/lib/${CMAKE_LIBRARY_ARCHITECTURE}" CACHE PATH "Output directory for libraries")

option(BUILD_SHARED_LIBS "Build in shared lib mode" OFF)
Expand All @@ -49,18 +76,18 @@ include_directories(
)

# cmake config files
configure_file(${CMAKE_MODULE_PATH}/firebase-qt-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}Config.cmake
@ONLY
)
configure_file(${CMAKE_MODULE_PATH}/firebase-qt-config-version.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}ConfigVersion.cmake
@ONLY
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}ConfigVersion.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/FirebaseQt${QT_VERSION_MAJOR}/
)
# configure_file(${CMAKE_MODULE_PATH}/firebase-qt-config.cmake.in
# ${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}Config.cmake
# @ONLY
# )
# configure_file(${CMAKE_MODULE_PATH}/firebase-qt-config-version.cmake.in
# ${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}ConfigVersion.cmake
# @ONLY
# )
# install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}Config.cmake
# ${CMAKE_CURRENT_BINARY_DIR}/FirebaseQt${QT_VERSION_MAJOR}ConfigVersion.cmake
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/FirebaseQt${QT_VERSION_MAJOR}/
# )

install(EXPORT FirebaseTargets
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/FirebaseQt${QT_VERSION_MAJOR}/
Expand Down
97 changes: 85 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,105 @@ Supports:

## Example of usage

Google Firebase SDK use futures that runs on different threads, so in order to avoid a crash due updating a Qt GUI from the wrong thread always use Qt:: QueuedConnection.
Google Firebase SDK use futures that runs on different threads, so in order to avoid a crash due updating a Qt GUI from the wrong thread we internally use Qt:: QueuedConnection.

Initialize:

auto firebase = new FirebaseQtApp(this);
auto messaging = new FirebaseQtMessaging(firebase);
connect(messaging, &FirebaseQtMessaging::tokenReceived, this, &Central::firebaseUpdateMessagingToken, Qt::QueuedConnection);
connect(messaging, &FirebaseQtMessaging::messageReceived, this, &Central::firebaseOnMessage, Qt::QueuedConnection);
connect(messaging, &FirebaseQtMessaging::tokenReceived, this, &Central::firebaseUpdateMessagingToken);
connect(messaging, &FirebaseQtMessaging::messageReceived, this, &Central::firebaseOnMessage);

m_firebaseAuth = new FirebaseQtAuth(firebase);
connect(m_firebaseAuth, &FirebaseQtAuth::signInToken, this, &Central::firebaseUserToken, Qt::QueuedConnection);
connect(m_firebaseAuth, &FirebaseQtAuth::signInError, this, &Central::firebaseUserTokenError, Qt::QueuedConnection);
connect(m_firebaseAuth, &FirebaseQtAuth::signInToken, this, &Central::firebaseUserToken);
connect(m_firebaseAuth, &FirebaseQtAuth::signInError, this, &Central::firebaseUserTokenError);

m_firebaseAuthPhone = new FirebaseQtAuthPhone(m_firebaseAuth);
connect(m_firebaseAuthPhone, &FirebaseQtAuthPhone::codeSent, this, &Central::firebaseAuthCodeSent, Qt::QueuedConnection);
connect(m_firebaseAuthPhone, &FirebaseQtAuthPhone::verificationFailed, this, &Central::firebaseAuthFailed, Qt::QueuedConnection);
connect(m_firebaseAuthPhone, &FirebaseQtAuthPhone::verificationCompleted, this, &Central::firebaseAuthCompleted, Qt::QueuedConnection);
connect(m_firebaseAuthPhone, &FirebaseQtAuthPhone::codeSent, this, &Central::firebaseAuthCodeSent);
connect(m_firebaseAuthPhone, &FirebaseQtAuthPhone::verificationFailed, this, &Central::firebaseAuthFailed);
connect(m_firebaseAuthPhone, &FirebaseQtAuthPhone::verificationCompleted, this, &Central::firebaseAuthCompleted);

firebase->initialize();



Ask for a phone number authentication:

m_firebaseAuthPhone->verifyPhoneNumber("+55999999999");

## Compiling
## Compiling Android

This version only supports CMake, Firebase 12.3 has one link issue so for now we use 12.2.0
Firebase-qt can download the firebase_cpp-sdk for you but on gradle part you will need to
point to where it should be so we are not _that_ automated yet.

if(ANDROID)
set(FIREBASE_CPP_SDK_DIR "..path/to/firebase_cpp_sdk_12.2.0/")
include(FetchContent)
FetchContent_Declare(
firebase_qt
GIT_REPOSITORY https://github.com/cutelyst/firebase-qt.git
GIT_TAG some_sha
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(firebase_qt)
endif()


Now an important step is to update your gradle in you project/android director, the one that
QtCreator created it's template, this is essential else you will run into DEX issues,
this should be run in project_sources/android directory not on your build director.
Also you must comment all Qt variables else it will fail:

./gradlew wrapper --gradle-version 8.7

Next gradle.properties where you also put your firebase_cpp-sdk path:

systemProp.firebase_cpp_sdk.dir=..path/to/firebase_cpp_sdk_12.2.0/

Last build.gradle:

buildscript {
repositories {
google()
mavenCentral()
}

dependencies {
# Notice the 8.6 version here, not sure why but seems we always need to be one version less
# than what we updated
classpath 'com.android.tools.build:gradle:8.6.0'
classpath 'com.google.gms:google-services:4.4.2' // Google Services plugin

# the lines below won't need to be changed later as the path is read from gradle.properties
def firebase_cpp_sdk_dir = System.getProperty('firebase_cpp_sdk.dir')

gradle.ext.firebase_cpp_sdk_dir = "$firebase_cpp_sdk_dir"
apply from: "$firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle"
}
}

...
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services' // Google Services plugin
...

# This can be placed at the end of the file
# You must put here all modules you want from firebase
firebaseCpp.dependencies {
analytics
messaging
auth
}

### AndroidManifest.xml

With the above code you will get Firebase working, which can give you a valid device token to send notifications.
However in order to receive them when the app is running you must add what firebase-cpp-sdk/AndroidManafest.xml
has to your own AndroidManifest.xml.

To receive notifications when the app is not running or hidden it requires Notifications permissions that
should by programatically asked: `android.permission.POST_NOTIFICATIONS`.

## Compiling iOS (TODO)

This is the most chalenging part, especially on iOS, because qmake doesn't support CocoaPods, and I'm not sure how mature Qt CMake integration is for iOS, so you have to download the C++ SDK **and** the Firebase iOS framework, here is how my qmake looks like (I'm pretty sure I can omit some links in iOS but didn't have the time to check):

Expand Down
56 changes: 53 additions & 3 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@
set(firebase_qt_SRC
firebaseqtapp.cpp
firebaseqtabstractmodule.cpp
firebaseqtmessaging.cpp
)

set(firebase_qt_HEADERS
firebaseqtapp.h
firebaseqtmessaging.h
)

set(firebase_qt_HEADERS_PRIVATE
firebaseqtapp_p.h
)

set(target_app FirebaseQt${QT_VERSION_MAJOR}_App)
add_library(${target_app}
add_library(${target_app} OBJECT
${firebase_qt_SRC}
${firebase_qt_HEADERS}
${firebase_qt_HEADERS_PRIVATE}
ios.swift
)

#Add an alias so that library can be used inside the build tree, e.g. when testing
Expand Down Expand Up @@ -182,3 +181,54 @@ install(TARGETS ${target_messaging}
ARCHIVE DESTINATION lib COMPONENT devel
PUBLIC_HEADER DESTINATION include/firebase-qt${QT_VERSION_MAJOR}/FirebaseQt COMPONENT devel
)


### iOS
if(IOS)
function(create_firebase_target target_name)
# Get the arguments passed to the function
set(dependencies ${ARGN})

# Define the target as an imported library
add_library(${target_name} STATIC IMPORTED)

# Set properties for the imported target
set_target_properties(${target_name} PROPERTIES
IMPORTED_LOCATION "${FIREBASE_IOS_SDK_DIR}/${target_name}/${target_name}.xcframework"
)

# Loop over the dependencies and create imported targets for them
foreach(dependency ${dependencies})
# Define each dependency as an imported library
add_library(${dependency} STATIC IMPORTED)

# Set properties for the imported dependency
set_target_properties(${dependency} PROPERTIES
IMPORTED_LOCATION "${FIREBASE_IOS_SDK_DIR}/${target_name}/${dependency}.xcframework"
)

# Link each dependency to the main target
target_link_libraries(${target_name} INTERFACE ${dependency})
endforeach()
endfunction()

create_firebase_target(FirebaseAnalytics
FBLPromises
FirebaseCoreInternal
GoogleAppMeasurementIdentitySupport
FirebaseInstallations
GoogleUtilities
FirebaseCore
GoogleAppMeasurement
nanopb
)

create_firebase_target(FirebaseMessaging GoogleDataTransport)
target_link_libraries(FirebaseMessaging INTERFACE FirebaseAnalytics)

target_link_libraries(${target_messaging}
PRIVATE
FirebaseMessaging
)

endif()
17 changes: 5 additions & 12 deletions src/firebaseqtapp.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
#include "firebaseqtapp.h"
#include "firebaseqtapp_p.h"

#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <QtAndroid>
#include <QAndroidJniEnvironment>
#elif defined(Q_OS_ANDROID)
#ifdef Q_OS_ANDROID
#include <QtCore/private/qandroidextras_p.h>
#include <QJniEnvironment>
#endif
Expand Down Expand Up @@ -88,17 +85,13 @@ void FirebaseQtApp::initialize()
options.set_project_id(d->projectId.toLatin1().constData());
}

#if defined(Q_OS_ANDROID) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QAndroidJniEnvironment env;
d->app = ::firebase::App::Create(options, &*env, QtAndroid::androidActivity().object());
#elif defined(Q_OS_ANDROID)
QJniEnvironment env;
d->app = ::firebase::App::Create(options, &*env, QNativeInterface::QAndroidApplication::context());
#ifdef Q_OS_ANDROID
d->app = ::firebase::App::Create(options, QJniEnvironment::getJniEnv(), QNativeInterface::QAndroidApplication::context().object());
Q_ASSERT_X(d->app, "FirebaseQtApp", "App::Create");
#else
d->app = ::firebase::App::Create();
#endif

Q_ASSERT_X(d->app, "FirebaseQtApp", "App::Create");
#endif

QTimer::singleShot(0, this, [=] {
for (FirebaseQtAbstractModule *module : d->modules) {
Expand Down
Loading