Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
698c632
Add delete-data, create-report, and breadcrumb, and buffer iOS networ…
krassx Oct 9, 2026
a74b63f
Match beta5 report and breadcrumb selectors and release a cancelled r…
krassx Oct 9, 2026
0dc19cf
Match beta5 report and breadcrumb selectors and release a cancelled r…
krassx Oct 9, 2026
b1a6d56
Use Boolean consumer for Android delete and apply attachment fileName.
krassx Oct 9, 2026
094a11d
Fence iOS upload after Stop and chain delete after stop when needed.
krassx Oct 9, 2026
39f218a
Cancel only the upload that was discarded, and keep attachment name a…
krassx Oct 9, 2026
e49f387
Let overlapping iOS managed report uploads stay independent.
krassx Oct 9, 2026
2506581
Call the Android 7.3 report, delete, and breadcrumb methods.
krassx Oct 9, 2026
586646b
Keep session attributes on an Android report and cancel an in-flight …
krassx Oct 9, 2026
3540dc6
Escape iOS report JSON and skip upload when that JSON does not parse.
krassx Oct 9, 2026
7e1e807
Write null for non-finite iOS report attribute values so the report J…
krassx Oct 9, 2026
37399a8
Release an Android report on stop and omit unset iOS severity.
krassx Oct 9, 2026
951262c
Clear native Android attachments when the managed report snapshot is …
krassx Oct 9, 2026
ffb17b9
Overlay Android report attributes per key unless the caller cleared all.
krassx Oct 9, 2026
1ae751d
Overlay iOS CreateReport attributes per key unless the caller cleared…
krassx Oct 9, 2026
a712540
Apply managed iOS uploads with BugseeExtendedReport APIs.
krassx Oct 9, 2026
3d9fb38
Warn on unsupported iOS CreateReport email and clear handler attachme…
krassx Oct 9, 2026
277c84b
Clear the iOS CreateReport slot on stopped-path delete.
krassx Oct 9, 2026
b3d965b
Append handler attachments unless the caller cleared them.
krassx Oct 9, 2026
e461acd
Keep a delete from wiping the next launch, and buffer iOS breadcrumbs…
krassx Oct 9, 2026
1804e3d
Ignore stale lifecycle Stopped after a new iOS launch begins.
krassx Oct 9, 2026
e885a71
Stamp buffered iOS breadcrumbs at enqueue and apply CreateReport mime…
krassx Oct 9, 2026
b2a1aa5
Ignore late lifecycle Stopped until managed Stop clears launch cycle.
krassx Oct 9, 2026
b5641b2
Clear iOS launch buffers on delete when not launched.
krassx Oct 9, 2026
448da2d
Merge the report attachment API into the public API branch.
krassx Oct 9, 2026
802063a
Load CreateReport file attachments from path and release snapshot fil…
krassx Oct 9, 2026
17aae36
Release abandoned report snapshots on Stop/Delete and snapshot Androi…
krassx Oct 9, 2026
b8b2fd3
Release Android CreateReport file snapshots after native apply in onC…
krassx Oct 9, 2026
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
258 changes: 258 additions & 0 deletions Plugins/iOS/BugseeUnityBridge.mm
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@

#if __has_include(<Bugsee/Bugsee.h>)
#import <Bugsee/Bugsee.h>
#import <Bugsee/BGSContracts.h>
#define BUGSEE_IOS_SDK 1
#elif __has_include("Bugsee/Bugsee.h")
#import "Bugsee/Bugsee.h"
#import "Bugsee/BGSContracts.h"
#define BUGSEE_IOS_SDK 1
#else
#define BUGSEE_IOS_SDK 0
Expand Down Expand Up @@ -42,6 +44,157 @@ static void BugseeRunOnMain(dispatch_block_t block)
}
}

typedef void (*BugseeManagedReportCreateCallback)(int succeeded, uint64_t uploadToken);

static uint64_t gManagedReportUploadFence;
static NSMutableSet<NSNumber *> *gCancelledManagedReportUploadIds;

static BOOL BugseeManagedReportUploadStillActive(uint64_t uploadId, uint64_t uploadFence)
{
if (uploadId == 0 || uploadFence != gManagedReportUploadFence) {
return NO;
}
if (!gCancelledManagedReportUploadIds) {
return YES;
}
return ![gCancelledManagedReportUploadIds containsObject:@(uploadId)];
}

static void BugseeBridgeSetAttachmentFileNameIfNeeded(id attachment, NSString *displayName, NSString *fileName)
{
if (!attachment || fileName.length == 0 || [fileName isEqualToString:displayName]) {
return;
}
if ([attachment conformsToProtocol:@protocol(BGSAttachmentContract)]) {
((id<BGSAttachmentContract>)attachment).fileName = fileName;
}
}

