{"id":18385392,"url":"https://github.com/upsight/upsightkit","last_synced_at":"2026-01-23T15:43:56.493Z","repository":{"id":56925428,"uuid":"49564886","full_name":"upsight/UpsightKit","owner":"upsight","description":"Upsight SDK for iOS and tvOS","archived":false,"fork":false,"pushed_at":"2018-11-27T16:04:13.000Z","size":148759,"stargazers_count":1,"open_issues_count":3,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-04-25T15:21:48.586Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/upsight.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-13T09:50:06.000Z","updated_at":"2018-11-27T16:03:27.000Z","dependencies_parsed_at":"2022-08-21T06:20:14.251Z","dependency_job_id":null,"html_url":"https://github.com/upsight/UpsightKit","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upsight%2FUpsightKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upsight%2FUpsightKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upsight%2FUpsightKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upsight%2FUpsightKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/upsight","download_url":"https://codeload.github.com/upsight/UpsightKit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501966,"owners_count":21114681,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T01:17:29.790Z","updated_at":"2026-01-23T15:43:56.457Z","avatar_url":"https://github.com/upsight.png","language":"HTML","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Upsight iOS SDK\n\n####Manual Integration\nTo start with, you need the UpsightKit framework stored on your machine\n\n1. Add the UpsightKit framework to your app in the Build Phases section of the target settings. Also add the CoreTelephony and AdSupport frameworks provided by Apple.\n1. In the Info section the target properties, add UpsightAppToken and UpsightAppSecret. These are of type `string` and were provided on the Upsight dashboard when you added your app.\n1. In the Build Settings section of the target, add -ObjC to the Other Linker Flags field.\n\n#####Xcode Documentation\nThe Upsight iOS SDK uses appledoc to generate visually appealing API method documentation in the form of HTML.\nThese docs are fully indexed and browsable through Xcode.\n\nWhen doing manual integration of the UpsightKit framework, you can optionally install the Upsight documentation so that it is available in Xcode. In order to do that, extract the [iOS SDK zip file](https://help.upsight.com/api-sdk-reference/downloads/) and run the following commands:\n\n    $ ./install_docsets.sh\n\nNote: Installing the Upsight documentation will fail if you have not installed any documentation yet. If the installation process fails, navigate to the Downloads section of Xcode settings and download any other documentation modules. Then run the script again.\n\nTo see the Upsight Documentation, click Help \u003e Documentation and API Reference. On the left hand side, you will find the UpsightKit Documentation.\n\n\n####CocoaPods Integration\nUsing [CocoaPods](https://guides.cocoapods.org) is the easiest way to integrate the SDK into your project. Simply add `pod 'UpsightKit'` to your `Podfile`. Here is an example of a Podfile with two targets, for iOS and tvOS apps:\n\n    target 'MyiOSApp' do\n        platform :ios, '8.0'\n        pod 'UpsightKit', '~\u003e 4.0.5'\n    end\n\n    target 'MyTvOSApp' do\n        platform :tvos, '9.1'\n        pod 'UpsightKit', '~\u003e 4.0.5'\n    end\n\n\n####Debugging\nTo start with, turn on full debug logging:\n\n1. Add `#import \u003cUpsightKit/UpsightKit.h` at the top of your AppDelegate.m file.\n1. In `application:didFinishLoadingWithOptions:` add `[Upsight setDefaultLogLevel:UpsightLoggerLevelDebug];` This will let you see the SDK's debug output while you confirm the integration. Once you're finished the integration, you can remove this line.\n\nIf you run the app now, you’ll see debug output showing SDK activity in XCode's debug console.\n\nAt this point:\n\n* Running the app will generate an \"upsight.config.expired\" message.\n* The server will send a response to this message. The response body defines the configuration for the SDK. You will see the response JSON in the debug console.\n\nSessions are being tracked, and session-based metrics will be available:\n\n* Starting the app will generate an \"upsight.session.start\" message.\n* Stowing the app will generate an \"upsight.session.pause” message.\n* When the app returns to the foreground an \"upsight.session.resume\" message is generated if you have been in the background for less than the configured session gap time (set by the server). Otherwise, a new \"upsight.session.start\" is sent, and the session ID is updated.\n* These messages are batched and sent together: they will not show up on the dashboard immediately. You'll see a message like this in the debug window when the batch of events are sent: \"batch queue sending batch to endpoint: http://batch.upsight-api.com/batch/v1/\"\n\n####Custom Events\nTo add a custom event:\n\n1. In your main view controller’s .m file, add `#import \u003cUpsightKit/UpsightKit.h\u003e`\n1. At the point where you want to emit the event, add \n\n`[Upsight recordAnalyticsEventWithName:@\"myApp.myEvent.name\" properties:nil];`\n\nThe debug console will now show the event has been stored. Note that until a sufficient number of events has been accumulated, or the batch of messages has reached a certain age, the messages will not actually be transmitted to the server.\n\n#### Location\nEnabling location tracking will allow you target and segment your users based on their geographical location. That data will be attached to all the events sent from the device. To provide the SDK with location data, first configure location services in your application.\n\n\tCLLocationManager *locationManager = [[CLLocationManager alloc] init];\n\tlocationManager.delegate = self; // something that implements CLLocationManagerDelegate\n\tlocationManager.desiredAccuracy = kCLLocationAccuracyBest;\n\tlocationManager.distanceFilter = 1.0;\n    if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {\n        [self.locationManager requestWhenInUseAuthorization];\n    }\n    [locationManager startUpdatingLocation];\n\nThen implement the following `CLLocationManagerDelegate` method.\n\n\t- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations\n\t{\n\t    CLLocation *location = [locations firstObject];\n\t    [Upsight setLocation:location];\n\t}\n\nFinally, add a row to your application's info.plist file with the `NSLocationWhenInUseUsageDescription` key, and set the value to the text you want the application to display to users when they're asked for permission to use their location data.\n\nThe location you provide to the SDK will be used until it is updated, or until the user's session ends (the application is put into the background for more than two minutes, or is brought from the background by a push notification).\n \nIt is good practice to have only one instance of CLLocationManager active at a time. When an instance of CLLocationManager goes out of scope you will cease to receive location updates, it is typically saved as a property in an active object instance.\n\nIt is also good practice to set the `desiredAccuracy` to the lowest value possible while still achieving your application's purpose. This is to prevent excessive use of system resources.\n\n#### User Attributes\nUser Attributes are properties you define which appear on every event sent to the Upsight servers. You must first define the attributes in the Info section of your project's settings.\n\t\n1. Add a new row to your project's Info list and name it `UpsightUserAttributes`\n1. Set the new row's type to `Dictionary`\n2. Click on the disclosure triangle to the left of `UpsightUserAttributes`\n3. Add a new row for each of the User Attributes you want to include, providing a name, type, and default value for each\n\nWithin your code you can retrieve and set values for the User Attributes you have created. You access them by name. Assuming you used the preceeding steps to create two attributes called \"myNumberAttribute\" and \"myStringAttribute\" with type Number and String respectively, you would retrieve their values with the following:\n\n\tNSNumber *aNumber = [USUserAttributes numberForKey:@\"myNumberAttribute\"];\n\tNSString *aString = [USUserAttributes stringForKey:@\"myStringAttribute\"];\n\tNSString *aDate = [USUserAttributes dateForKey:@\"myDateAttribute\"];\n\tBOOL aBool = [USUserAttributes boolForKey:@\"myBooleanAttribute\"];\t\n\t\nThis returns the default values as defined in the Info settings, unless you change the values, like this:\n\n\t[USUserAttributes setNumber:@(10) forKey:@\"myNumberAttribute\"];\n\t[USUserAttributes setString:@\"aString\" forKey:@\"myStringAttribute\"];\n\t[USUserAttributes setDate:[NSDate date] forKey:@\"myDateAttribute\"];\n\t[USUserAttributes setBool:NO forKey:@\"myBooleanAttribute\"];\n\t\nNow, `NSNumber *aNumber = [USUserAttributes numberForKey:@\"myNumberAttribute\"];` will return 10, `NSString *aString = [USUserAttributes stringForKey:@\"myStringAttribute\"];` will return aString, `[USUserAttributes dateForKey:@\"myDateAttribute\"]` will return the date and time when the `setDate:forKey:` method was called, and `BOOL aBool = [USUserAttributes boolForKey:@\"myBooleanAttribute\"]` will return NO.\n\nNote that you cannot programmatically add or remove User Attributes. They must be added and removed through the Info settings. Adding and removing User Attributes will cause a discontinuity in the data stored on Upsight's servers, so it is something you should do only with forethought and planning.\n\n####Milestones\nMilestones should be triggered when your user reaches a point in your application that you want to track or on which you want to take action. \n\nMilestones have one required property - a \"Scope\" that uniquely describes that location in your application. \nExamples of __Scope__ are \"main_menu\", \"inventory\", or \"level_up\"\n\n`[Upsight recordMilestoneEventForScope:@\"main_menu\"];`\n\n####Showing Content\nThe SDK receives content from the server in response to events. By default, Milestone events will return content. In the future, publishers will be able to configure any event to return content.\n\nDisplaying marketing content requires a 'Billboard.' A billboard is like an empty frame into which the SDK can place content it receives from the server. Like milestone events, each billboard is associated with a __Scope__. The content returned after a Milestone event will have the same __Scope__ as the Milestone.\n\nThere are three basic steps to show content:\n\n2. Add the USBillboardDelegate protocol to one of your class and implement `presentingViewControllerForBillboard:`\n3. Get the USBillboard object for the scope in which you want to show content, and set its delegate property to point at an object of the class from step 1.\n1. Record a Milestone event for the scope.\n\nBy changing when your code performs steps 2. and 3. you can control when the content will be shown. Steps 2. and 3. do not need to be performed in a particular order, and content won't be shown until both steps have been performed.\n\n######Example 1: Showing a Billboard in a View Controller\nChange the ViewController’s interface definition to indicate that it implements `USBillboardDelegate`. This requires a `\u003cUSBillboardDelegate\u003e` at the end of the View Controler’s `@interface` directive at the top of the .m file:\n\n\t@interface ViewController () \u003cUSBillboardDelegate\u003e\n\nAdd the following method:\n\n\t-(UIViewController *)presentingViewControllerForBillboard:(id\u003cUSBillboard\u003e)aBillboard\n\t{\n\t\treturn self;\n\t}\n\nIn the View Controller’s `viewDidLoad:` method get the USBillboard for your scope:\n\n\tid \u003cUSBillboard\u003e billboard = [Upsight billboardForScope:@“\u003cyour scope name\u003e”];  \n\tbillboard.delegate = self;\n\nFinally, send the Milestone:\n\n\t[Upsight recordMilestoneEventForScope:@“\u003cyour scope name\u003e”];  \n\n######Example 2: Showing a Billboard in a Custom Class\n\nIn this example, content will be shown whenever MyClass's anInterestingMethod is called\n\n\t@implementation MyClass\n\t\n\t- (instancetype)init\n\t{\n\t    self = [super init];\n\t    if (nil != self) {\n\t        \n\t        // Get the billboard for the scope\n\t        id\u003cUSBillboard\u003e billboard = [Upsight billboardForScope:@\"SomethingInteresting\"];\n\t        \n\t        // Set this object as the billboard's delegate\n\t        // so the billboard can call presentingViewControllerForBillboard:\n\t        billboard.delegate = self;\n\t    }\n\t    return self;\n\t}\n\t\n\t\n\t- (void)anInterestingMethod\n\t{\n\t    // Send a Milestone event here because something intersting happened\n\t    [Upsight recordMilestoneEventForScope:@\"SomethingInteresting\"];\n\t}\n\t\n\t\n\t#pragma mark USBillboardDelegate methods\n\t\n\t- (UIViewController *)presentingViewControllerForBillboard:(id\u003cUSBillboard\u003e)aBillboard\n\t{\n\t    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;\n\t    \n\t    while (topController.presentedViewController) {\n\t        topController = topController.presentedViewController;\n\t    }\n\t    \n\t    return topController;\n\t}\n\t\n\t@end\n\n**Note:** When you no longer want content to appear for a given billboard, you should set its delegate back to nil;\n\nIf you want to check if there is any content ready for a given scope, call the `isContentReady` method on the billboard for that scope.\n\n    if (YES == [Upsight billboardForScope:self.currentScope].isContentReady) {\n        // Take action\n    }\n\n#### Reacting to Virtual Goods Promotions and Rewards\nRewards and Virtual Goods Promotions (VGP) are two types of content you can set up through the dashboard and display in a billboard. These are different from other content units in that you, as a programmer, need to know whether or not the customer acepted the offer.\n\nIn the event your user receives a reward or agrees to puchase a virtual good you are notified through one of two optional methods of the billboard delegate protocol:\n\n\t- (void)billboard:(id\u003cUSBillboard\u003e)aBillboard didReceiveReward:(id\u003cUSReward\u003e)aReward;\n\t- (void)billboard:(id\u003cUSBillboard\u003e)aBillboard didReceivePurchase:(id\u003cUSPurchase\u003e)aPurchase;\n\nBy providing an implementation for one or both of these methods your code is given the opportunity to take appropriate action based on what your user has done.\n\nIn the case of a VGP, it is likely you will need to broker a transaction between your user and Apple's App Store. Once that transaction is finished you can track the results using an InAppPurchase event.\n\n####Tracking Monetization\nThere are two separate events that track monetization. If you wish to track a purchase made through the App Store, use the following method from the Upsight class:\n\n\t+ (void)recordInAppPurchaseEventWithResolution:(USPurchaseResolution)resolution\n\t                                       product:(NSString *)product\n\t                                      quantity:(NSUInteger)quantity\n\t                                         price:(float)price\n\t                                      currency:(NSString *)currency;\n                                      \n*resolution* - one of \n\n* `USPurchaseResolutionBuy`if the purchase was successful\n* `USPurchaseResolutionCancel` if the user canceled the purchase\n* `USPurchaseResolutionFraud` if you have detected this is a fraudulent purchase\n* `USPurchaseResolutionRefund` if you are refunding the purchase\n\n*product* - The product name, as provided by the productIdentifier property of an SKProduct object  \n*quantity* - The number of products purchased during the transaction, as set in the quantity property of an SKPayment object  \n*price* - the value returned by `price.floatValue` where `price` is the price property of an SKProduct object  \n*currency* - the value returned by `[priceLocale objectForKey:NSLocaleCurrencyCode]` where `priceLocale` is the priceLocale of an SKProduct object\n\nWhen the SDK sends an InAppPurchase event recorded with this method, it attaches the receipt object from the transaction with the App Store; the Upsight server validates this receipt with the Apple servers.\n\nIf you have a purchase to record that did not involve the Apple store, you can use this method instead:\n\n\t+ (void)recordMonetizationEventWithTotalPrice:(float)totalPrice currency:(NSString *)currency;\n\n*totalPrice* - the total amount of the transaction  \n*currency* - typically retrieved using `[[NSLocale currentLocale] objectForKey:NSLocaleCurrencyCode]`\n\n#### Push\nPush is supported through the USPush interface. Pushed content is displayed through a Push-specific billboard which is managed by the USPush interface. To integrate push, you must:\n\n1. Add the USBillboardDelegate protocol to your App Delegate and implement `presentingViewControllerForBillboard:`\n1. Register for push by calling `[USPush registerForPushNotifications]` in your App Delegate's `didFinishLaunchingWithOptions:` method.\n1. Call the USPush API method `didRegisterUserNotificationSettings:` in your App Delegate's `application:didRegisterUserNotificationSettings:` method.\n1. Call the USPush API method `registerPushToken:` from `didRegisterForRemoteNotificationsWithDeviceToken:`\n1. When a remote notification is received, set the USPush billboard's delegate to be your App Delegate class and call the USPush interface's `handleRemoteNotificationWithUserInfo:` method.\n1. If you plan to start downloading content when a push notification arrives, which is referred to as \"silent\" push notifications, check 'Remote notifications' from the list of Background Modes in the Capabilities section of your target's settings.\n\nHere is a sample AppDelegate implementation that properly integrates Push\n\n\t@interface AppDelegate () \u003cUSBillboardDelegate\u003e\n\t@end\n\t\n\t@implementation AppDelegate\t\n\t\n\t- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n\t    [Upsight setDefaultLogLevel:UpsightLoggerLevelDebug];\n\t\n\t    // Override point for customization after application launch.\n\t    [USPush registerForPushNotifications];\n\t    \n\t    return YES;\n\t}\n\t\n\t- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings\n\t{\n\t    [USPush didRegisterUserNotificationSettings:notificationSettings];\n\t}\n\t\n\t- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken\n\t{\n\t    [USPush registerPushToken:deviceToken];\n\t}\n\t\n\t- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler\n\t{\n\t    [USPush pushBillboard].delegate = self;\n\t    [USPush handleRemoteNotificationWithUserInfo:userInfo];\n\n        if (NULL != completionHandler) {\n            completionHandler(UIBackgroundFetchResultNoData);\n        }\n\t}\n\t\n\t- (void)applicationWillResignActive:(UIApplication *)application {\n\t    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n\t    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.\n\t}\n\t\n\t- (void)applicationDidEnterBackground:(UIApplication *)application {\n\t    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.\n\t    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.\n\t}\n\t\n\t- (void)applicationWillEnterForeground:(UIApplication *)application {\n\t    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.\n\t}\n\t\n\t- (void)applicationDidBecomeActive:(UIApplication *)application {\n\t    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n\t}\n\t\n\t- (void)applicationWillTerminate:(UIApplication *)application {\n\t    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n\t}\n\t\n\t#pragma mark - Billboard delegate\n\t\n\t- (UIViewController *)presentingViewControllerForBillboard:(id\u003cUSBillboard\u003e)aBillboard\n\t{\n\t    UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController;\n\t    \n\t    while (topController.presentedViewController) {\n\t        topController = topController.presentedViewController;\n\t    }\n\t    \n\t    return topController;\n\t}\n\t\n\t@end\n\n#### Managed Variables\n\nThis feature allows you to use Managed Variables in your application; these are variables whose values are set by the server. Managed Variables are referenced by tag.\n\nTo begin with you must declare your Managed Variables in a file called UpsightUXMVariables.plist\n\n![image](./UXMVariableDeclaration.png)\n\nThe default field should contain the value the Managed Variable should return if it has not been updated by the server.\nThe description field should contain a `string` that describes what the Managed Variable is used for.\nThe type field should define the type of the Managed Variable. Variables can be of type `string`, `integer`, `float`, or `boolean`.\nThe tag field defines the name by which you will reference the variable in your code. Each tag must be unique within your app.\nThe max field is only used by number type variables. It defines the largest number value that the Managed Variable can return; any larger values will be replaced by this value.\nThe min field is only used by number type variables. It defines the smallest number value that the Managed Variable can return; any smaller values will be replaced by this value.\n\nIn the source file where you want to access a managed variable, import the Upsight framework.\n\n    #import \u003cUpsightKit/UpsightKit.h\u003e\n\nThen, call the USManagedVariable class method listed below that corresponds to the type of the managed variable you are accessing.\n\n    + (id\u003cUSManagedString\u003e)managedStringWithTag:(NSString *)aTag;\n    + (id\u003cUSManagedInteger\u003e)managedIntegerWithTag:(NSString *)aTag;\n    + (id\u003cUSManagedFloat\u003e)managedFloatWithTag:(NSString *)aTag;\n    + (id\u003cUSManagedBoolean\u003e)managedBooleanWithTag:(NSString *)aTag;\n\nThe returned object will have a `value` method appropriate to its type. The `value` method returns the most recent value retrieved from the server, or the default value if no value has been retrieved yet.\n\n    id\u003cUSManagedString\u003e uxmString = [USManagedVariable managedStringWithTag:@\"personage_name\"];\n    NSLog(@\"The current value of personage_name is %@\", uxmString.stringValue);\n\nIf you wish to be notified when the system has updated the values of the Managed Variables, you can set a delegate on the variable observer:\n\n    [Upsight variableObserver].delegate = self;\n\nThe object you assign as the variableObserver's delegate must conform to the `USManagedVariableObserverDelegate` protocol. This call must be made no earlier than the call to your app delegate's `applicationDidBecomeActive:` method.\n\nIf you implement the optional `observer:didSynchronizeManagedVariables:` method, the Managed Variable observer will call your implementation immediately after all the managed variables have been updated. Included in this call is an `NSArray` containing the tags of all the variables that were updated.\n\nIf you keep any references to Managed Variables that you access before the call to `observer:didSynchronizeManagedVariables:` you need to refresh those references afterward in order to see the new values.\n\nIf you wish to prevent updates to the Managed Variables you can override `observerShouldSynchronizeManagedVariables:`. If you return `NO` from your implementation of this method none of the managed variables will be updated. This method is called every time a session starts, so you will have the chance to accept updates you previously rejected.\n\n#### SDK Size\nThe Upsight SDK increases the size of what you'll submit to iTunesConnect by 450 KB. Adding the Mediation SDK will increase the size of an app by at most 3.1 MB. The impact will be reduced if your app already links against some or all of the frameworks the Mediation SDK depends on.\n\nMeasurements were made by exporting ipa files from Xcode 7.2 using the \"Prepare for upload to App Store\" and comparing their size in Finder.\n\n| App | Base Size | With Platform | With Mediation |\n| --- | --------- | ------------- | -------------- | \n| Test App 1 | 62 KB | 514 KB | 3.1 MB |\n| Test App 2 | 146 KB | 596 KB | 3.2 MB | ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupsight%2Fupsightkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fupsight%2Fupsightkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupsight%2Fupsightkit/lists"}