Commit bb9ada8d authored by Thierry's avatar Thierry

Release 0.0.7

parent 24e62353
......@@ -24,6 +24,7 @@
873B8AEB1B1F5CCA007FD442 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 873B8AEA1B1F5CCA007FD442 /* Main.storyboard */; };
CDCB1F756EA31D8DF51DBCDD /* Pods_GMPhobos_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 766AC377CD6FFC595CF3CF7F /* Pods_GMPhobos_Example.framework */; };
E49977061C59F40000623ABA /* GMPhotoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E49977051C59F40000623ABA /* GMPhotoTest.m */; };
E4AF92C01C71C6C700CF0B64 /* GMPhobosUtilTest.m in Sources */ = {isa = PBXBuildFile; fileRef = E4AF92BF1C71C6C700CF0B64 /* GMPhobosUtilTest.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
......@@ -67,6 +68,8 @@
E0668666E63CBCF8FB52F9C7 /* Pods-GMPhobos_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GMPhobos_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-GMPhobos_Example/Pods-GMPhobos_Example.release.xcconfig"; sourceTree = "<group>"; };
E2E7FEEA45FF5DF8941CEE34 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
E49977051C59F40000623ABA /* GMPhotoTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GMPhotoTest.m; sourceTree = "<group>"; };
E4AF92BF1C71C6C700CF0B64 /* GMPhobosUtilTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GMPhobosUtilTest.m; sourceTree = "<group>"; };
E4AF92C31C71CCE600CF0B64 /* libz.1.2.5.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.1.2.5.tbd; path = usr/lib/libz.1.2.5.tbd; sourceTree = SDKROOT; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -119,6 +122,7 @@
6003F58C195388D20070C39A /* Frameworks */ = {
isa = PBXGroup;
children = (
E4AF92C31C71CCE600CF0B64 /* libz.1.2.5.tbd */,
6003F58D195388D20070C39A /* Foundation.framework */,
6003F58F195388D20070C39A /* CoreGraphics.framework */,
6003F591195388D20070C39A /* UIKit.framework */,
......@@ -158,6 +162,7 @@
6003F5B5195388D20070C39A /* Tests */ = {
isa = PBXGroup;
children = (
E4AF92BF1C71C6C700CF0B64 /* GMPhobosUtilTest.m */,
E49977051C59F40000623ABA /* GMPhotoTest.m */,
6003F5BB195388D20070C39A /* Tests.m */,
6003F5B6195388D20070C39A /* Supporting Files */,
......@@ -408,6 +413,7 @@
files = (
E49977061C59F40000623ABA /* GMPhotoTest.m in Sources */,
6003F5BC195388D20070C39A /* Tests.m in Sources */,
E4AF92C01C71C6C700CF0B64 /* GMPhobosUtilTest.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>classNames</key>
<dict>
<key>GMPhobosUtilTest</key>
<dict>
<key>testDataCompressPerformance</key>
<dict>
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.01</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
</dict>
</dict>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>runDestinationsByUUID</key>
<dict>
<key>7B4DE565-1959-4DF7-998A-B880E0603EBF</key>
<dict>
<key>localComputer</key>
<dict>
<key>busSpeedInMHz</key>
<integer>100</integer>
<key>cpuCount</key>
<integer>1</integer>
<key>cpuKind</key>
<string>Intel Core i7</string>
<key>cpuSpeedInMHz</key>
<integer>2000</integer>
<key>logicalCPUCoresPerPackage</key>
<integer>8</integer>
<key>modelCode</key>
<string>MacBookPro11,2</string>
<key>physicalCPUCoresPerPackage</key>
<integer>4</integer>
<key>platformIdentifier</key>
<string>com.apple.platform.macosx</string>
</dict>
<key>targetArchitecture</key>
<string>x86_64</string>
<key>targetDevice</key>
<dict>
<key>modelCode</key>
<string>iPhone7,2</string>
<key>platformIdentifier</key>
<string>com.apple.platform.iphonesimulator</string>
</dict>
</dict>
</dict>
</dict>
</plist>
......@@ -36,6 +36,11 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
......
../../../../../Pod/Classes/PhobosConfig.h
\ No newline at end of file
../../../../../Pod/Classes/PhobosUtil.h
\ No newline at end of file
This diff is collapsed.
......@@ -14,7 +14,7 @@
buildForArchiving = "YES">
<BuildableReference
BuildableIdentifier = 'primary'
BlueprintIdentifier = '22972869A84C7597A0E65FB2'
BlueprintIdentifier = '958B62752352F89538A448C9'
BlueprintName = 'GMPhobos'
ReferencedContainer = 'container:Pods.xcodeproj'
BuildableName = 'GMPhobos.framework'>
......
#import <UIKit/UIKit.h>
#import "Phobos.h"
#import "PhobosConfig.h"
#import "PhobosUtil.h"
FOUNDATION_EXPORT double GMPhobosVersionNumber;
FOUNDATION_EXPORT const unsigned char GMPhobosVersionString[];
......
//
// GMDataCompressorTest.m
// GMPhobos
//
// Created by Thierry on 16/2/15.
// Copyright © 2016年 licong. All rights reserved.
//
#import <XCTest/XCTest.h>
#import "PhobosUtil.h"
@interface GMPhobosUtilTest : XCTestCase
@property (nonatomic, retain) NSMutableArray *mockArray;
@end
@implementation GMPhobosUtilTest
- (void)setUp {
[super setUp];
_mockArray = [[NSMutableArray alloc] init];
for (int i=0; i<50; i++) {
[_mockArray addObject:@{@"app":@{@"channel":@"App Store",@"name":@"gengmei_doctor",@"version":@"1.8.0"},
@"created_at":@"1455529180",
@"device":@{@"device_id":@"E8E13EB6-030A-40A0-A55A-70E7CE288740",@"device_type":@"ios"},
@"params":@{@"tab_id":@"diary"},
@"type":@"home_click_tab",
@"user_id": @0,
@"version": @110
}];
}
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown {
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
/**
* @brief 测试50条数据压缩速度
*
* @description 经过测试50条数据压缩大约1毫秒左右,几乎不耗时
*
* @since 0.0.7
*/
- (void)testDataCompressPerformance {
[self measureBlock:^{
[self encodeAndCompressArray:_mockArray];
}];
}
/**
* @brief 发送数据测试
*
* @since 0.0.7
*/
- (void)testSendDataToMars{
XCTestExpectation *expectation = [self expectationWithDescription:@"Testing Async Method Works!"];
NSData *mockData = [self encodeAndCompressArray:_mockArray];
[PhobosUtil sendData:mockData success:^(NSInteger code) {
[expectation fulfill];
XCTAssertEqual(code, 200);
}];
//如果超时,则认为发送失败
[self waitForExpectationsWithTimeout:3 handler:^(NSError *error) {
if(error)
{
XCTFail(@"Expectation Failed with error: %@", error);
}
}];
}
- (NSData *)encodeAndCompressArray:(id)obj {
NSData *data = [NSJSONSerialization dataWithJSONObject:obj options:0 error:nil];
NSData *compressedData = [PhobosUtil compressData:data];
return compressedData;
}
@end
......@@ -12,7 +12,7 @@
#define PhobosCacheKey @"PhobosCacheKey"
NSString *const MockAppName = @"gengmei";
NSString *const MockAppName = @"gengmei_test";
NSString *const MockChannelId = @"AppStore";
NSString *const MockEventId = @"eventId";
NSInteger const MockUserId = 1;
......@@ -91,27 +91,15 @@ NSInteger const MockUserId = 1;
* @since <#version number#>
*/
- (void)testTrackEventWithAttrAndSendNow{
// Given
NSDictionary *attr = @{@"attr":@"track_attr"};
// When
[_client track:MockEventId attributes:attr sendNow:YES];
// Then
NSArray *array = [_cache fetchObjectAtDiskWithkey:PhobosCacheKey];
XCTAssertTrue(array.count == 0, @"array should be empty");
}
/**
* @brief 测试数据压缩的性能,准备50条数据
*
* @since <#version number#>
*/
- (void)testCompressPerformanceExample {
NSDictionary *attr = @{@"attr":@"track_attr"};
for (int i=0; i<=49; i++) {
[_client track:MockEventId attributes:attr];
}
[self measureBlock:^{
[_client track:MockEventId attributes:attr];
}];
}
- (void)verfiyDict:(NSDictionary *)dict{
NSArray *keys = [dict allKeys];
XCTAssertTrue([keys containsObject:@"type"], @"Missing type");
......
......@@ -16,6 +16,11 @@
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
......
......@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = "GMPhobos"
s.version = "0.0.6"
s.version = "0.0.7"
s.summary = "GM statistic data sdk"
# This description is used to generate tags and improve search results.
......@@ -26,7 +26,7 @@ Pod::Spec.new do |s|
# s.screenshots = "www.example.com/screenshots_1", "www.example.com/screenshots_2"
s.license = 'MIT'
s.author = { "licong" => "licong@gmei.com" }
s.source = { :git => "http://git.gengmei.cc/gengmeiios/GMPhobos.git", :tag => "0.0.6" }
s.source = { :git => "http://git.gengmei.cc/gengmeiios/GMPhobos.git", :tag => "0.0.7" }
# s.social_media_url = 'https://twitter.com/<TWITTER_USERNAME>'
s.platform = :ios, '7.0'
......
......@@ -7,21 +7,11 @@
//
#import "Phobos.h"
#import <AdSupport/AdSupport.h>
#import <zlib.h>
#import "WMCacheService.h"
#import <AdSupport/AdSupport.h>
#import "PhobosUtil.h"
#import "PhobosConfig.h"
#ifdef DEBUG
#define phobosLog(...) NSLog(@"[Phobos] %@",__VA_ARGS__)
#else
#define phobosLog(...)
#endif
#define PhobosHaveOpenApp @"PhobosHaveOpenApp" //是否打开过APP
#define PhobosBeginTime @"PhobosBeginTime" //记录APP打开|从后台启动时的时间戳
#define PhobosEndTime @"PhobosEndTime" //记录APP退出|退到后台时的时间戳
#define PhobosCacheKey @"PhobosCacheKey"
#define PhobosShardCount 50 //收集数据分段发送的个数
static Phobos *sharedClient = nil;
static NSString *sdkVersion = @"110";
......@@ -177,12 +167,12 @@ static NSString *sdkVersion = @"110";
}
- (void)track:(NSString *)eventId attributes:(NSDictionary *)attributes{
[self track:eventId attributes:attributes sendNow:NO];
NSArray *array = [[WMCacheService sharedInstance] fetchObjectAtDiskWithkey:PhobosCacheKey];
//超过一定数量的话,统一发送一次
if (array.count > PhobosShardCount) {
[self sendArray:array];
}
[self track:eventId attributes:attributes sendNow:NO];
}
- (void)track:(NSString *)eventId attributes:(NSDictionary *)attributes sendNow:(BOOL)sendNow{
......@@ -285,7 +275,6 @@ static NSString *sdkVersion = @"110";
}
}
/**
* @brief 发送数组
*
......@@ -299,144 +288,19 @@ static NSString *sdkVersion = @"110";
}
@try {
NSData *JSON = [self encodeJSON:array];
NSData *compressedData = [self compressData:JSON];
[self sendData:compressedData];
}
@catch (NSException *exception) {
phobosLog(exception);
}
}
/**
* @brief 上传数据
*
* @param data
*
* @since 0.0.1
*/
- (void)sendData:(NSData *)data {
#ifdef DEBUG
NSString *url = @"http://log.test.gengmei.cc/log/collect";
#else
NSString *url = @"http://log.gengmei.cc/log/collect";
#endif
@try {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
request.HTTPBody = data;
request.HTTPMethod = @"POST";
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
NSInteger code = res.statusCode;
if (code == 200) {
NSData *compressedData = [PhobosUtil compressData:JSON];
if (compressedData) {
[PhobosUtil sendData:compressedData success:^(NSInteger code) {
phobosLog(@"✈ ---------- ✈ data arrived Mars");
[[WMCacheService sharedInstance] removeObjectAtDiskWithkey:PhobosCacheKey];
}
}];
}];
}
}
@catch (NSException *exception) {
phobosLog(exception);
}
}
/**
* @brief 压缩待上传的数据。经过测试,50条数据压缩大概6毫秒,所以不要放在异步线程中处理
*
* @param originData 压缩前的数据
*
* @return 压缩后的数据
*
* @since 0.0.1
*/
- (NSData *)compressData:(NSData *)originData{
if (!originData || [originData length] == 0)
{
if (_logEnabled) {
phobosLog(@"Error: Can't compress an empty or null NSData object.");
}
return nil;
}
z_stream zlibStreamStruct;
zlibStreamStruct.zalloc = Z_NULL; // Set zalloc, zfree, and opaque to Z_NULL so
zlibStreamStruct.zfree = Z_NULL; // that when we call deflateInit2 they will be
zlibStreamStruct.opaque = Z_NULL; // updated to use default allocation functions.
zlibStreamStruct.total_out = 0; // Total number of output bytes produced so far
zlibStreamStruct.next_in = (Bytef*)[originData bytes]; // Pointer to input bytes
zlibStreamStruct.avail_in = (uInt)[originData length]; // Number of input bytes left to process
int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
if (initError != Z_OK)
{
NSString *errorMsg = nil;
if (_logEnabled) {
switch (initError)
{
case Z_STREAM_ERROR:
errorMsg = @"Invalid parameter passed in to function.";
break;
case Z_MEM_ERROR:
errorMsg = @"Insufficient memory.";
break;
case Z_VERSION_ERROR:
errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
break;
default:
errorMsg = @"Unknown error code.";
break;
}
}
return nil;
}
NSMutableData *compressedData = [NSMutableData dataWithLength:[originData length] * 1.01 + 12];
NSInteger deflateStatus;
do
{
zlibStreamStruct.next_out = [compressedData mutableBytes] + zlibStreamStruct.total_out;
zlibStreamStruct.avail_out = (uInt)([compressedData length] - zlibStreamStruct.total_out);
deflateStatus = deflate(&zlibStreamStruct, Z_FINISH);
} while ( deflateStatus == Z_OK );
// Check for zlib error and convert code to usable error message if appropriate
if (deflateStatus != Z_STREAM_END)
{
NSString *errorMsg = nil;
if (_logEnabled) {
switch (deflateStatus)
{
case Z_ERRNO:
errorMsg = @"Error occured while reading file.";
break;
case Z_STREAM_ERROR:
errorMsg = @"The stream state was inconsistent (e.g., next_in or next_out was NULL).";
break;
case Z_DATA_ERROR:
errorMsg = @"The deflate data was invalid or incomplete.";
break;
case Z_MEM_ERROR:
errorMsg = @"Memory could not be allocated for processing.";
break;
case Z_BUF_ERROR:
errorMsg = @"Ran out of output buffer for writing compressed bytes.";
break;
case Z_VERSION_ERROR:
errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
break;
default:
errorMsg = @"Unknown error code.";
break;
}
}
deflateEnd(&zlibStreamStruct);
return nil;
}
deflateEnd(&zlibStreamStruct);
[compressedData setLength: zlibStreamStruct.total_out];
return compressedData;
}
#pragma mark - helpers
/**
* @brief 将对象转成JSON格式数据
......
//
// PhobosConfig.h
// Pods
//
// Created by Thierry on 16/2/15.
//
//
#ifndef PhobosConfig_h
#define PhobosConfig_h
#ifdef DEBUG
#define phobosLog(...) NSLog(@"[Phobos] %@",__VA_ARGS__)
#else
#define phobosLog(...)
#endif
#define PhobosHaveOpenApp @"PhobosHaveOpenApp" //是否打开过APP
#define PhobosBeginTime @"PhobosBeginTime" //记录APP打开|从后台启动时的时间戳
#define PhobosEndTime @"PhobosEndTime" //记录APP退出|退到后台时的时间戳
#define PhobosCacheKey @"PhobosCacheKey"
#define PhobosShardCount 50 //收集数据分段发送的个数
#endif /* PhobosConfig_h */
//
// PhobosUtil.h
// Phobos工具类
//
// Created by Thierry on 16/2/15.
//
//
#import <Foundation/Foundation.h>
typedef void (^SendDataSuccessBlock)(NSInteger code);
@interface PhobosUtil : NSObject
/**
* @brief 压缩待上传的数据。经过测试,50条数据压缩大概6毫秒,所以不要放在异步线程中处理
*
* @param originData 压缩前的数据
*
* @return 压缩后的数据
*
* @since 0.0.1
*/
+ (NSData *)compressData:(NSData *)originData;
/**
* @brief 上传数据
*
* @param data,success
*
* @since 0.0.1
*/
+ (void)sendData:(NSData *)data success:(SendDataSuccessBlock)success;
@end
//
// PhobosUtil.m
// Pods
//
// Created by Thierry on 16/2/15.
//
//
#import "PhobosUtil.h"
#import <zlib.h>
#import "PhobosConfig.h"
#import "WMCacheService.h"
@implementation PhobosUtil
+ (NSData *)compressData:(NSData *)originData{
if (!originData || [originData length] == 0)
{
return nil;
}
z_stream zlibStreamStruct;
zlibStreamStruct.zalloc = Z_NULL; // Set zalloc, zfree, and opaque to Z_NULL so
zlibStreamStruct.zfree = Z_NULL; // that when we call deflateInit2 they will be
zlibStreamStruct.opaque = Z_NULL; // updated to use default allocation functions.
zlibStreamStruct.total_out = 0; // Total number of output bytes produced so far
zlibStreamStruct.next_in = (Bytef*)[originData bytes]; // Pointer to input bytes
zlibStreamStruct.avail_in = (uInt)[originData length]; // Number of input bytes left to process
int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);
if (initError != Z_OK)
{
NSString *errorMsg = nil;
switch (initError)
{
case Z_STREAM_ERROR:
errorMsg = @"Invalid parameter passed in to function.";
break;
case Z_MEM_ERROR:
errorMsg = @"Insufficient memory.";
break;
case Z_VERSION_ERROR:
errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
break;
default:
errorMsg = @"Unknown error code.";
break;
}
return nil;
}
NSMutableData *compressedData = [NSMutableData dataWithLength:[originData length] * 1.01 + 12];
NSInteger deflateStatus;
do
{
zlibStreamStruct.next_out = [compressedData mutableBytes] + zlibStreamStruct.total_out;
zlibStreamStruct.avail_out = (uInt)([compressedData length] - zlibStreamStruct.total_out);
deflateStatus = deflate(&zlibStreamStruct, Z_FINISH);
} while ( deflateStatus == Z_OK );
// Check for zlib error and convert code to usable error message if appropriate
if (deflateStatus != Z_STREAM_END)
{
NSString *errorMsg = nil;
switch (deflateStatus)
{
case Z_ERRNO:
errorMsg = @"Error occured while reading file.";
break;
case Z_STREAM_ERROR:
errorMsg = @"The stream state was inconsistent (e.g., next_in or next_out was NULL).";
break;
case Z_DATA_ERROR:
errorMsg = @"The deflate data was invalid or incomplete.";
break;
case Z_MEM_ERROR:
errorMsg = @"Memory could not be allocated for processing.";
break;
case Z_BUF_ERROR:
errorMsg = @"Ran out of output buffer for writing compressed bytes.";
break;
case Z_VERSION_ERROR:
errorMsg = @"The version of zlib.h and the version of the library linked do not match.";
break;
default:
errorMsg = @"Unknown error code.";
break;
}
deflateEnd(&zlibStreamStruct);
return nil;
}
deflateEnd(&zlibStreamStruct);
[compressedData setLength: zlibStreamStruct.total_out];
phobosLog([NSString stringWithFormat:@"Compressed file from %lu B to %lu B", [originData length], [compressedData length]]);
return compressedData;
}
+ (void)sendData:(NSData *)data success:(SendDataSuccessBlock)success {
#ifdef DEBUG
NSString *url = @"http://log.test.gengmei.cc/log/collect";
#else
NSString *url = @"http://log.gengmei.cc/log/collect";
#endif
@try {
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
request.HTTPBody = data;
request.HTTPMethod = @"POST";
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSHTTPURLResponse *res = (NSHTTPURLResponse *)response;
NSInteger code = res.statusCode;
if (code == 200) {
if (success) {
success(code);
}
}
}];
}
@catch (NSException *exception) {
phobosLog(exception);
}
}
@end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment