Flexible bug report framework for iOS

Overview

Clue

Travis status CocoaPods platforms CocoaPods version Twitter: @ahmed__sulajman

Clue is a simple smart-bug report framework for iOS, which allows your users to record full bug/crash report and send it to you as a single .clue file via email.

(which includes full video of the screen, views structure, all network operations and user interactions during recording)

🕵️ Why

When you get a new bug report from your users - you're not a software engineer anymore. You become a true detective, trying to get a clue why something went wrong in your app. Especially it’s true if you’re working in the company, where you need to talk to users directly.

I believe it’s a problem. What if you can fix bug/crash without trying to reproduce it? What if you can see all required information and exact cause of the problem right away, without wasting your time figuring it out?

ℹ️ Description

Clue demo

Clue.framework records all required information so you’ll be able to fix bug or crash really fast. Just import and setup Clue.framework in your iOS application (Xcode project) and you'd be able to shake the device (or simulator) to start bug report recording. During that recording you can do whatever you want to reproduce the bug.

After you’re done you need to shake the device (or simulator) once again (or tap on recording indicator view) and Clue will save the .clue file with the following information:

  • Device Information
  • All network operations during recording
  • All views structure changes (including view’s properties and subviews)
  • All user touches and interactions
  • Screen record video

Next Clue will open system mail window with your email (unfortunately on device only, simulator doesn’t support system mail client) — so the user can send .clue report file right to your inbox.

📲 How to install

Manually

If you prefer not to use dependency managers, you can integrate Clue into your project manually.

  1. Clone the repo git clone [email protected]:Geek-1001/Clue.git
  2. Open Clue.xcodeproj with Xcode
  3. Choose Clue build schema and build it with Xcode
  4. Drag and Drop Clue.framework file from Product folder right into your Xcode project
  5. Make sure to select "Copy item if needed" in the dialog
  6. Go to Project settings > Build Phases
  7. Expand "Copy Files" section, choose "Frameworks" in the Destination dropdown
  8. Click on the plus icon and add Clue.framework

Using CocoaPods

  1. To integrate Clue into your Xcode project using CocoaPods, add following line to your Podfile : pod 'Clue', '~> 0.1.0'
  2. Then, run install command: $ pod install

💻 How to use

Basic usage

  • Import Clue framework wherever you need it

Objective-C :

@import Clue

Swift :

import Clue
  • To setup Clue you need to enable it with launch configuration in your AppDelegate.

Objective-C :

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [[ClueController sharedInstance] enableWithOptions:[CLUOptions optionsWithEmail:@"[email protected]"]];
    return YES;
}

Swift :

optional func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
    ClueController.sharedInstance().enable(with: CLUOptions(email: "[email protected]"))
    return true
}
  • Then you should handle shake gesture in AppDelegate (if you want to record bug reports from anywhere in the app on iOS 8, 9, 10) or in specific UIViewController (since motionBegan method doesn’t work in AppDelegate starting from iOS 10).

Objective-C :

- (void)motionBegan:(UIEventSubtype)motion withEvent:(UIEvent *)event {
    [[ClueController sharedInstance] handleShake:motion];
}

Swift :

func motionBegan(_ motion: UIEventSubtype, with event: UIEvent?) {
    ClueController.sharedInstance().handleShake(motion)
}

After that you can shake your device and start recording. Shake it again to stop it.

Additional abilities

  • If you want to use your own custom UI element to enable report recording you can call startRecording() and stopRecording() methods from ClueController directly like in the example below.

Objective-C :

[[ClueController sharedInstance] startRecording];
...
[[ClueController sharedInstance] stopRecording];

Swift :

ClueController.sharedInstance().startRecording()
...
ClueController.sharedInstance().stopRecording()
  • To disable ability to record bug reports just call disable() method from ClueController.

Objective-C :

[[ClueController sharedInstance] disable];

Swift :

ClueController.sharedInstance().disable()

Platforms Support

Clue supports iOS 9+ devices. The current version of Clue (and the master branch) is compatible with Xcode 8.

🛠️ Hackable

I love this part. Clue designed in a way, that you can tweak whatever you want and add more functionalities to report recording process (and I encourage you to do so). It means, that Clue will fit into your custom needs. For example, you want to track your custom logs during recording. Just create a separate module for that and plug it to the recording process (I’ll explain this in details below).

For now Clue is built with Objective-C (at least for the first iteration. I’m considering to rewrite some parts to Swift). So all internals were not designed to be Swift compatible. (If you can fix it — contributions are welcome!)

