Skip to content

Commit

Permalink
Merge pull request #45 from plaidev/flutter
Browse files Browse the repository at this point in the history
[ios] impl crash report feature
  • Loading branch information
RyosukeCla authored Jun 18, 2024
2 parents a19594b + 9b5cf84 commit eea2815
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 14 deletions.
2 changes: 1 addition & 1 deletion Nativebrik.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Nativebrik'
s.version = '0.5.7'
s.version = '0.5.8'
s.summary = 'Nativebrik SDK'
s.description = <<-DESC
Nativebrik SDK for iOS.
Expand Down
7 changes: 5 additions & 2 deletions ios/Example-UIKit/Example-UIKit/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.

NSSetUncaughtExceptionHandler { exception in
nativebrik.experiment.record(exception: exception)
}

return true
}

Expand Down
16 changes: 15 additions & 1 deletion ios/Example/Example/ExampleApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,27 @@

import SwiftUI
import Nativebrik
import UIKit

let nativebrik = NativebrikClient(projectId: "cgv3p3223akg00fod19g")

class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NSSetUncaughtExceptionHandler { exception in
nativebrik.experiment.record(exception: exception)
}
return true
}
}

@main
struct ExampleApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

var body: some Scene {
WindowGroup {
NativebrikProvider(
client: NativebrikClient(projectId: "cgv3p3223akg00fod19g")
client: nativebrik
) {
ContentView()
}
Expand Down
8 changes: 8 additions & 0 deletions ios/Nativebrik/Nativebrik/data/cotnainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ protocol Container {
func fetchEmbedding(experimentId: String, componentId: String?) async -> Result<UIBlock, NativebrikError>
func fetchInAppMessage(trigger: String) async -> Result<UIBlock, NativebrikError>
func fetchRemoteConfig(experimentId: String) async -> Result<(String, ExperimentVariant), NativebrikError>

func record(_ exception: NSException)
}

class ContainerEmptyImpl: Container {
Expand All @@ -64,6 +66,8 @@ class ContainerEmptyImpl: Container {
func fetchRemoteConfig(experimentId: String) async -> Result<(String, ExperimentVariant), NativebrikError> {
return Result.failure(NativebrikError.notFound)
}
func record(_ exception: NSException) {
}
}

class ContainerImpl: Container {
Expand Down Expand Up @@ -281,4 +285,8 @@ class ContainerImpl: Container {
}
return Result.success((experimentId, variant))
}

func record(_ exception: NSException) {
self.trackRepository.record(exception)
}
}
69 changes: 61 additions & 8 deletions ios/Nativebrik/Nativebrik/data/track.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,18 @@

import Foundation

private let CRASH_RECORD_KEY: String = "NATIVEBRIK_CRASH_RECORD"

struct CrashRecord: Codable {
var reason: String?
var callStacks: [String]?
}

protocol TrackRepository2 {
func trackExperimentEvent(_ event: TrackExperimentEvent)
func trackEvent(_ event: TrackUserEvent)

func record(_ exception: NSException)
}

struct TrackRequest: Encodable {
Expand Down Expand Up @@ -57,12 +66,14 @@ class TrackRespositoryImpl: TrackRepository2 {
self.queueLock = NSLock()
self.buffer = []
self.timer = nil

self.report()
}

deinit {
self.timer?.invalidate()
}

func trackExperimentEvent(_ event: TrackExperimentEvent) {
self.pushToQueue(TrackEvent(
typename: .Experiment,
Expand All @@ -71,15 +82,15 @@ class TrackRespositoryImpl: TrackRepository2 {
timestamp: formatToISO8601(getCurrentDate())
))
}

func trackEvent(_ event: TrackUserEvent) {
self.pushToQueue(TrackEvent(
typename: .Event,
name: event.name,
timestamp: formatToISO8601(getCurrentDate())
))
}

private func pushToQueue(_ event: TrackEvent) {
self.queueLock.lock()
if self.timer == nil {
Expand All @@ -103,10 +114,10 @@ class TrackRespositoryImpl: TrackRepository2 {
if self.buffer.count >= self.maxQueueSize {
self.buffer.removeFirst(self.maxQueueSize - self.buffer.count)
}

self.queueLock.unlock()
}

private func sendAndFlush() async throws {
if self.buffer.count == 0 {
return
Expand All @@ -119,7 +130,7 @@ class TrackRespositoryImpl: TrackRepository2 {
timestamp: formatToISO8601(getCurrentDate()),
events: events
)

do {
let url = URL(string: config.trackUrl)!
let jsonData = try JSONEncoder().encode(trackRequest)
Expand All @@ -128,11 +139,53 @@ class TrackRespositoryImpl: TrackRepository2 {
request.httpBody = jsonData
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let _ = try await nativebrikSession.data(for: request)

self.timer?.invalidate()
self.timer = nil
} catch {
self.buffer.append(contentsOf: events)
}
}

private func report() {
guard let data = self.user.userDB.data(forKey: CRASH_RECORD_KEY) else {
return
}
do {
self.user.userDB.removeObject(forKey: CRASH_RECORD_KEY)
let crashRecord = try JSONDecoder().decode(CrashRecord.self, from: data)
let causedByNativebrik = (
crashRecord.callStacks?.contains(where: { callStack in
return callStack.contains("Nativebrik")
}) ?? false) || (crashRecord.reason?.contains("Nativebrik") ?? false)
self.buffer.append(TrackEvent(
typename: .Event,
name: "N_CRASH_RECORD",
timestamp: formatToISO8601(getCurrentDate())
))
if causedByNativebrik {
self.buffer.append(TrackEvent(
typename: .Event,
name: "N_CRASH_IN_SDK_RECORD",
timestamp: formatToISO8601(getCurrentDate())
))
}

Task(priority: .low) {
try await self.sendAndFlush()
}

} catch {}
}

func record(_ exception: NSException) {
let record = CrashRecord(
reason: exception.reason,
callStacks: exception.callStackSymbols
)
do {
let json = try JSONEncoder().encode(record)
self.user.userDB.set(json, forKey: CRASH_RECORD_KEY)
} catch {}
}
}
2 changes: 1 addition & 1 deletion ios/Nativebrik/Nativebrik/data/user.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private let USER_SEED_MAX: Int = 100000000
public class NativebrikUser {
private var properties: [String: String]
private var lastBootTime: Double = getCurrentDate().timeIntervalSince1970
private var userDB: UserDefaults
internal var userDB: UserDefaults

init() {
if !isNativebrikAvailable {
Expand Down
6 changes: 5 additions & 1 deletion ios/Nativebrik/Nativebrik/sdk.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Foundation
import SwiftUI
import Combine

public let nativebrikSdkVersion = "0.5.7"
public let nativebrikSdkVersion = "0.5.8"
public let isNativebrikAvailable: Bool = {
if #available(iOS 15.0, *) {
return true
Expand Down Expand Up @@ -154,6 +154,10 @@ public class NativebrikExperiment {
public func dispatch(_ event: NativebrikEvent) {
self.overlayVC.triggerViewController.dispatch(event: event)
}

public func record(exception: NSException) {
self.container.record(exception)
}

public func overlayViewController() -> UIViewController {
if !isNativebrikAvailable {
Expand Down

0 comments on commit eea2815

Please sign in to comment.