Permalink
Browse files

Merge branch 'master' of https://github.com/omz/AppSales-Mobile

Conflicts:
	AppSalesMobile.xcodeproj/project.pbxproj
	Classes/App.m
	Classes/ReviewManager.m
  • Loading branch information...
2 parents d46515e + c23bbed commit 98d64906d61ab07b0ad1727636f5f64bc9400c2c @jonkean jonkean committed Apr 27, 2011
View
@@ -10,4 +10,5 @@ build
*.perspectivev3
*.mode1v3
*.xcworkspacedata
-*.xcuserstate
+*.xcuserstate
+*xcuserdata*
@@ -138,6 +138,7 @@
7FEA5B40105B6CD2000A12E6 /* ReviewTemplate.html in Resources */ = {isa = PBXBuildFile; fileRef = 7FEA5B3F105B6CD2000A12E6 /* ReviewTemplate.html */; };
7FEA5B43105B6D38000A12E6 /* SingleReviewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEA5B42105B6D38000A12E6 /* SingleReviewController.m */; };
7FEA5C2C105C1A57000A12E6 /* 5stars_gray.png in Resources */ = {isa = PBXBuildFile; fileRef = 7FEA5C2B105C1A57000A12E6 /* 5stars_gray.png */; };
+ B03E6A4D134BC34C00532893 /* AppleFiscalCalendar.m in Sources */ = {isa = PBXBuildFile; fileRef = B03E6A4C134BC34C00532893 /* AppleFiscalCalendar.m */; };
F22984BC11DD32700067EFD2 /* Graphs_Highlighted@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F22984BA11DD32700067EFD2 /* Graphs_Highlighted@2x.png */; };
F22984BD11DD32700067EFD2 /* Graphs@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F22984BB11DD32700067EFD2 /* Graphs@2x.png */; };
F22984C911DD343C0067EFD2 /* 5stars_gray@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F22984C711DD343C0067EFD2 /* 5stars_gray@2x.png */; };
@@ -869,6 +870,8 @@
7FEA5B42105B6D38000A12E6 /* SingleReviewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SingleReviewController.m; sourceTree = "<group>"; };
7FEA5C2B105C1A57000A12E6 /* 5stars_gray.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 5stars_gray.png; sourceTree = "<group>"; };
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ B03E6A4B134BC34C00532893 /* AppleFiscalCalendar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppleFiscalCalendar.h; sourceTree = "<group>"; };
+ B03E6A4C134BC34C00532893 /* AppleFiscalCalendar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppleFiscalCalendar.m; sourceTree = "<group>"; };
F22984BA11DD32700067EFD2 /* Graphs_Highlighted@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Graphs_Highlighted@2x.png"; sourceTree = "<group>"; };
F22984BB11DD32700067EFD2 /* Graphs@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Graphs@2x.png"; sourceTree = "<group>"; };
F22984C711DD343C0067EFD2 /* 5stars_gray@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "5stars_gray@2x.png"; sourceTree = "<group>"; };
@@ -2171,6 +2174,14 @@
7F4A525A116A9F4E00FA2019 /* NSDateFormatter+SharedInstances.m */,
7FB0C4400EB97BC1005E2C45 /* NSDictionary+HTTP.h */,
7FB0C4410EB97BC1005E2C45 /* NSDictionary+HTTP.m */,
+ 7FB0C4A90EB99D01005E2C45 /* CurrencyManager.h */,
+ 7FB0C4AA0EB99D01005E2C45 /* CurrencyManager.m */,
+ B03E6A4B134BC34C00532893 /* AppleFiscalCalendar.h */,
+ B03E6A4C134BC34C00532893 /* AppleFiscalCalendar.m */,
+ 34DCF08E0F0461A2009F9929 /* SFHFKeychainUtils.h */,
+ 34DCF08D0F0461A2009F9929 /* SFHFKeychainUtils.m */,
+ 7FE3138E1152891A004DEA7C /* ProgressHUD.h */,
+ 7FE3138F1152891A004DEA7C /* ProgressHUD.m */,
7FE313A0115294C9004DEA7C /* NSData+Compression.h */,
7FE313A1115294C9004DEA7C /* NSData+Compression.m */,
FAED39B811E7E86E003061C4 /* NSString+UnescapeHtml.h */,
@@ -3128,6 +3139,7 @@
FA009F1211E93E8200242DFF /* AppManager.m in Sources */,
FA027E6E12407BF500067812 /* RegexKitLite.m in Sources */,
FAA9E92712E22BB200ADD3C9 /* AppSalesUtils.m in Sources */,
+ B03E6A4D134BC34C00532893 /* AppleFiscalCalendar.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -3239,16 +3251,20 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_DYNAMIC_NO_PIC = YES;
+ GCC_FAST_MATH = YES;
GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_OPTIMIZATION_LEVEL = 3;
+ GCC_PREPROCESSOR_DEFINITIONS = "NS_BLOCK_ASSERTIONS=1";
+ GCC_STRICT_ALIASING = YES;
GCC_VERSION = com.apple.compilers.llvmgcc42;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 3.1;
- PREBINDING = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
PRODUCT_NAME = AppSales;
PROVISIONING_PROFILE = "";
SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
};
name = Distribution;
};
@@ -3259,13 +3275,16 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_DYNAMIC_NO_PIC = YES;
+ GCC_FAST_MATH = YES;
GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = "APPSALES_DEBUG=1";
+ GCC_STRICT_ALIASING = YES;
GCC_VERSION = com.apple.compilers.llvmgcc42;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.0;
- PREBINDING = YES;
PRODUCT_NAME = AppSales;
PROVISIONING_PROFILE = "";
SDKROOT = iphoneos;
@@ -3281,13 +3300,16 @@
CODE_SIGN_IDENTITY = "iPhone Developer";
GCC_C_LANGUAGE_STANDARD = c99;
GCC_DYNAMIC_NO_PIC = YES;
+ GCC_FAST_MATH = YES;
GCC_INLINES_ARE_PRIVATE_EXTERN = YES;
+ GCC_OPTIMIZATION_LEVEL = 3;
+ GCC_PREPROCESSOR_DEFINITIONS = "NS_BLOCK_ASSERTIONS=1";
+ GCC_STRICT_ALIASING = YES;
GCC_VERSION = com.apple.compilers.llvmgcc42;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 3.1;
- PREBINDING = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
PRODUCT_NAME = AppSales;
PROVISIONING_PROFILE = "";
SDKROOT = iphoneos;
View
@@ -17,15 +17,21 @@
NSMutableDictionary *reviewsByUser;
NSMutableDictionary *lastTimeRegionDownloaded; // mapping of app store to NSDate of last time reviews fetched
float averageStars;
+ float recentStars;
+ NSString *recentVersion;
}
@property (readonly) NSString *appID;
@property (readonly) NSString *appName;
+@property (readonly) NSString *recentVersion; // the current app version
@property (readonly) NSDictionary *reviewsByUser;
-@property (readonly) NSUInteger totalReviewsCount;
+@property (readonly) NSUInteger totalReviewsCount; // all reviews downloaded, for any version (current or old)
@property (readonly) NSUInteger newReviewsCount;
+@property (readonly) NSUInteger recentReviewsCount; // reviews for the current app version
+@property (readonly) NSUInteger newRecentReviewsCount; // freshly downloaded reviews for the current app version
@property (readonly) NSArray *allAppNames;
@property (readonly) float averageStars;
+@property (readonly) float recentStars; // average stars for the current app version
- (id) initWithID:(NSString*)identifier name:(NSString*)name;
- (void) addOrReplaceReview:(Review*)review;
View
@@ -11,7 +11,28 @@
@implementation App
-@synthesize appID, appName, reviewsByUser, averageStars;
+@synthesize appID, appName, reviewsByUser, averageStars, recentStars, recentVersion;
+
+- (void) updateAverages {
+ double overallSum = 0;
+ double mostRecentVersionSum = 0;
+ int mostRecentVersionCount = 0;
+ for (Review *r in reviewsByUser.allValues) {
+ overallSum += r.stars;
+ if (recentVersion == nil || [recentVersion compare:r.version] == NSOrderedAscending) {
+ [recentVersion release];
+ recentVersion = [r.version retain];
+ mostRecentVersionCount = 0;
+ mostRecentVersionSum = 0;
+ }
+ if ([r.version isEqualToString:recentVersion]) {
+ mostRecentVersionCount++;
+ mostRecentVersionSum += r.stars;
+ }
+ }
+ averageStars = overallSum / reviewsByUser.count;
+ recentStars = mostRecentVersionSum / mostRecentVersionCount;
+}
- (id)initWithCoder:(NSCoder *)coder {
self = [super init];
@@ -25,6 +46,12 @@ - (id)initWithCoder:(NSCoder *)coder {
if (lastTimeRegionDownloaded == nil) { // backwards compatibility with older serialized objects
lastTimeRegionDownloaded = [NSMutableDictionary new];
}
+ if ([coder containsValueForKey:@"recentVersion"] && [coder containsValueForKey:@"recentStars"]) {
+ recentVersion = [[coder decodeObjectForKey:@"recentVersion"] retain];
+ recentStars = [coder decodeFloatForKey:@"recentStars"];
+ } else {
+ [self updateAverages]; // older serialized object
+ }
}
return self;
}
@@ -78,30 +105,47 @@ - (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:appID forKey:@"appID"];
[coder encodeObject:appName forKey:@"appName"];
- [coder encodeObject:reviewsByUser forKey:@"reviewsByUser"];
[coder encodeObject:allAppNames forKey:@"allAppNames"];
[coder encodeObject:lastTimeRegionDownloaded forKey:@"lastTimeRegionDownloaded"];
[coder encodeFloat:averageStars forKey:@"averageStars"];
+ [coder encodeObject:reviewsByUser forKey:@"reviewsByUser"];
+ [coder encodeObject:recentVersion forKey:@"recentVersion"];
+ [coder encodeFloat:recentStars forKey:@"recentStars"];
}
- (NSString *) description {
- return [NSString stringWithFormat:@"App %@ (%@)", self.appName, self.appID];
+ return [NSString stringWithFormat:NSLocalizedString(@"App %@ (%@)", nil), self.appName, self.appID];
}
- (void) addOrReplaceReview:(Review*)review {
[reviewsByUser setObject:review forKey:review.user];
-
- double sum = 0;
- for (Review *r in reviewsByUser.allValues) {
- sum += r.stars;
- }
- averageStars = sum / reviewsByUser.count;
+ [self updateAverages];
}
- (NSUInteger) totalReviewsCount {
return reviewsByUser.count;
}
+- (NSUInteger) recentReviewsCount {
+ NSUInteger recentReviewsCount = 0;
+ for (Review *r in reviewsByUser.allValues) {
+ if ([r.version isEqualToString:recentVersion]) {
+ recentReviewsCount++;
+ }
+ }
+ return recentReviewsCount;
+}
+
+- (NSUInteger) newRecentReviewsCount {
+ NSUInteger newReviewsCount = 0;
+ for (Review *r in reviewsByUser.allValues) {
+ if (r.newOrUpdatedReview && [r.version isEqualToString:recentVersion]) {
+ newReviewsCount++;
+ }
+ }
+ return newReviewsCount;
+}
+
- (NSUInteger) newReviewsCount {
NSUInteger newReviewsCount = 0;
for (Review *r in reviewsByUser.allValues) {
@@ -119,6 +163,7 @@ - (void) dealloc
[reviewsByUser release];
[allAppNames release];
[lastTimeRegionDownloaded release];
+ [recentVersion release];
[super dealloc];
}
View
@@ -51,7 +51,9 @@ @implementation AppCellView
- (id)initWithCell:(AppCell *)appCell
{
- [super initWithFrame:appCell.bounds];
+ CGRect bounds = appCell.bounds;
+ bounds.size.height = 60;
+ [super initWithFrame:bounds];
self.backgroundColor = [UIColor whiteColor];
cell = appCell;
return self;
@@ -63,7 +65,7 @@ - (void)drawRect:(CGRect)rect
App *app = cell.app;
[[UIColor colorWithWhite:0.95 alpha:1.0] set];
- CGContextFillRect(c, CGRectMake(0,0,45,44));
+ CGContextFillRect(c, CGRectMake(0,0,45,59));
UIImage *appIcon = [[AppIconManager sharedManager] iconForAppID:app.appID];
[appIcon drawInRect:CGRectMake(6, 7, 28, 28)];
@@ -72,7 +74,7 @@ - (void)drawRect:(CGRect)rect
[((cell.highlighted) ? [UIColor whiteColor] : [UIColor blackColor]) set];
[app.appName drawInRect:CGRectMake(50, 3, 140, 30) withFont:[UIFont boldSystemFontOfSize:17.0]];
- [[UIImage imageNamed:@"5stars_gray.png"] drawInRect:CGRectMake(200, 15, 90, 15)];
+ [[UIImage imageNamed:@"5stars_gray.png"] drawInRect:CGRectMake(200, 8, 90, 15)];
UIImage *starsImage = [UIImage imageNamed:@"5stars.png"];
CGSize size = CGSizeMake(90,15);
if (&UIGraphicsBeginImageContextWithOptions) {
@@ -82,30 +84,46 @@ - (void)drawRect:(CGRect)rect
}
CGContextRef ctx = UIGraphicsGetCurrentContext();
[starsImage drawInRect:CGRectMake(0,0,90,15)];
- float averageStars = [app averageStars];
+ float averageStars = [app recentStars];
float widthOfStars = 90.0 - (averageStars / 5.0) * 90.0;
[[UIColor clearColor] set];
CGContextSetBlendMode(ctx, kCGBlendModeCopy);
CGContextFillRect(ctx, CGRectMake(90 - widthOfStars, 0, widthOfStars, 15));
UIImage *averageStarsImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
- [averageStarsImage drawInRect:CGRectMake(200, 15, 90, 15)];
+ [averageStarsImage drawInRect:CGRectMake(200, 8, 90, 15)];
//[[UIImage imageNamed:@"5stars.png"] drawInRect:CGRectMake(200, 15, 88, 15)];
if(cell.highlighted)
[[UIColor whiteColor] set];
- else if(app.newReviewsCount)
+ else if(app.newRecentReviewsCount)
[[UIColor redColor] set];
else
[[UIColor darkGrayColor] set];
+
+ [app.recentVersion drawInRect:CGRectMake(50, 25, 140, 15) withFont:[UIFont systemFontOfSize:12.0]];
+ NSString *recentSummary = [NSString stringWithFormat:NSLocalizedString(@"%1.2f avg, %i reviews",nil), app.recentStars, app.recentReviewsCount];
+ if (app.newRecentReviewsCount) {
+ recentSummary = [recentSummary stringByAppendingFormat:NSLocalizedString(@" (%i new)",nil), app.newRecentReviewsCount];
+ }
+ CGSize recentSummarySize = [recentSummary sizeWithFont:[UIFont systemFontOfSize:12.0]];
+ [recentSummary drawInRect:CGRectMake(290-recentSummarySize.width, 40-recentSummarySize.height, recentSummarySize.width, recentSummarySize.height) withFont:[UIFont systemFontOfSize:12.0]];
- int numberOfReviews = [app.reviewsByUser count];
- NSString *numberOfReviewsDescription = [NSString stringWithFormat:NSLocalizedString(@"%i reviews",nil), numberOfReviews];
- if (app.newReviewsCount) {
- numberOfReviewsDescription = [numberOfReviewsDescription stringByAppendingFormat:NSLocalizedString(@" (%i new)",nil), app.newReviewsCount];
+ if(cell.highlighted)
+ [[UIColor whiteColor] set];
+ else if(app.newReviewsCount)
+ [[UIColor redColor] set];
+ else
+ [[UIColor lightGrayColor] set];
+
+ [@"Overall" drawInRect:CGRectMake(50, 40, 140, 15) withFont:[UIFont italicSystemFontOfSize:12.0]];
+ NSString *overallSummary = [NSString stringWithFormat:NSLocalizedString(@"%1.2f avg, %i reviews",nil), app.averageStars, [app.reviewsByUser count]];
+ if (app.newRecentReviewsCount) {
+ overallSummary = [overallSummary stringByAppendingFormat:NSLocalizedString(@" (%i new)",nil), app.newRecentReviewsCount];
}
- [numberOfReviewsDescription drawInRect:CGRectMake(50, 25, 140, 15) withFont:[UIFont systemFontOfSize:12.0]];
+ CGSize overallSummarySize = [overallSummary sizeWithFont:[UIFont systemFontOfSize:12.0]];
+ [overallSummary drawInRect:CGRectMake(290-overallSummarySize.width, 55-overallSummarySize.height, overallSummarySize.width, overallSummarySize.height) withFont:[UIFont italicSystemFontOfSize:12.0]];
}
@end
@@ -19,6 +19,7 @@
- (App*) appWithID:(NSString*)appID;
- (void) addApp:(App*)app;
- (BOOL) createOrUpdateAppIfNeededWithID:(NSString*)appID name:(NSString*)appName;
+- (void) removeAppWithID:(NSString*)appID;
- (void) saveToDisk;
@end
@@ -54,6 +54,10 @@ - (void) addApp:(App*)app {
[appsByID setObject:app forKey:app.appID];
}
+- (void) removeAppWithID:(NSString*)appID {
+ [appsByID removeObjectForKey:appID];
+}
+
- (BOOL) createOrUpdateAppIfNeededWithID:(NSString*)appID name:(NSString*)appName {
App *app = [self appWithID:appID];
if (app == nil) {
@@ -8,6 +8,9 @@
#endif
+#define ASSERT_IS_MAIN_THREAD() NSAssert([NSThread isMainThread], @"must call from main thread");
+#define ASSERT_NOT_MAIN_THREAD() NSAssert([NSThread isMainThread] == false, @"do not call from main thread");
+
__attribute__((constructor)) // run this function run when the app loads
static void InitRandom() {
srandom(time(NULL));
@@ -0,0 +1,34 @@
+//
+// AppleFiscalCalendar.h
+// AppSalesMobile
+//
+// Created by Tim Shadel on 4/5/11.
+// Copyright 2011 Shadel Software, Inc. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+typedef enum {
+ DayCalendarTypeCalendar,
+ DayCalendarTypeAppleFiscal
+} DayCalendarType;
+
+
+@interface AppleFiscalCalendar : NSObject {
+ @private
+ NSArray *sortedFiscalMonthNames;
+ NSArray *sortedDateStrings;
+ NSArray *sortedDates;
+}
+
+/**
+ * Returns the full month name and year of the fiscal month in which the given date falls.
+ */
+- (NSString *)fiscalMonthForDate:(NSDate *)date;
+
+/**
+ * Returns the shared instance for use anywhere fiscal date information is needed.
+ */
++ (AppleFiscalCalendar *)sharedFiscalCalendar;
+
+@end
Oops, something went wrong.

0 comments on commit 98d6490

Please sign in to comment.