For more details please check out full documentation and code examples below.

Basic architecture and structures:

Clue structure

There are two main concepts in Clue which you need to understand in order to build it by yourself:

  • Modules
    • Recordable modules
    • Info modules
  • Writers

Modules are responsible for actual data handling. Those classes observe some specific data during recording or collect this data just once during record launch. There are two module types: CLURecordableModule and CLUInfoModule.

CLURecordableModule — is a protocol. It describes recordable module (like Video, View Structure, Network modules etc) which needs to track or inspect some specific information over time (like view structure for example) and record this information with specific timestamp using Writers

Note : Every recordable modules have to implement this protocol to be able to work normally inside the system

If your Recordable Module needs to observe new data instead of writing new data with every new frame you should use (subclass your new module from) CLUObserveModule which provide common interface to write new data as soon as data become available.


CLUInfoModule — is a protocol. It describes info modules (like Device Info module), static one-time modules which needs to write their data only once during whole recording also using Writers

Note : Every info modules have to implement this protocol to be able to work normally inside the system

Writers are responsible for writing some specific content (video, text etc) into the file.

CLUWritable — is a protocol. It describes writers (like CLUDataWriter or CLUVideoWriter) which needs to actually write new data to specific file (could be text file, video file etc.)

New Recordable Module Example

Let’s assume you want to add module which will intercept logs and write them into json file with specific timestamp inside .clue report file. First of you need to create new module class and subclass it from CLUObserveModule (we’re assuming here that we need to observe new logs and they are not available right away)

@interface CLULogsModule : CLUObserveModule
@end

Next we need to implement some methods from CLURecordableModule protocol:

@implementation CLULogsModule

- (instancetype)initWithWriter:(id <CLUWritable>)writer {
    // If you will write just text data into json file you can use CLUDataWriter as an argument for this method. CLUDataWriter implements CLUWritable protocol
}

- (void)startRecording {
    if (!self.isRecording) {
        [super startRecording];
         // Start logs interception    
    }
}

- (void)stopRecording {
    if (self.isRecording) {
        [super stopRecording];
        // Stop logs interception
    }
}

// This method will be called every time new frame with new timestamp is available.
// Since you subclass your module from CLUObserveModule you don't need to implement this method. CLUObserveModule takes care about it with data buffer.
// - (void)addNewFrameWithTimestamp:(CFTimeInterval)timestamp { }

@end

Next let’s assume we have some kind of log delegate method which will be called every time new log record is available. In this delegate method you should compose NSDictionary (if you want to write json) with all properties of this log data entity you want to record into final .clue report.

- (void)newLog:(NSString *)logText isAvailableWithDate:(NSDate *)date {
    // Let's build NSDictionary with data we need
    // You always should include timestamp property for every new data entry
    NSDictionary *logDictionary =
                    @{@"text" : logText,
                      @"date" : date,
                TIMESTAMP_KEY : self.currentTimeStamp}; // TIMESTAMP_KEY - is a #define with default name for timetamp declared in CLUObserveModule. self.currentTimeStamp – is a property declared in CLUObserveModule which is updates every [CLUObserveModule addNewFrameWithTimestamp:] method call. So you can use it to indicate current timestamp

    // Now we need to check validity of NSDictionary so we can convert it in to NSData as a json
    if ([NSJSONSerialization isValidJSONObject:touchDictionary]) {
        NSError *error;
        // Convert NSDictionary to NSData
        NSData *logData = [NSJSONSerialization dataWithJSONObject:logDictionary options:0 error:&error];

        // Add log data to data buffer from CLUObserveModule with [CLUObserveModule addData:]
        [self addData:logData];
    }  
}

To make it work you also need to plug your new module into the system of modules. The CLUReportComposer is exactly for this purposes.

CLUReportComposer is a class responsible for composing final Clue report from many modules. This class initialize all recordable and info modules and actually start recording. Also this class is calling addNewFrameWithTimestamp: method for every recordable module during recording and recordInfoData method from CLURecordableModule protocol for every info module only once at record launch.

So in ClueController you need to add your new module in this method

- (NSMutableArray *)configureRecordableModules {
    CLUVideoModule *videoModul = [self configureVideoModule];
    CLUViewStructureModule *viewStructureModule = [self configureViewStructureModule];
    CLUUserInteractionModule *userInteractionModule = [self configureUserInteractionModule];
    CLUNetworkModule *networkModule = [self configureNetworkModule];

    // Initialize your module just like other modules above
    CLULogsModule *logModule = [self configureLogsModule]

    NSMutableArray *modulesArray = [[NSMutableArray alloc] initWithObjects:videoModul, viewStructureModule, userInteractionModule, networkModule, /* HERE goes your module, */ nil];
    return modulesArray;
}

Here is configuration method example:

- (void)configureLogsModule {
    // Get directory URL for all recordable modules inside .clue file
    NSURL *recordableModulesDirectory = [[CLUReportFileManager sharedManager] recordableModulesDirectoryURL];

    // Specify URL for output json file
    NSURL *outputURL = [recordableModulesDirectory URLByAppendingPathComponent:@"module_logs.json"];

    // Initialize CLUDataWriter with specific output file URL
    CLUDataWriter *dataWriter = [[CLUDataWriter alloc] initWithOutputURL:outputURL];

    // Initialize CLULogsModule with specific Writer
    CLULogsModule *logsModule = [[CLULogsModule alloc] initWithWriter:dataWriter];

    return logsModule;
}

That’s it. Now you have logs inside your .clue report file as well as other useful data for fast bug fixing. You can literally build any module you want which can record any possible data inside your .clue bug report file.

New Info Module Example

Now let’s assume you want to add info module which will collect some initial data at the beginning of report recording for current users's location and write in into json file inside .clue report file. First of you need to create new info module class and declare CLUInfoModule protocol in the header file.

CLUInfoModule protocol describe info modules (like Device Info module or Exception module), static one-time modules which needs to write their data only once during recording. Every info modules have to implement this protocol to be able to work normally inside the system

@interface CLULocationInfoModule : NSObject <CLUInfoModule>
@end

Next we need to implement some methods from CLUInfoModule protocol:

@interface CLULocationInfoModule()
@property (nonatomic) CLUDataWriter *writer; // Just keep reference to Writer object from initialization
@end

@implementation CLULocationInfoModule

// If you will write just text data into json file you can use CLUDataWriter as an argument for this method. CLUDataWriter implements CLUWritable protocol so you can write any NSData object into output file
- (instancetype)initWithWriter:(id <CLUWritable>)writer {    
    // Basic initialization stuff
    _writer = writer;
    return self;
}

// This method will be called once at recording startup. Here you can add all your required data (in our case user's location) into report file
- (void)recordInfoData {
    NSData *locationData = [self retrieveLocationData];

    // Add location data to the output file via Writer object
    [_writer startWriting];
    [_writer addData:locationData];
    [_writer finishWriting];
}

@end

To make it work you also need to plug your new info module into the system of modules (just like recordable module) using CLUReportComposer.

CLUReportComposer is a class responsible for composing final Clue report from many modules. This class initialize all recordable and info modules and actually start recording. Also this class is calling addNewFrameWithTimestamp: method for every recordable module during recording and recordInfoData method from CLURecordableModule protocol for every info module only once at record launch.

So in ClueController you need to add your new info module in this method

- (NSMutableArray *)configureInfoModules {
    CLUDeviceInfoModule *deviceModule = [self configureDeviceInfoModule];

    // Initialize your info module just like other info modules above
    CLULocationInfoModule *locationModule = [self configureLocationModule];

    NSMutableArray *modulesArray = [[NSMutableArray alloc] initWithObjects:deviceModule, /* HERE goes your info module ,*/  nil];
    return modulesArray;
}

Here is configuration method example:

- (void)configureLocationModule {
    // Get directory URL for all info modules inside .clue file
    NSURL *infoModulesDirectory = [[CLUReportFileManager sharedManager] infoModulesDirectoryURL];

    // Specify URL for output json file
    NSURL *outputURL = [infoModulesDirectory URLByAppendingPathComponent:@"info_location.json"];

    // Initialize CLUDataWriter with specific output file URL
    CLUDataWriter *dataWriter = [[CLUDataWriter alloc] initWithOutputURL:outputURL];

    // Initialize CLULocationInfoModule with specific Writer
    CLULocationInfoModule *locationModule = [[CLULogsModule alloc] initWithWriter:dataWriter];

    return locationModule;
}

That’s it. Now you have location information inside your .clue report file.

How to add custom View object parsing

Clue records views' structure. This means that Clue needs to parse all properties and subviews for every visible View (and invisible as well) on the screen to be able to represent whole views' structure at the current time.

UIView categories are used for this purposes. There are following categories for UIView related classes:

  • UIView (CLUViewRecordableAdditions)
  • UILabel (CLUViewRecordableAdditions)
  • UIImageView (CLUViewRecordableAdditions)
  • UITextField (CLUViewRecordableAdditions)

UIView (CLUViewRecordableAdditions) category has methods to parse basic view properties (this is general for every view object) and recursively parse subviews.

Other categories made to parse View specific properties (like text property for UITextField, for example). If you want to parse some specific view properties and add them to final report you have to create separate category like this

@interface AHCustomView (CLUViewRecordableAdditions) <CLUViewRecordableProperties>
@end

Every new View category always have to implement method from CLUViewRecordableProperties protocol to be able to use root properties from base view class (UIView, for example) so all properties would be in place for your custom view object as well.

Now we need to implement actual property parsing

// Method from CLUViewRecordableProperties protocol
- (NSMutableDictionary *)clue_viewPropertiesDictionary {
    // First of you need to get property dictionary from view's superclass
    NSMutableDictionary *rootDictionary = [super clue_viewPropertiesDictionary];

    // Change class name, so it would be real instead of superclass' class name
    [rootDictionary setObject:NSStringFromClass([self class]) forKey:@"class"];

    // Next you want to add some view specific properties into root dictionary.  
    NSMutableDictionary *propertiesDictionary = [rootDictionary objectForKey:@"properties"];

    // Let's add text property just for example
    [propertiesDictionary setObject:self.text ?: @""
                             forKey:@"text"];
    …

    // Add new properties dictionary to root dictionary
    [rootDictionary setObject:propertiesDictionary
                       forKey:@"properties"];

    // Return root dictionary as a result
    return rootDictionary;
}

That’s it, now even your custom view classes will show specific properties in final .clue report.

What is .clue file

It’s basically just a package file with json data from network, view structure, user interactions modules and device info module and .mp4 video file from video module.

Here is tree representation of Report.clue file:

Report.clue
        ├── Info
        │     └── info_device.json
        └── Modules
            ├── module_interaction.json
            ├── module_network.json
            ├── module_video.mp4
            └── module_view.json

🖥️ macOS Companion Application

Clue macOS app

I also made macOS companion app (open source as well) which allow you to view .clue report files in a nice and simple way. It shows full timeline with all event so you can inspect dependencies between user actions and actual app’s responses pretty easily.

Obviously you can view .clue report files with whatever method you want since it’s just json files and mp4 video file combined inside single entity.

🛣️ Roadmap

  • Build basic modules for Network, View Structure, User Interactions and Video
  • Send final report file via email
  • Skip recordable property if this property is invalid
  • Integrate Nonnull and Nullable annotations
  • Migrate some parts of the framework to Swift
  • Add more useful recordable/info modules
  • Slack integration
  • macOS version of the framework to use in macOS apps
  • Suggestions are welcome!

👩‍💻 👨‍💻 Contribution

Contributions are welcome! That's why Open Source is cool! Please check out the Contributing Guide for more details.

☎️ Contacts

Feel free to reach me on twitter @ahmed_sulajman or drop me a line on [email protected] I Hope Clue framework will help you with your bug reports!

📄 Licence

Clue is released under the MIT License. See the LICENSE file.

Comments
  • Create JSONDataWriter for writing JSON files

    Create JSONDataWriter for writing JSON files

    Description

    Right now, we are using CLUDataWriter to write JSON content to file. We'd like to create a specific writer for JSON data (JSONDataWriter) that allows to validate the content before writing to the file system. This new class should be used for all scenario where we are writing JSON data.

    task 
    opened by andrea-prearo 2
  • Refactor JSONDataWriter to support better error notification

    Refactor JSONDataWriter to support better error notification

    Description

    The append method in the JSONWriter class created for https://github.com/Geek-1001/Clue/issues/9 doesn't provide a straightforward way to handle errors. A nice improvements would be to either:

    • Allow JSONWriter to throw a JSONWriterError as needed.
    • Return a Result<Bool, JSONWriterError> to encapsulate the semantics of the return value(s).

    Personally, I prefer using Result. If we go down this path, we have two possible scenarios:

    • We create our own simple Result type (with limited functionality):
    enum Result<T, ErrorType> {
      case success(T)
      case error(ErrorType)
    }
    
    • We take advantage of the existing Result library, which provides a lot of useful additional functionality. This would require introducing third-party Cocoapod dependencies (with the related pros and cons).

    All the suggested above enhancements are not compatible with Objective-C code. In order to start working on these improvements, we first need to migrate all writing functionality related classes to Swift.

    Depends on https://github.com/Geek-1001/Clue/issues/11 and https://github.com/Geek-1001/Clue/issues/12.

    task 
    opened by andrea-prearo 1
  • Migrate classes which use JSONWriter. appendWithJson to Swift to be able to use Result error type

    Migrate classes which use JSONWriter. appendWithJson to Swift to be able to use Result error type

    Description

    Result error type can't be used with Objective-C code. In particular, this applies to the following classes:

    • CLUObserveModule
    • CLUDeviceInfoModule
    • CLUExceptionInfoModule

    Where the appendWithJson method from JSONWriter needs to be called. As this method is now returning a Result<Int, DataWriterError> type, it is not visible/available anymore from Objective-C.

    So we need to migrate the above three classes to Swift so we can fully take advantage of the Result return type for writing JSON.

    task 
    opened by Geek-1001 0
  • Create DataWriter as a base class for JSONWriter

    Create DataWriter as a base class for JSONWriter

    Description

    Right now we are using JSONWriter to write JSON data into the file from recordable/info modules. We need to create DataWriter as a base class to leave an ability to write raw data into the file. This will leave some flexibility and allow to encapsulate all low-level stream management code into DataWriter instead of JSONWriter.

    task 
    opened by Geek-1001 0
  • Skip invalid recordable properties

    Skip invalid recordable properties

    Right now Clue records in final .clue file every property and if it's invalid – it just records an empty string.

    For example if UIView doesn't have backgroundColor property – Clue will record background color as an empty dictionary. It isn't a good idea.

    So we need to skip invalid properties at all.

    task 
    opened by Geek-1001 0
  • PATCH /api/user can not be enabled, version 1.12

    PATCH /api/user can not be enabled, version 1.12

    I would like to make use of the PATCH /api/user/ endpoint available for an 'API Key'. But the Fusionauth configuration only allows me to active POST,PUT,DELETE, and GET for a function. I'm using version 1.12. What I had to do is disable all endpoint, thus giving me access to all functions, but this seem like a security risk.

    opened by radicaljohan 0
  • [BUG] Crash when you instantiate Clue before window setup

    [BUG] Crash when you instantiate Clue before window setup

    Hi,

    I've found a bug: When I setup the Clue instance and I'm not setted up the UIApplication's window yet, then it occurs a crash. The problem is in the ClueController.m:236 where you use the app's window bounds size and not the UIScreen.mainScreen...

    Improve the readme.md or the above mentioned part of the code.

    Thanks

    opened by chosa91 0
  • Main Thread Checker issues

    Main Thread Checker issues

    Hy,

    In our codebase we enabled the Main Thread Checker: You can found it: Edit schema > Run > diagnostics > Runtime API Checking > [✔] Main Thread Checker

    Produces a lot of warning around UIKit component getter/setters.

    opened by chosa91 0
  • MFMailComposeViewController is not dismissed after user sent, saved, or canceled the mail.

    MFMailComposeViewController is not dismissed after user sent, saved, or canceled the mail.

    After I shaken the phone to send the report, I find I'm trapped in the mail screen. I'd like to go back to the APP and continue what I was doing. But currently I have to kill the APP to quit the mail view.

    I checked the code a little bit, it seems that CLUMailDelegate doesn't dismiss the mailComposeController in mailComposeController(_:didFinishWith:error:). I am not sure if it's by design or not. But by adding [controller dismissViewControllerAnimated:YES completion:nil]; after the switch solved my issue. Could you kindly fix this issue on your side? Or I can open a PR with my fix if you need. Thanks!

    opened by ccmjz 2
  • Migrate CLUWritable to Swift

    Migrate CLUWritable to Swift

    Description

    We should create a Writable Swift protocol to replace CLUWritable. This would allow to migrate, over a period of time, all classes dependent on CLUWritable to Swift.

    Depends on https://github.com/Geek-1001/Clue/issues/11.

    opened by andrea-prearo 0
Owner
Ahmed Sulaiman
Product Lead @ Pitch + Building CrossPatch, tool to stay informed on team's progress
Ahmed Sulaiman
Aardvark is a library that makes it dead simple to create actionable bug reports.

Aardvark Aardvark makes it dead simple to create actionable bug reports. Aardvark is made up of a collection of frameworks that provide different bug

Square 257 Dec 19, 2022
Example repo for reproduction of a cocoapods bug

CocoapodsBugExample Run a pod install with use_frameworks! un-commented, see how Pods/CoreDataPodSample compile sources includes CoreDataPodSample.xcd

