Skip to content

Commit

Permalink
Add crash reporter to secant (Electric-Coin-Company#531)
Browse files Browse the repository at this point in the history
* [Electric-Coin-Company#525] Adds functions to configure, testCrash and check if it can start.

This adds a build phase where a dummy file is added to the project to
make the build and Plist copy happy. When building in the CI there
will be a script to replace this Plist file with the real one that
then will be copied to the bundle

Crashlytics will be "off" by default and then be turned on when
starting up to be an Opt-Out thing.

This is the only way it can be turned off later.
reference: https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=ios#enable_opt-in_reporting

The app will start with crash reporting turned off and will set it up
on by default on the application's code. Then if the user wants to
opt-out of crash reporting, it can. Otherwise, it won't be possible.

Adds opting out of crash reporting as a stored user preference.
This adds a value inside UserPreferencesStorage and its live and
mock counterparts.

also creates a builer for `CrashReporterClient` that has a Dependency
to `@Dependency(\.userStoredPreferences)` and sets the references
for the client to set the appropriate values into the user storage

`UserPreferencesStorage` as been adapted to be a TCA Dependency.

`SettingsStore` now as a `Toogle()` to turn off and on crash
reporting. But it doesn't work yet because I haven't found out
how to make a TCA Binding that can rely on an initial value that
is not hardcoded but injected from somewhere else.

See https://www.pointfree.co/episodes/ep158-safer-conciser-forms-part-1
https://www.pointfree.co/episodes/ep158-safer-conciser-forms-part-2

Adds Test Crash button and enable crash reporting

Adds upload-symbols run script phase

Closes Electric-Coin-Company#525

Add a custom build environment variable "UPLOAD_CRASHLYTICS_SYMBOLS"
that will let Xcode skip the upload_symbols script for debug builds

Fix Initialization tests

* bump build
  • Loading branch information
Francisco Gindre authored Feb 15, 2023
1 parent f946de1 commit 26dd0ea
Show file tree
Hide file tree
Showing 18 changed files with 487 additions and 38 deletions.
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ iOSInjectionProject/
#ignore Pods directory for example project
Pods

# do not commit Google Firebase config PLIST file
GoogleService-info.plist

# do not commit generated libraries to this repo
lib
*.a
*.generated.swift
env-vars.sh
wallet/wallet/Generated/Constants.generated.swift
91 changes: 89 additions & 2 deletions secant.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
{
"pins" : [
{
"identity" : "abseil-cpp-swiftpm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git",
"state" : {
"revision" : "583de9bd60f66b40e78d08599cc92036c2e7e4e1",
"version" : "0.20220203.2"
}
},
{
"identity" : "boringssl-swiftpm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/boringssl-SwiftPM.git",
"state" : {
"revision" : "dd3eda2b05a3f459fc3073695ad1b28659066eab",
"version" : "0.9.1"
}
},
{
"identity" : "combine-schedulers",
"kind" : "remoteSourceControl",
Expand All @@ -9,6 +27,51 @@
"version" : "0.9.1"
}
},
{
"identity" : "firebase-ios-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/firebase-ios-sdk.git",
"state" : {
"revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050",
"version" : "9.6.0"
}
},
{
"identity" : "googleappmeasurement",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleAppMeasurement.git",
"state" : {
"revision" : "c1cfde8067668027b23a42c29d11c246152fe046",
"version" : "9.6.0"
}
},
{
"identity" : "googledatatransport",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleDataTransport.git",
"state" : {
"revision" : "f6b558e3f801f2cac336b04f615ce111fa9ddaa0",
"version" : "9.2.1"
}
},
{
"identity" : "googleutilities",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
"revision" : "0543562f85620b5b7c510c6bcbef75b562a5127b",
"version" : "7.11.0"
}
},
{
"identity" : "grpc-ios",
"kind" : "remoteSourceControl",
"location" : "https://github.com/grpc/grpc-ios.git",
"state" : {
"revision" : "8440b914756e0d26d4f4d054a1c1581daedfc5b6",
"version" : "1.44.3-grpc"
}
},
{
"identity" : "grpc-swift",
"kind" : "remoteSourceControl",
Expand All @@ -18,6 +81,24 @@
"version" : "1.14.0"
}
},
{
"identity" : "gtm-session-fetcher",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/gtm-session-fetcher.git",
"state" : {
"revision" : "5ccda3981422a84186387dbb763ba739178b529c",
"version" : "2.3.0"
}
},
{
"identity" : "leveldb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
"revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
"version" : "1.22.2"
}
},
{
"identity" : "lottie-ios",
"kind" : "remoteSourceControl",
Expand All @@ -36,6 +117,24 @@
"version" : "2.2.4"
}
},
{
"identity" : "nanopb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/nanopb.git",
"state" : {
"revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
"version" : "2.30909.0"
}
},
{
"identity" : "promises",
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/promises.git",
"state" : {
"revision" : "3e4e743631e86c8c70dbc6efdc7beaa6e90fd3bb",
"version" : "2.1.1"
}
},
{
"identity" : "sqlite.swift",
"kind" : "remoteSourceControl",
Expand Down
41 changes: 41 additions & 0 deletions secant/Dependencies/CrashReporter/CrashReporterLiveKey.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// CrashReporterLiveKey.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/2/23.
//
import ComposableArchitecture
import FirebaseCore
import FirebaseCrashlytics
extension CrashReporterClient: DependencyKey {
static let liveValue: CrashReporterClient = CrashReporterClient(
configure: { canConfigure in
let fileName = "GoogleService-Info.plist"

// checks whether the crash reporter's config file is a dummy_file purposedly placed by the build job or the real one.
// this does not check the integrity of the Plist file for Firebase.
// that's a problem for the library itself.
guard
let configFile = Bundle.main.url(forResource: fileName, withExtension: nil),
let properties = NSDictionary(contentsOf: configFile),
properties["IS_DUMMY_FILE"] == nil,
canConfigure
else {
return
}

FirebaseApp.configure()
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)
},
testCrash: {
fatalError("Crash was triggered to test the crash reporter")
},
optIn: {
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(true)
},
optOut: {
Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false)

}
)
}
16 changes: 16 additions & 0 deletions secant/Dependencies/CrashReporter/CrashReporterTestKey.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// CrashReporterTestKey.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/2/23.
//