static void BugseeBridgeSetAttachmentMimeTypeIfNeeded(id attachment, NSString *mimeType)
{
if (!attachment || mimeType.length == 0) {
return;
}
if ([attachment conformsToProtocol:@protocol(BGSAttachmentContract)]) {
((id<BGSAttachmentContract>)attachment).mimeType = mimeType;
return;
}
if ([attachment respondsToSelector:@selector(setMimeType:)]) {
[attachment performSelector:@selector(setMimeType:) withObject:mimeType];
}
}

static void BugseeBridgeApplyExtendedReportDict(BugseeExtendedReport *report, NSDictionary *d)
{
if (![d isKindOfClass:[NSDictionary class]] || !report) {
return;
}

id summary = d[@"summary"];
if ([summary isKindOfClass:[NSString class]]) {
[report setSummary:summary];
}
id desc = d[@"description"];
if ([desc isKindOfClass:[NSString class]]) {
[report setDescription:desc];
Comment thread
cursor[bot] marked this conversation as resolved.
}
id sev = d[@"severity"];
if ([sev respondsToSelector:@selector(integerValue)]) {
NSInteger sevVal = [sev integerValue];
if (sevVal != 0) {
[report setSeverity:(BugseeSeverityLevel)sevVal];
}
}

id labels = d[@"labels"];
if ([labels isKindOfClass:[NSArray class]]) {
NSMutableArray<NSString *> *clean = [NSMutableArray array];
for (id label in (NSArray *)labels) {
if ([label isKindOfClass:[NSString class]]) {
[clean addObject:label];
}
}
report.labels = clean;
}

id removals = d[@"attributeRemovals"];
if ([removals isKindOfClass:[NSArray class]]) {
for (id name in (NSArray *)removals) {
if ([name isKindOfClass:[NSString class]]) {
[report clearAttribute:(NSString *)name];
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
}
}
}

id replaceAll = d[@"attributesReplaceAll"];
BOOL shouldReplaceAll = [replaceAll respondsToSelector:@selector(boolValue)] && [replaceAll boolValue];

id attrs = d[@"attributes"];
if ([attrs isKindOfClass:[NSDictionary class]]) {
if (shouldReplaceAll) {
[report clearAllAttributes];
}
[(NSDictionary *)attrs enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([key isKindOfClass:[NSString class]]) {
[report setAttribute:(NSString *)key withValue:obj];
}
}];
} else if (shouldReplaceAll) {
Comment thread
cursor[bot] marked this conversation as resolved.
[report clearAllAttributes];
}
Comment thread
cursor[bot] marked this conversation as resolved.

id attachments = d[@"attachments"];
if ([attachments isKindOfClass:[NSArray class]]) {
[report clearAllAttachments];
for (id item in (NSArray *)attachments) {
if (![item isKindOfClass:[NSDictionary class]]) {
continue;
}
NSDictionary *att = (NSDictionary *)item;
NSString *name = [att[@"name"] isKindOfClass:[NSString class]] ? att[@"name"] : @"attachment";
id fileNameVal = att[@"fileName"];
NSString *fileName = [fileNameVal isKindOfClass:[NSString class]] ? fileNameVal : nil;
if (fileName.length == 0) {
fileName = name;
}
id mime = att[@"mimeType"];
NSString *mimeType = [mime isKindOfClass:[NSString class]] ? (NSString *)mime : nil;

id pathVal = att[@"path"];
if ([pathVal isKindOfClass:[NSString class]] && [(NSString *)pathVal length] > 0) {
NSData *fileData = [NSData dataWithContentsOfFile:(NSString *)pathVal];
if (!fileData) {
continue;
}
BugseeAttachment *attachment = [BugseeAttachment attachmentWithName:name filename:fileName data:fileData];
if (attachment) {
BugseeBridgeSetAttachmentMimeTypeIfNeeded(attachment, mimeType);
[report setAttachment:attachment];
}
continue;
}

NSData *data = nil;
id b64 = att[@"dataBase64"];
if ([b64 isKindOfClass:[NSString class]] && [(NSString *)b64 length] > 0) {
data = [[NSData alloc] initWithBase64EncodedString:(NSString *)b64 options:0];
}
id text = att[@"text"];
if (!data && [text isKindOfClass:[NSString class]]) {
data = [(NSString *)text dataUsingEncoding:NSUTF8StringEncoding];
}
if (!data) {
Comment thread
cursor[bot] marked this conversation as resolved.
continue;
}
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
BugseeAttachment *attachment = [BugseeAttachment attachmentWithName:name filename:fileName data:data];
if (attachment) {
BugseeBridgeSetAttachmentMimeTypeIfNeeded(attachment, mimeType);
[report setAttachment:attachment];
Comment thread
cursor[bot] marked this conversation as resolved.
}
}
}
}

