dbukowski / dbdebugtoolkit Goto Github PK
View Code? Open in Web Editor NEWSet of easy to use debugging tools for iOS developers & QA engineers.
License: MIT License
Set of easy to use debugging tools for iOS developers & QA engineers.
License: MIT License
There is a weird bug related to custom URLSession
and URLCache
.
Here is my AppDelegate
that enables DBDebugToolkit
.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DBDebugToolkit.setup()
let window = UIWindow(frame: UIScreen.main.bounds)
self.window = window
window.backgroundColor = UIColor.white
window.rootViewController = ViewController()
window.makeKeyAndVisible()
return true
}
}
In my ViewController
, I have a button that loads an url with Cache-Control
header set, meaning the system should cache the response.
class ViewController: UIViewController {
private lazy var button: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Load", for: .normal)
button.addTarget(self, action: #selector(load), for: .touchUpInside)
return button
}()
// Custom URLSession with custom cache and custom diskPath
private lazy var session: URLSession = {
let memoryCapacity = 500 * 1024 * 1024 // 500 MB
let diskCapacity = 500 * 1024 * 1024 // 500 MB
let cache = URLCache(
memoryCapacity: memoryCapacity,
diskCapacity: diskCapacity,
diskPath: "com.xxxxx.yyyyy"
)
let configuration = URLSessionConfiguration.default
configuration.urlCache = cache
return URLSession(
configuration: configuration,
delegate: self,
delegateQueue: nil
)
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(button)
button.frame = view.bounds
}
// MARK: - Private
@objc private func load() {
// A ~130 KB image, small enough to be cached by the default URLSession session
let url = URL(string: "https://images.unsplash.com/photo-1518791841217-8f162f1e1131?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80")!
session.dataTask(with: url).resume()
}
}
extension ViewController: URLSessionDataDelegate {
// MARK: - URLSessionDataDelegate
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
willCacheResponse proposedResponse: CachedURLResponse,
completionHandler: @escaping (CachedURLResponse?) -> Void) {
print("session:willCacheResponse:")
completionHandler(proposedResponse)
}
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
print("dataTask:didReceive response")
completionHandler(.allow)
}
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive data: Data) {
print("data = \(data)")
}
}
When I first clic on the load button, the request is sent to the server, and the logs are the following :
dataTask:didReceive response
data = 129408 bytes
When I clic a second time, the request is not sent to the server, the cached image is used and the logs are the same.
We can note that the log print("session:willCacheResponse:")
is never called even it should be.
The cache folder structure is :
Note the duplication of the Cache.db
files. What's more the fsCachedData
folder, that contains the cached image, is at the wrong location. That means the image is saved in the default URLCache
and not in the custom one I defined in my sesssion.
If I trash the app and remove the line DBDebugToolkit.setup()
in the AppDelegate
, when I first clic on the image, the request is sent to the server and the logs are the following:
dataTask:didReceive response
data = 16384 bytes
data = 16384 bytes
data = 16384 bytes
data = 16383 bytes
data = 16384 bytes
data = 16384 bytes
data = 16384 bytes
data = 14721 bytes
session:willCacheResponse:
Note that this time the logs are correct and session:willCacheResponse:
is printed !
The cache folder structure is
This time there is only one Cache.db
file, and the fsCachedData
folder is under the com.xxxxx.yyyyy
directory. Which is the expected behavior.
So the issue is that a custom url session with a custom url cache is never taken into account with DBDebugToolkit
activated. What's more the URLSessionDataDelegate
method urlSession(_:, dataTask:, willCacheResponse:, completionHandler:)
is not called.
I have multiple debugging libraries in the project and I can't find API to initialize manually parts of the app by QA or other engineers.
Hi,
For first, very good job, I'm trying to use your library and it appears that it's not compatible with URLSessionDelegate
, in my app i'm using https with a self-signed certificate and I use URLSessionDelegate
for debug and when i'm setup your library, delegate are not fired and make me errors.
Steps for reproducing:
AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
#if DEBUG
DBDebugToolkit.setup() // <-- if I comment network works good
#endif
...
SSLDebugSessionDelegate.swift
class SSLDebugSessionDelegate: NSObject, URLSessionDelegate {
/// The host to allow a self-signed SSL certificate for.
let host: String
/** Designated initializer.
- parameter host: The host to which the exception should apply
*/
public init(host: String) {
self.host = host
}
open func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
Log.debug("----DEBUG SESSION DELEGATE----")
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if challenge.protectionSpace.host == host, let trust = challenge.protectionSpace.serverTrust {
let credential = URLCredential(trust: trust)
completionHandler(.useCredential, credential)
return
}
}
completionHandler(.performDefaultHandling, nil)
}
}
let sessionDelegate = SSLDebugSessionDelegate(host:"xxxxxxx")
URLSession(configuration: config, delegate: sessionDelegate, delegateQueue: nil)
And use a self-signed certificate.
__block IMP originalInitWithCoderIMP = [self replaceMethodWithSelector:@selector(initWithCoder:)
block:^UIView * (UIView *blockSelf, NSCoder *aDecoder) {
UIView *res = ((UIView * (*)(id, SEL, NSCoder *))originalInitWithCoderIMP)(blockSelf, @selector(initWithCoder:), aDecoder);
[res db_refreshDebugBorders];
[res db_registerForNotifications];
return res;
}];
I got crash in this line:
UIView *res = ((UIView * (*)(id, SEL, NSCoder *))originalInitWithCoderIMP)(blockSelf, @selector(initWithCoder:), aDecoder);
error:
-[UIView initWithCoder:] must be used from main thread only
I understand the gist of the error, but I don't call load directly
Could you add a button that allows to copy the entire request and response body? Often they are huge, and scrolling and selecting is very inconvenient. cmd+A not always works in simulator
Thank you
Due to the way DBRequestModel intercepts requests progress block is never called when using file upload task for a multi-part request from AFNetworking.
Example code:
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:@"POST" URLString:@"http://example.com/upload" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:@"file://path/to/image.jpg"] name:@"file" fileName:@"filename.jpg" mimeType:@"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This block is never called after installing DBDebugToolkit
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
[progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[uploadTask resume];
This issue caused us a lot of problems related to disappearing file upload progress bars etc. and we tracked the problem to be related with DBDebugToolkit. We can't use it anymore because just calling [DBDebugToolkit setup];
results in our app being unstable and behaving differently than it used to.
In my opinion, if it's impossible to intercept multi-part upload request without breaking its documented behavior, such a requests shouldn't be intercepted and logged at all (logging such requests should be opt-in), or at least we should have a way to disable it.
When you turn off "Logging enabled" at network setting screen, while it still receiving networking logging, it will crashed, because it tries to access DBNetworkTookit
's requests
list even after the list is empty.
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndexedSubscript:]: index ****5807 beyond bounds for empty array'
After installing DBDebugToolkit, the phone crashes every time two successive popups were created (shown at the same time) and then when dismissing both (one after the other) causes the app to crash. Uninstalling DBDebugToolkit causes the crash not to happen. It is replicable within all scenarios whereby two popups were created and then dismissed.
Hello,
Very nice toolkit!
One thing I’ve noticed is, the more blocked the main thread is, the less accurate the measurements are. Especially the FPS measurement, it is possible to lose entire CPU spikes (60 fps -> quick ramp up of a CPU spike for some time -> 60 fps). I have found that the CADisplayLink strategy for measuring FPS is not the most accurate if main thread spikes exist.
One challenge with moving the measurements to a background queue is updating the widget and table view. This can be solved by using a CATextLayer
to update the text in a background queue (CALayer
and CATransation
are thread safe as of iOS 4).
Would you be interested in a PR that accomplishes this?
When 0.8.0 will be available on CocoaPods? Unfortunately, the latest version there (0.7.0) is crashing.
What?
We use Carthage as our dependency manager and would very much like for DBDebugToolkit
to support it.
How?
Supporting Carthage is very easy, I would have opened a PR myself but it seems as if the xcproj is not included in the source code.
All you basically need to do is share the scheme. More info can be found here: https://github.com/Carthage/Carthage#supporting-carthage-for-your-framework
I tried to integrate this toolkit into my project (supported from iOS 13) and I got this crash when I chose the Network.
Thread 1: Exception: "UISearchDisplayController is no longer supported when linking against this version of iOS. Please migrate your application to UISearchController."
For debugging purposes I've written
@objc public func onPlayButtonTapped() {
logger.warning("`last message before crashing`")
assert(false)
}
so that I can log to the console and trigger a crash by tapping on a button.
Generally a crash report gets generated as expected:
Also a screenshot is generated and I can see some of the console output.
However, the last messages, in this case "last message before crashing" is missing.
How can I ensure that the console output is always persisted before the app crash handling terminates?
Edit: I'm using the latest version 0.6.0
xcodebuild: error: Could not resolve package dependencies:
target at '.../Library/Developer/Xcode/DerivedData/../SourcePackages/checkouts/DBDebugToolkit/DBDebugToolkit' contains mixed language source files; feature not supported
Using Tuist with version 0.8.0 and 0.9.0 is failing with above error
Xcode 14.2
text/event-stream requests are "chunked" requests with double newline breaks. Because of the way DBURLProtocol intercepts requests, what ends up happening is all response chunks are batched up until the end and then forwarded to the original response handler. Any ideas how to fix this other than disabling interception altogether?
*** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <DBPerformanceToolkit 0x1741193e0> for the key path "layer.sublayers" from <_UIAlertControllerShimPresenterWindow 0x102cdacd0> because it is not registered as an observer.'
Also wrapping a try-catch around it does not fix the issue.
Hey, i have i case where i would like to register our subclass of DBURLProtocol as a default protocol of db_defaultSessionConfiguration
and db_ephemeralSessionConfiguration
in addition to that i would love to allow to have it also here:
+ (void)setupURLProtocol {
[NSURLProtocol registerClass:[DBURLProtocol class]];
}
Can you allow to set custom class as a default URLProtocol ? Can it be as a static Class property.
The use case is that custom library is creating URLSession
and we don't want to intercept their network requests and would like to skip logging when they make an request:
+ (instancetype)db_defaultSessionConfiguration {
NSURLSessionConfiguration *defaultSessionConfiguration = [self db_defaultSessionConfiguration];
NSMutableArray *originalProtocols = [NSMutableArray arrayWithArray:defaultSessionConfiguration.protocolClasses];
[originalProtocols insertObject:[DBURLProtocol class] atIndex:0];
defaultSessionConfiguration.protocolClasses = originalProtocols;
return defaultSessionConfiguration;
}
``
In a scenario I can reproduce every time, it seems there is an improper management of the table view’s rows:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (39) must be equal to the number of rows contained in that section before the update (38), plus or minus the number of rows inserted or deleted from that section (1 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
The scenario is go in the Requests screen, and do a scroll to bottom. Then quickly open the debug menu and go in the Network screen. The requests are being updated, and the crash occurs in this method:
- (void)networkDebugToolkit:(DBNetworkToolkit *)networkToolkit didUpdateRequestAtIndex:(NSInteger)index {
dispatch_async(dispatch_get_main_queue(), ^{
DBRequestModel *requestModel = self.networkToolkit.savedRequests[index];
if (requestModel == self.openedRequest) {
[self.requestDetailsViewController configureWithRequestModel:requestModel];
}
[self updateRequests];
NSInteger updatedRequestIndex = [self.filteredRequests indexOfObject:requestModel];
if (updatedRequestIndex != NSNotFound) {
[self.tableView reloadRowsAtIndexPaths:@[ [NSIndexPath indexPathForRow:self.filteredRequests.count - 1 - updatedRequestIndex inSection:0] ]
withRowAnimation:UITableViewRowAnimationAutomatic];
}
});
}
/Pods/DBDebugToolkit/DBDebugToolkit/Classes/DBDebugToolkit.m:100 : 42: 'sharedApplication' is unavailable: not available on iOS (App Extension) - Use view controller based solutions where appropriate instead.
Is there a possibility for us to get a SPM support? It would be sooo much easier just to use it. Thanks!
Does this framework support Swift Package Manager?
In cocoapods.org when I search for DBDebugToolKit Pod it shows version 0.5.0 as latest. I am unable to pull the latest version 0.5.1 .
When I try to pull the latest version I see this error on my terminal
[!] CocoaPods could not find compatible versions for pod "DBDebugToolkit":
In Podfile:
DBDebugToolkit (~> 0.5.1)
None of your spec sources contain a spec satisfying the dependency: DBDebugToolkit (~> 0.5.1)
.
Bumping from 0.8.0 to 0.9.0 completely breaks all network requests that are done through Alamofire.
This is what I see in the console:
API MISUSE: NSURLSession delegate Alamofire.SessionDelegate: <Alamofire.SessionDelegate: 0x600000e609b0> (0x600000e609b0)
API MISUSE: dataTask:didReceiveResponse:completionHandler: completion handler not called
Feels like methods are not correctly swizzled anymore?
i guess maybe something wrong in CLURLProtocol
Hi,
Xcode: 15.2
Version: 0.9.0
While compiling the source code getting this error, 'shared' is unavailable in application extensions for iOS: Use view controller based solutions where appropriate instead.
Due to some reason, the solution of using the config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'No' is not used.
Can this be looked upon and update the code?
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'data parameter is nil'
Hello, dbukowski!
I've faced an issue (bug?) while using DBDebugToolkit with Swift iOS(iPhone) application. If app being built to Simulator, or directly to device using XCode and usb cable, everything works perfectly fine.
But for testing purposes, I upload application to Firebase App Distribution. I do that with Fastlane. Fastlane builds the app, and then loads the build to Firebase App Distribution (through Firebase CLI). After that, the build becomes available for installation on approved devices without a cable, directly from Firebase App Distribution service.
When app installed this way, Console output in DBDebugToolkit is incomplete. Many lines are, for sure, missing. And, sometimes, missing lines appear, but with long delay. Sometimes missing lines don't appear at all.
What I've tried:
1/ Change build configuration from debug to adhoc and build directly to device using usb cable - console output was complete, means issue is not dependant on build configuration.
2/ Change build options in Fastfile and build to Firebase again (available options: include bitcode, include symbols) - nothing changed.
About environment:
Xcode 13.1
Swift 5
DBDebugToolkit 0.6.2
Fastlane 2.205.1
Is there a way to set DBCustomVariables that survive App restarts ?
We use AFNetworking and have code like the following
let httpSessionManager = AFHTTPSessionManager(baseURL: baseURL)
httpSessionManager.setTaskWillPerformHTTPRedirectionBlock { urlSession, task, response, request in
return someCheck
? request
: nil // Stop following redirect
}
After we integrated DBDebugToolkit
we noticed that this block was not getting called anymore. Disabling networking logging via DBDebugToolkit.setNetworkRequestsLoggingEnabled(false)
fixes the issue.
I looked at AFNetworking's related code (part 1, part 2) and it does not seem to do anything special which leads me to believe that this is a general issue.
Please let me know if additional information is needed to reproduce this behavior.
// UIView+DBUserInterfaceToolkit.h
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
__block IMP originalInitWithCoderIMP = [self replaceMethodWithSelector:@selector(initWithCoder:)
block:^UIView * (UIView *blockSelf, NSCoder *aDecoder) {
UIView *res = ((UIView * (*)(id, SEL, NSCoder *))originalInitWithCoderIMP)(blockSelf, @selector(initWithCoder:), aDecoder);
[res db_refreshDebugBorders];
[res db_registerForNotifications];
return res;
}];
__block IMP originalInitWithFrameIMP = [self replaceMethodWithSelector:@selector(initWithFrame:)
block:^UIView * (UIView *blockSelf, CGRect frame) {
UIView *res = ((UIView * (*)(id, SEL, CGRect))originalInitWithFrameIMP)(blockSelf, @selector(initWithCoder:), frame);
[res db_refreshDebugBorders];
[res db_registerForNotifications];
return res;
}];
__block IMP originalDeallocIMP = [self replaceMethodWithSelector:NSSelectorFromString(@"dealloc")
block:^(__unsafe_unretained UIView *blockSelf) {
[[NSNotificationCenter defaultCenter] removeObserver:blockSelf];
((void (*)(id, SEL))originalDeallocIMP)(blockSelf, NSSelectorFromString(@"dealloc"));
}];
});
}
Is there a bug with this line:
UIView *res = ((UIView * (*)(id, SEL, CGRect))originalInitWithFrameIMP)(blockSelf, @selector(initWithCoder:), frame);
You pass @selector(initWithCoder:)
but shouldn't you pass @selector(initWithFrame:)
?
Hello!
Version: 0.9.0 installed by cocoapods
App works fine in debug mode, but crashes app immediately on Test Flight/App Store without crash logs.
iOS 15.4.1
Xcode 13.3.1
OS X Monterey
I've experienced similar crashes when external dynamic library was not embedded into the target.
Hi,
it is sometimes helpful to be able to edit NSUserDefaults and Keychain items within the app. Do you plan to add this feature?
Thanks,
Jidrich
A crash occurred when my app continued to send requests while the Network section was open
__71-[DBNetworkViewController networkDebugToolkit:didUpdateRequestAtIndex:]_block_invoke (DBNetworkViewController.m:210)
__29-[DBMainQueueOperation start]_block_invoke (DBMainQueueOperation.m:72)
DBDebugToolkit (0.6.0)
iOS 15.5
MacOS 12
Xcode 13.4.1
Hey, thanks for sharing this one, looks very good :)
As a propolsal i suggest to make a macOS app simillar to NSLogger for example.
version 0.7.0
crash if any network request triggered before DBDebugToolkit.setup()
:
file name: NSURLSessionConfiguration+DBURLProtocol.m
[originalProtocols insertObject:DBNetworkURLProtocolClass atIndex:0];
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'
The cause is from the update for issue #50.
In this update, NSURLSessionConfiguration+DBURLProtocol.m
:
[originalProtocols insertObject:DBNetworkURLProtocolClass atIndex:0]
NSURLSessionConfiguration
's swizzing happens in +(void)load
, which is when app starts, even before appDelegate.didFinishLaunchingWithOptions
.DBNetworkURLProtocolClass
is initialised in + (void)initialize
, which will be triggered by DBDebgToolkit.setup()
.So, without executing DBDebgToolkit.setup()
, DBNetworkURLProtocolClass
will not be initialed, and it will be crashed.
We can use load
to initialise DBNetworkURLProtocolClass
, or ask user to run DBDebgToolkit.setup()
before any network calls that involved NSURLSessionConfiguration
.
My initial setup for DBDebugToolkit is:
let shakeTrigger = DBShakeTrigger()
DBDebugToolkit.setup(with: [shakeTrigger])
DBDebugToolkit.setupCrashReporting()
DBDebugToolkit.addClearDataShortcutItem()
let clearDefaultsAction = DBCustomAction(name: "Clear User Defaults") {
DBDebugToolkit.clearUserDefaults()
}
DBDebugToolkit.add(clearDefaultsAction)
The issue i have: no crash reports are being made.
I have intentionally crashed the app, and even purposefully loaded large images and created OOM errors and there have been no crash logs being logged.
Did i miss some obvious configuration setup?
- (void)saveCrashReportWithName:(NSString *)name
reason:(NSString *)reason
userInfo:(NSDictionary *)userInfo
callStackSymbols:(NSArray<NSString *> *)callStackSymbols {
NSDate *date = [NSDate date];
NSString *consoleOutput = self.consoleOutputCaptor.consoleOutput;
UIImage *screenshot = [[UIApplication sharedApplication].keyWindow snapshot];
-[UIApplication keyWindow] must be used from main thread only
This is sometimes called on a thread that isn't main and prevents crash reports from being saved.
I got the following error detail when I tried the library in a Objective C project with deployment target 8.0.
Undefined symbols for architecture x86_64:
"OBJC_CLASS$_DBDebugToolkit", referenced from:
objc-class-ref in AppDelegate.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
How to overcome this issue ?
For some reason indicatorView in db_removeTouchIndicatorWithTouch can end up being null and adding a null object to a set will crash. There should be a check prior to adding that it's not nil.
My project is kind of a legacy and uses a library called MKNetworkKit https://github.com/MugunthKumar/MKNetworkKit
It uses NSURLConnection
, and like in this ticket #68, swizzling with such library is causing the debug tool kit to fail to read the network calls.
Solution is to downgrade to any version below 0.9.0. However, a new issue will come up. And that is the broken basic authentication.
- (NSInteger)read:(uint8_t *)buffer
maxLength:(NSUInteger)length
{
if ([self streamStatus] == NSStreamStatusClosed) {
return 0;
}
NSInteger totalNumberOfBytesRead = 0;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
while ((NSUInteger)totalNumberOfBytesRead < MIN(length, self.numberOfBytesInPacket)) {
if (!self.currentHTTPBodyPart || ![self.currentHTTPBodyPart hasBytesAvailable]) {
if (!(self.currentHTTPBodyPart = [self.HTTPBodyPartEnumerator nextObject])) {
break;
}
} else {
NSUInteger maxLength = MIN(length, self.numberOfBytesInPacket) - (NSUInteger)totalNumberOfBytesRead;
NSInteger numberOfBytesRead = [self.currentHTTPBodyPart read:&buffer[totalNumberOfBytesRead] maxLength:maxLength];
if (numberOfBytesRead == -1) {
self.streamError = self.currentHTTPBodyPart.inputStream.streamError;
break;
} else {
totalNumberOfBytesRead += numberOfBytesRead;
if (self.delay > 0.0f) {
[NSThread sleepForTimeInterval:self.delay];
}
}
}
}
#pragma clang diagnostic pop
return totalNumberOfBytesRead;
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.