import ComposableArchitecture
extension CrashReporterClient: TestDependencyKey {
static let testValue: CrashReporterClient = CrashReporterClient(
configure: { _ in },
testCrash: {},
optIn: {},
optOut: {}
)
}
31 changes: 31 additions & 0 deletions secant/Dependencies/CrashReporter/CrashReportingInterface.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// CrashReportingInterface.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/2/23.
//
import ComposableArchitecture
import Foundation

extension DependencyValues {
var crashReporter: CrashReporterClient {
get { self[CrashReporterClient.self] }
set { self[CrashReporterClient.self] = newValue }
}
}

struct CrashReporterClient {
/// Configures the crash reporter if possible.
/// if it can't be configured this will fail silently
var configure: (Bool) -> Void

/// this will test the crash reporter
/// - Note: depending of the crash reporter this may or may not crash your app.
var testCrash: () -> Void

/// this will tell the crash reporter that the user a has decided to opt-in crash reporting
var optIn: () -> Void

/// this will tell the crash reporter that the user has decided to opt-out of crash reporting
var optOut: () -> Void
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ struct UserPreferencesStorage {
case zcashFiatConverted
case zcashRecoveryPhraseTestCompleted
case zcashSessionAutoshielded
case zcashUserOptedOutOfCrashReporting
}

/// Default values for all preferences in case there is no value stored (counterparts to `Constants`)
Expand All @@ -25,6 +26,7 @@ struct UserPreferencesStorage {
private let fiatConvertion: Bool
private let recoveryPhraseTestCompleted: Bool
private let sessionAutoshielded: Bool
private let userOptedOutOfCrashReporting: Bool

private let userDefaults: UserDefaultsClient

Expand All @@ -34,13 +36,15 @@ struct UserPreferencesStorage {
fiatConvertion: Bool,
recoveryPhraseTestCompleted: Bool,
sessionAutoshielded: Bool,
userOptedOutOfCrashReporting: Bool,
userDefaults: UserDefaultsClient
) {
self.appSessionFrom = appSessionFrom
self.convertedCurrency = convertedCurrency
self.fiatConvertion = fiatConvertion
self.recoveryPhraseTestCompleted = recoveryPhraseTestCompleted
self.sessionAutoshielded = sessionAutoshielded
self.userOptedOutOfCrashReporting = userOptedOutOfCrashReporting
self.userDefaults = userDefaults
}

Expand Down Expand Up @@ -89,6 +93,15 @@ struct UserPreferencesStorage {
await setValue(bool, forKey: Constants.zcashSessionAutoshielded.rawValue)
}

/// Whether the user has opted out of crash reporting
var isUserOptedOutOfCrashReporting: Bool {
getValue(forKey: Constants.zcashUserOptedOutOfCrashReporting.rawValue, default: false)
}

func setIsUserOptedOutOfCrashReporting(_ bool: Bool) async {
await setValue(bool, forKey: Constants.zcashUserOptedOutOfCrashReporting.rawValue)
}

/// Use carefully: Deletes all user preferences from the User Defaults
func removeAll() async {
for key in Constants.allCases {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// UserPreferencesStorageInterface.swift
// secant-testnet
//
// Created by Francisco Gindre on 2/6/23.
//

import Foundation
import ComposableArchitecture

extension DependencyValues {
var userStoredPreferences: UserPreferencesStorageClient {
get { self [UserPreferencesStorageClient.self] }
set { self[UserPreferencesStorageClient.self] = newValue }
}
}

struct UserPreferencesStorageClient {
var activeAppSessionFrom: () -> TimeInterval
var setActiveAppSessionFrom: (TimeInterval) async -> Void

var currency: () -> String
var setCurrenty: (String) async -> Void

var isFiatConverted: () -> Bool
var setIsFiatConverted: (Bool) async -> Void

var isRecoveryPhraseTestCompleted: () -> Bool
var setIsRecoveryPhraseTestCompleted: (Bool) async -> Void

var isSessionAutoshielded: () -> Bool
var setIsSessionAutoshielded: (Bool) async -> Void

var isUserOptedOutOfCrashReporting: () -> Bool
var setIsUserOptedOutOfCrashReporting: (Bool) async -> Void

var removeAll: () async -> Void
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@
//

import Foundation
import ComposableArchitecture

extension UserPreferencesStorageClient: DependencyKey {
static var liveValue: UserPreferencesStorageClient = {
let live = UserPreferencesStorage.live

return UserPreferencesStorageClient(
activeAppSessionFrom: { live.activeAppSessionFrom },
setActiveAppSessionFrom: live.setActiveAppSessionFrom(_:),
currency: { live.currency },
setCurrenty: live.setCurrency(_:),
isFiatConverted: { live.isFiatConverted },
setIsFiatConverted: live.setIsFiatConverted(_:),
isRecoveryPhraseTestCompleted: {
live.isRecoveryPhraseTestCompleted
},
setIsRecoveryPhraseTestCompleted: live.setIsRecoveryPhraseTestCompleted(_:),
isSessionAutoshielded: { live.isSessionAutoshielded },
setIsSessionAutoshielded: live.setIsSessionAutoshielded(_:),
isUserOptedOutOfCrashReporting: {
live.isUserOptedOutOfCrashReporting
},
setIsUserOptedOutOfCrashReporting: live.setIsUserOptedOutOfCrashReporting(_:),
removeAll: live.removeAll
)
}()
}


extension UserPreferencesStorage {
static let live = UserPreferencesStorage(
Expand All @@ -14,6 +42,7 @@ extension UserPreferencesStorage {
fiatConvertion: true,
recoveryPhraseTestCompleted: false,
sessionAutoshielded: true,
userOptedOutOfCrashReporting: false,
userDefaults: .live()
)
}
Loading

0 comments on commit 26dd0ea

Please sign in to comment.