null 0 Nov 3, 2021
A Swift micro-framework to easily deal with weak references to self inside closures

WeakableSelf Context Closures are one of Swift must-have features, and Swift developers are aware of how tricky they can be when they capture the refe

Vincent Pradeilles 72 Sep 1, 2022
iOS tool that helps with profiling iOS Memory usage.

FBMemoryProfiler An iOS library providing developer tools for browsing objects in memory over time, using FBAllocationTracker and FBRetainCycleDetecto

Facebook Archive 3.4k Dec 7, 2022
iOS project bootstrap aimed at high quality coding.

iOS Project bootstrap How do you setup your iOS projects? Since we are approaching 2015 I’m working on refreshing my project bootstrap. I’ve decided t

Krzysztof Zabłocki 2k Dec 23, 2022
Find memory leaks in your iOS app at develop time.

中文介绍 | FAQ中文 MLeaksFinder MLeaksFinder helps you find memory leaks in your iOS apps at develop time. It can automatically find leaks in UIView and UIV

Tencent 5.3k Dec 22, 2022
Find memory issues & leaks in your iOS app without instruments

HeapInspector Find memory issues & leaks in your iOS app HeapInspector is a debug tool that monitors the memory heap with backtrace recording in your

Christian Menschel 1.8k Nov 24, 2022
iOS library to help detecting retain cycles in runtime.

FBRetainCycleDetector An iOS library that finds retain cycles using runtime analysis. About Retain cycles are one of the most common ways of creating

Facebook 4.1k Dec 26, 2022
In-app memory usage monitoring for iOS

What's Stats Stats displays load statuses such as the memory usage, the CPU load, and the number of subviews in-app, and in realtime. How to use Just

Shuichi Tsutsumi 170 Sep 18, 2022
Makes it easier to support older versions of iOS by fixing things and adding missing methods

PSTModernizer PSTModernizer carefully applies patches to UIKit and related Apple frameworks to fix known radars with the least impact. The current set

PSPDFKit Labs 217 Aug 9, 2022
decoupling between modules in your iOS Project. iOS模块化过程中模块间解耦方案

DecouplingKit 中文readme Podfile platform :ios, '7.0' pod 'DecouplingKit', '~> 0.0.2' DecouplingKit, decoupling between modules in your iOS Project. D

coderyi 139 Aug 23, 2022
The project used in the iOS Architect Crash Course lectures

iOS Architect Crash Course • August 2nd-8th • EssentialDeveloper.com https://www.essentialdeveloper.com/ios-architect-crash-course/aug-2021-a5220 It's

Aleksei Korolev 1 Jul 20, 2022
Skredvarsel app - an iOS, iPadOS, and macOS application that provides daily avalanche warnings from the Norwegian Avalanche Warning Service API

Skredvarsel (Avalanche warning) app is an iOS, iPadOS, and macOS application that provides daily avalanche warnings from the Norwegian Avalanche Warning Service API

Jonas Follesø 8 Dec 15, 2022
A library that enables dynamically rebinding symbols in Mach-O binaries running on iOS.

fishhook fishhook is a very simple library that enables dynamically rebinding symbols in Mach-O binaries running on iOS in the simulator and on device

Meta 4.9k Jan 8, 2023
Simple iOS app blackbox assessment tool. Powered by frida.re and vuejs.

Discontinued Project This project has been discontinued. Please use the new Grapefruit #74 frida@14 compatibility issues frida@14 introduces lots of b

Chaitin Tech 1.6k Dec 16, 2022
Butterfly is a lightweight library for integrating bug-report and feedback features with shake-motion event.

Butterfly is a lightweight library for integrating bug-report and feedback features with shake-motion event. Goals of this project One of th

Zigii Wong 410 Sep 9, 2022
Matthew Asaminew 0 Jan 25, 2022
A Github action for creating generic run report using Markdown

create-report A Github action for creating generic run report (using Markdown!) - uses: michaelhenry/[email protected] with: report-title: "

Michael Henry 4 Apr 19, 2022
A danger-swift plug-in to report xcresult in your PR

DangerSwiftKantoku A danger-swift plug-in report xcresult in your PR. Install DangerSwiftKantoku SwiftPM (Recommended) Add dependency package to your

YUMEMI Inc. 9 Nov 18, 2022
🌊 A clean wave - report unused localized strings

Ripple ?? Ripple - a command line tool that reports unused localization strings in your Xcode project. Install Clone from repo git clone cd Ripple sw

Edoardo Benissimo 3 Sep 16, 2022