extern "C" {

void _bugsee_launch(const char *appToken, const char *optionsJson)
Expand Down Expand Up @@ -355,10 +508,96 @@ void _bugsee_free(char *ptr)
}
}

typedef int (*BugseeDeleteCollectedDataShouldRunFn)(int capturedGeneration);

void _bugsee_delete_collected_data(int capturedGeneration, BugseeDeleteCollectedDataShouldRunFn shouldRun)
{
BugseeRunOnMain(^{
void (^runDeleteIfAllowed)(void) = ^{
if (shouldRun && !shouldRun(capturedGeneration)) {
return;
}
[Bugsee deleteCollectedDataOnDevice:YES completion:nil];
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
};
if ([Bugsee sharedInstance] != nil) {
[Bugsee stop:^{
runDeleteIfAllowed();
}];
} else {
runDeleteIfAllowed();
}
});
}

void _bugsee_cancel_managed_report_upload(uint64_t uploadId)
{
if (!gCancelledManagedReportUploadIds) {
gCancelledManagedReportUploadIds = [NSMutableSet set];
}
[gCancelledManagedReportUploadIds addObject:@(uploadId)];
}

void _bugsee_invalidate_managed_report_uploads(void)
{
gManagedReportUploadFence++;
[gCancelledManagedReportUploadIds removeAllObjects];
}

void _bugsee_upload_managed_report(const char *reportJson,
uint64_t uploadId,
uint64_t uploadFence,
BugseeManagedReportCreateCallback callback)
{
if (!reportJson) {
if (callback) {
callback(0, uploadId);
}
return;
}
NSString *jsonCopy = [NSString stringWithUTF8String:reportJson];
[Bugsee createReportWithCompletion:^(BugseeExtendedReport *_Nullable report) {
if (!BugseeManagedReportUploadStillActive(uploadId, uploadFence)) {
if (callback) {
callback(0, uploadId);
}
return;
}
if (!report) {
if (callback) {
callback(0, uploadId);
}
return;
}
NSDictionary *dict = BugseeDeserializeJson(jsonCopy.UTF8String);
if (![dict isKindOfClass:[NSDictionary class]]) {
if (callback) {
callback(0, uploadId);
}
return;
}
BugseeBridgeApplyExtendedReportDict(report, dict);
if (!BugseeManagedReportUploadStillActive(uploadId, uploadFence)) {
if (callback) {
callback(0, uploadId);
}
return;
}
if (callback) {
callback(1, uploadId);
Comment thread
cursor[bot] marked this conversation as resolved.
}
if (BugseeManagedReportUploadStillActive(uploadId, uploadFence)) {
[Bugsee uploadReport:report completion:nil];
Comment thread
cursor[bot] marked this conversation as resolved.
Comment thread
cursor[bot] marked this conversation as resolved.
}
}];
}

} // extern "C"

#else // !BUGSEE_IOS_SDK

typedef void (*BugseeManagedReportCreateCallback)(int succeeded);
typedef int (*BugseeDeleteCollectedDataShouldRunFn)(int capturedGeneration);

extern "C" {

void _bugsee_launch(const char *appToken, const char *optionsJson) { (void)appToken; (void)optionsJson; }
Expand Down Expand Up @@ -393,6 +632,25 @@ void _bugsee_feedback_show(void) {}
void _bugsee_appearance_set_string(const char *propertyName, const char *propertyValue) { (void)propertyName; (void)propertyValue; }
char *_bugsee_appearance_get_string(const char *propertyName) { (void)propertyName; return NULL; }
void _bugsee_free(char *ptr) { (void)ptr; }
void _bugsee_delete_collected_data(int capturedGeneration, BugseeDeleteCollectedDataShouldRunFn shouldRun)
{
(void)capturedGeneration;
(void)shouldRun;
}
void _bugsee_cancel_managed_report_upload(uint64_t uploadId) { (void)uploadId; }
void _bugsee_invalidate_managed_report_uploads(void) {}
void _bugsee_upload_managed_report(const char *reportJson,
uint64_t uploadId,
uint64_t uploadFence,
BugseeManagedReportCreateCallback callback)
{
(void)reportJson;
(void)uploadId;
(void)uploadFence;
if (callback) {
callback(0, uploadId);
}
}

}

Expand Down
Loading
Loading