add the appirater class for getting more positive reviews
authorkoda
Sat, 11 Dec 2010 01:55:16 +0100
changeset 4504 8906b2409d97
parent 4503 a8ab151bcae3
child 4505 4a8f87eb67cb
add the appirater class for getting more positive reviews enabled full multitasking for iphone (but only with ios >= 4.2) fixed a glitch that would leave stuff onscreen while ammo menu open
project_files/HedgewarsMobile/Classes/Appirater.h
project_files/HedgewarsMobile/Classes/Appirater.m
project_files/HedgewarsMobile/Classes/GameConfigViewController.m
project_files/HedgewarsMobile/Classes/ObjcExports.h
project_files/HedgewarsMobile/Classes/ObjcExports.m
project_files/HedgewarsMobile/Classes/OverlayViewController.m
project_files/HedgewarsMobile/Classes/SDL_uikitappdelegate.m
project_files/HedgewarsMobile/Classes/TeamConfigViewController.m
project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/Appirater.h	Sat Dec 11 01:55:16 2010 +0100
@@ -0,0 +1,105 @@
+/*
+ This file is part of Appirater.
+ 
+ Copyright (c) 2010, Arash Payan
+ All rights reserved.
+ 
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+ 
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Appirater.h
+ * appirater
+ *
+ * Created by Arash Payan on 9/5/09.
+ * http://arashpayan.com
+ * Copyright 2010 Arash Payan. All rights reserved.
+ */
+
+#import <Foundation/Foundation.h>
+
+extern NSString *const kAppiraterLaunchDate;
+extern NSString *const kAppiraterLaunchCount;
+extern NSString *const kAppiraterCurrentVersion;
+extern NSString *const kAppiraterRatedCurrentVersion;
+extern NSString *const kAppiraterDeclinedToRate;
+
+/*
+ Place your Apple generated software id here.
+ */
+#define APPIRATER_APP_ID                 391234866
+
+/*
+ Your app's name.
+ */
+#define APPIRATER_APP_NAME              [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleNameKey]
+
+/*
+ This is the message your users will see once they've passed the day+launches
+ threshold.
+ */
+#define APPIRATER_MESSAGE               [NSString stringWithFormat:@"If you enjoy using %@, would you mind taking a moment to rate it? It won't take more than a minute. Thanks for your support!", APPIRATER_APP_NAME]
+
+/*
+ This is the title of the message alert that users will see.
+ */
+#define APPIRATER_MESSAGE_TITLE         [NSString stringWithFormat:@"Rate %@", APPIRATER_APP_NAME]
+
+/*
+ The text of the button that rejects reviewing the app.
+ */
+#define APPIRATER_CANCEL_BUTTON         NSLocalizedString(@"No thanks",@"")
+
+/*
+ Text of button that will send user to app review page.
+ */
+#define APPIRATER_RATE_BUTTON           [NSString stringWithFormat:@"Rate %@", APPIRATER_APP_NAME]
+
+/*
+ Text for button to remind the user to review later.
+ */
+#define APPIRATER_RATE_LATER            NSLocalizedString(@"Remind me later",@"")
+
+/*
+ Users will need to have the same version of your app installed for this many
+ days before they will be prompted to rate it.
+ */
+#define DAYS_UNTIL_PROMPT               5       // double
+
+/*
+ Users will need to launch the same version of the app this many times before
+ they will be prompted to rate it.
+ */
+#define LAUNCHES_UNTIL_PROMPT           10      // integer
+
+/*
+ 'YES' will show the Appirater alert everytime. Useful for testing how your message
+ looks and making sure the link to your app's review page works.
+ */
+#define APPIRATER_DEBUG                 NO      // bool
+
+@interface Appirater : NSObject <UIAlertViewDelegate> {
+
+}
+
++(void) appLaunched;
+
+@end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/Appirater.m	Sat Dec 11 01:55:16 2010 +0100
@@ -0,0 +1,207 @@
+/*
+ This file is part of Appirater.
+ 
+ Copyright (c) 2010, Arash Payan
+ All rights reserved.
+ 
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation
+ files (the "Software"), to deal in the Software without
+ restriction, including without limitation the rights to use,
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following
+ conditions:
+ 
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+ 
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ OTHER DEALINGS IN THE SOFTWARE.
+ */
+/*
+ * Appirater.m
+ * appirater
+ *
+ * Created by Arash Payan on 9/5/09.
+ * http://arashpayan.com
+ * Copyright 2010 Arash Payan. All rights reserved.
+ */
+
+#import "Appirater.h"
+#import <SystemConfiguration/SCNetworkReachability.h>
+#import <netinet/in.h>
+
+NSString *const kAppiraterLaunchDate            = @"kAppiraterLaunchDate";
+NSString *const kAppiraterLaunchCount           = @"kAppiraterLaunchCount";
+NSString *const kAppiraterCurrentVersion        = @"kAppiraterCurrentVersion";
+NSString *const kAppiraterRatedCurrentVersion   = @"kAppiraterRatedCurrentVersion";
+NSString *const kAppiraterDeclinedToRate        = @"kAppiraterDeclinedToRate";
+
+NSString *templateReviewURL = @"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
+
+@interface Appirater (hidden)
+
+-(BOOL) connectedToNetwork;
+
+@end
+
+@implementation Appirater (hidden)
+
+-(BOOL) connectedToNetwork {
+    // Create zero addy
+    struct sockaddr_in zeroAddress;
+    bzero(&zeroAddress, sizeof(zeroAddress));
+    zeroAddress.sin_len = sizeof(zeroAddress);
+    zeroAddress.sin_family = AF_INET;
+    
+    // Recover reachability flags
+    SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
+    SCNetworkReachabilityFlags flags;
+    
+    BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
+    CFRelease(defaultRouteReachability);
+    
+    if (!didRetrieveFlags) {
+        NSLog(@"Error. Could not recover network reachability flags");
+        return NO;
+    }
+    
+    BOOL isReachable = flags & kSCNetworkFlagsReachable;
+    BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
+    BOOL nonWiFi = flags & kSCNetworkReachabilityFlagsTransientConnection;
+    
+    NSURL *testURL = [NSURL URLWithString:@"http://www.apple.com/"];
+    NSURLRequest *testRequest = [NSURLRequest requestWithURL:testURL  cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20.0];
+    NSURLConnection *testConnection = [[NSURLConnection alloc] initWithRequest:testRequest delegate:self];
+    
+    return ((isReachable && !needsConnection) || nonWiFi) ? (testConnection ? YES : NO) : NO;
+}
+
+@end
+
+
+@implementation Appirater
+
++(void) appLaunched {
+    Appirater *appirater = [[Appirater alloc] init];
+    [NSThread detachNewThreadSelector:@selector(appLaunchedHandler) toTarget:appirater withObject:nil];
+}
+
+-(void) appLaunchedHandler {
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    if (APPIRATER_DEBUG) {
+        [self performSelectorOnMainThread:@selector(showPrompt) withObject:nil waitUntilDone:NO];
+        return;
+    }
+
+    BOOL willShowPrompt = NO;
+
+    // get the app's version
+    NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:(NSString*)kCFBundleVersionKey];
+
+    // get the version number that we've been tracking
+    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+    NSString *trackingVersion = [userDefaults stringForKey:kAppiraterCurrentVersion];
+    if (trackingVersion == nil) {
+        trackingVersion = version;
+        [userDefaults setObject:version forKey:kAppiraterCurrentVersion];
+    }
+    
+    if (APPIRATER_DEBUG)
+        DLog(@"APPIRATER Tracking version: %@", trackingVersion);
+
+    if ([trackingVersion isEqualToString:version]) {
+        // get the launch date
+        NSTimeInterval timeInterval = [userDefaults doubleForKey:kAppiraterLaunchDate];
+        if (timeInterval == 0) {
+            timeInterval = [[NSDate date] timeIntervalSince1970];
+            [userDefaults setDouble:timeInterval forKey:kAppiraterLaunchDate];
+        }
+
+        NSTimeInterval secondsSinceLaunch = [[NSDate date] timeIntervalSinceDate:[NSDate dateWithTimeIntervalSince1970:timeInterval]];
+        double secondsUntilPrompt = 60 * 60 * 24 * DAYS_UNTIL_PROMPT;
+
+        // get the launch count
+        int launchCount = [userDefaults integerForKey:kAppiraterLaunchCount];
+        launchCount++;
+        [userDefaults setInteger:launchCount forKey:kAppiraterLaunchCount];
+        if (APPIRATER_DEBUG)
+            NSLog(@"APPIRATER Launch count: %d", launchCount);
+
+        // have they previously declined to rate this version of the app?
+        BOOL declinedToRate = [userDefaults boolForKey:kAppiraterDeclinedToRate];
+
+        // have they already rated the app?
+        BOOL ratedApp = [userDefaults boolForKey:kAppiraterRatedCurrentVersion];
+
+        if (secondsSinceLaunch > secondsUntilPrompt &&
+             launchCount > LAUNCHES_UNTIL_PROMPT &&
+             !declinedToRate &&
+             !ratedApp) {
+            if ([self connectedToNetwork]) {	// check if they can reach the app store
+                willShowPrompt = YES;
+                [self performSelectorOnMainThread:@selector(showPrompt) withObject:nil waitUntilDone:NO];
+            }
+        }
+    } else {
+        // it's a new version of the app, so restart tracking
+        [userDefaults setObject:version forKey:kAppiraterCurrentVersion];
+        [userDefaults setDouble:[[NSDate date] timeIntervalSince1970] forKey:kAppiraterLaunchDate];
+        [userDefaults setInteger:1 forKey:kAppiraterLaunchCount];
+        [userDefaults setBool:NO forKey:kAppiraterRatedCurrentVersion];
+        [userDefaults setBool:NO forKey:kAppiraterDeclinedToRate];
+    }
+
+    [userDefaults synchronize];
+    if (!willShowPrompt)
+        [self autorelease];
+
+    [pool release];
+}
+
+-(void) showPrompt {
+    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:APPIRATER_MESSAGE_TITLE
+                                                        message:APPIRATER_MESSAGE
+                                                       delegate:self
+                                              cancelButtonTitle:APPIRATER_CANCEL_BUTTON
+                                              otherButtonTitles:APPIRATER_RATE_BUTTON, APPIRATER_RATE_LATER, nil];
+    [alertView show];
+    [alertView release];
+}
+
+-(void) alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger) buttonIndex {
+    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
+
+    switch (buttonIndex) {
+        case 0:
+            // they don't want to rate it
+            [userDefaults setBool:YES forKey:kAppiraterDeclinedToRate];
+            break;
+        case 1:
+            // they want to rate it
+            [[UIApplication sharedApplication] openURL:
+             [NSURL URLWithString:[templateReviewURL stringByReplacingOccurrencesOfString:@"APP_ID" withString:[NSString stringWithFormat:@"%d", APPIRATER_APP_ID]]]];
+            
+            [userDefaults setBool:YES forKey:kAppiraterRatedCurrentVersion];
+            break;
+        case 2:
+            // remind them later
+            break;
+        default:
+            break;
+    }
+
+    [userDefaults synchronize];
+
+    [self release];
+}
+
+@end
--- a/project_files/HedgewarsMobile/Classes/GameConfigViewController.m	Sat Dec 11 01:00:00 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/GameConfigViewController.m	Sat Dec 11 01:55:16 2010 +0100
@@ -226,9 +226,6 @@
                                     [NSNumber numberWithInt:self.interfaceOrientation],@"orientation",
                                     nil];
 
-    // finally launch game and remove this controller
-    DLog(@"sending config %@", gameDictionary);
-
     NSDictionary *allDataNecessary = [NSDictionary dictionaryWithObjectsAndKeys:gameDictionary,@"game_dictionary", @"",@"savefile",
                                       [NSNumber numberWithBool:NO],@"netgame", nil];
     if (IS_IPAD())
--- a/project_files/HedgewarsMobile/Classes/ObjcExports.h	Sat Dec 11 01:00:00 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/ObjcExports.h	Sat Dec 11 01:55:16 2010 +0100
@@ -31,5 +31,6 @@
 BOOL isGameRunning();
 void setGameRunning(BOOL value);
 NSInteger cachedGrenadeTime();
+void clearView();
 void setGrenadeTime(NSInteger value);
 void setAmmoMenuInstance(AmmoMenuViewController *instance);
--- a/project_files/HedgewarsMobile/Classes/ObjcExports.m	Sat Dec 11 01:00:00 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/ObjcExports.m	Sat Dec 11 01:55:16 2010 +0100
@@ -96,6 +96,7 @@
 }
 
 void clearView() {
+    // don't use any engine calls here as this function is called every time the ammomenu is opened
     UIWindow *theWindow = (IS_DUALHEAD()) ? [SDLUIKitDelegate sharedAppDelegate].uiwindow : [[UIApplication sharedApplication] keyWindow];
     UIButton *theButton = (UIButton *)[theWindow viewWithTag:CONFIRMATION_TAG];
     UISegmentedControl *theSegment = (UISegmentedControl *)[theWindow viewWithTag:GRENADE_TAG];
@@ -166,6 +167,7 @@
             gAudioSessionInited = YES;
     }
     UInt32 propertySize = sizeof(CFStringRef);
+    BOOL muteResult = NO;
 
     // this checks if there is volume
     Float32 volume;
@@ -179,9 +181,10 @@
     n = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);
     if (n != 0)
         DLog( @"AudioSessionGetProperty 'audioRoute': %d", n );
-    NSString *result = (NSString *)state;
-    BOOL muteResult = ([result length] == 0);
-    releaseAndNil(result);
-    
+    else {
+        NSString *result = (NSString *)state;
+        muteResult = ([result length] == 0);
+        releaseAndNil(result);
+    }
     return volumeResult || muteResult;
 }
--- a/project_files/HedgewarsMobile/Classes/OverlayViewController.m	Sat Dec 11 01:00:00 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/OverlayViewController.m	Sat Dec 11 01:55:16 2010 +0100
@@ -395,6 +395,7 @@
             break;
         case 10:
             playSound(@"clickSound");
+            clearView();
             HW_pause();
             if (self.amvc.isVisible && IS_DUALHEAD() == NO) {
                 doDim();
@@ -405,6 +406,7 @@
             break;
         case 11:
             playSound(@"clickSound");
+            clearView();
             removeConfirmationInput();
             
             if (IS_DUALHEAD() || self.useClassicMenu == NO) {
--- a/project_files/HedgewarsMobile/Classes/SDL_uikitappdelegate.m	Sat Dec 11 01:00:00 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/SDL_uikitappdelegate.m	Sat Dec 11 01:55:16 2010 +0100
@@ -24,7 +24,6 @@
 #import "SDL_uikitopenglview.h"
 #import "SDL_uikitwindow.h"
 #import "SDL_events_c.h"
-#import "../SDL_sysvideo.h"
 #import "jumphack.h"
 #import "SDL_video.h"
 #import "SDL_mixer.h"
@@ -34,6 +33,8 @@
 #import "GameSetup.h"
 #import "MainMenuViewController.h"
 #import "OverlayViewController.h"
+#import "Appirater.h"
+#include <unistd.h>
 
 #ifdef main
 #undef main
@@ -41,16 +42,15 @@
 
 #define BLACKVIEW_TAG 17935
 #define SECONDBLACKVIEW_TAG 48620
-#define VALGRIND "/opt/valgrind/bin/valgrind"
+#define VALGRIND "/opt/fink/bin/valgrind"
 
 int main (int argc, char *argv[]) {
 #ifdef VALGRIND_REXEC
     // Using the valgrind build config, rexec ourself in valgrind
     // from http://landonf.bikemonkey.org/code/iphone/iPhone_Simulator_Valgrind.20081224.html
     if (argc < 2 || (argc >= 2 && strcmp(argv[1], "-valgrind") != 0))
-        execl(VALGRIND, VALGRIND, "--leak-check=full", argv[0], "-valgrind", NULL);
+        execl(VALGRIND, VALGRIND, "--leak-check=full", "--dsymutil=yes", argv[0], "-valgrind", NULL);
 #endif
-
     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
     int retVal = UIApplicationMain(argc, argv, nil, @"SDLUIKitDelegate");
     [pool release];
@@ -173,7 +173,7 @@
 }
 
 // override the direct execution of SDL_main to allow us to implement the frontend (or even using a nib)
--(void) applicationDidFinishLaunching:(UIApplication *)application {
+-(BOOL) application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     [application setStatusBarHidden:YES];
 
     self.uiwindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
@@ -204,6 +204,9 @@
         [titleView release];
         [self.secondWindow makeKeyAndVisible];
     }
+
+    [Appirater appLaunched];
+    return YES;
 }
 
 -(void) applicationWillTerminate:(UIApplication *)application {
@@ -227,15 +230,15 @@
     if ([device respondsToSelector:@selector(isMultitaskingSupported)] &&
          device.multitaskingSupported &&
          self.isInGame) {
-        // there is a bug on iphone that presents a sdl view with a black screen, so it returns to frontend
-        if (IS_IPAD())
+        // multiasking in-game works only for ios >= 4.2, returns a black screen on other verions
+        if (NSClassFromString(@"UIPrintInfo"))
             HW_suspend();
         else {
-            // while screen is loading you can't call HW_terminate() so we close the app
+            // so the game returns to the configuration view
             if (isGameRunning())
                 HW_terminate(NO);
             else {
-                // force app closure
+                // while screen is loading you can't call HW_terminate() so we close the app
                 SDL_SendQuit();
                 HW_terminate(YES);
                 longjmp(*(jump_env()), 1);
--- a/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m	Sat Dec 11 01:00:00 2010 +0100
+++ b/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m	Sat Dec 11 01:55:16 2010 +0100
@@ -236,7 +236,7 @@
     label.textColor = [UIColor whiteColor];
     label.numberOfLines = 1;
     if (section == 0)
-        label.text = NSLocalizedString(@"Tap to add hogs or change color, hold tap to remove team.",@"");
+        label.text = NSLocalizedString(@"Tap to add hogs or change color, touch and hold to remove team.",@"");
     else
         label.text = NSLocalizedString(@"The robot badge indicates an AI-controlled team.",@"");
 
--- a/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj	Sat Dec 11 01:00:00 2010 +0100
+++ b/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj	Sat Dec 11 01:55:16 2010 +0100
@@ -182,6 +182,7 @@
 		61A670C012747D9B00B06CE7 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 6183D83D11E2BCE200A88903 /* Default.png */; };
 		61A670C112747DB900B06CE7 /* MainMenuViewController-iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6165924C11CA9CB400D6E256 /* MainMenuViewController-iPhone.xib */; };
 		61A670C212747DBD00B06CE7 /* MapConfigViewController-iPhone.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6165924E11CA9CB400D6E256 /* MapConfigViewController-iPhone.xib */; };
+		61AC067412B2E32D000B52A2 /* Appirater.m in Sources */ = {isa = PBXBuildFile; fileRef = 61AC067312B2E32D000B52A2 /* Appirater.m */; };
 		61B3D71C11EA6F2700EC7420 /* uKeys.pas in Sources */ = {isa = PBXBuildFile; fileRef = 617987FE114AA34C00BA94A9 /* uKeys.pas */; };
 		61C079E411F35A300072BF46 /* EditableCellView.m in Sources */ = {isa = PBXBuildFile; fileRef = 61C079E311F35A300072BF46 /* EditableCellView.m */; };
 		61D205A1127CDD1100ABD83E /* ObjcExports.m in Sources */ = {isa = PBXBuildFile; fileRef = 61D205A0127CDD1100ABD83E /* ObjcExports.m */; };
@@ -958,6 +959,8 @@
 		61A4A39212A5CCC2004D81E6 /* uUtils.pas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = uUtils.pas; path = ../../hedgewars/uUtils.pas; sourceTree = SOURCE_ROOT; };
 		61A4A39312A5CCC2004D81E6 /* uVariables.pas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = uVariables.pas; path = ../../hedgewars/uVariables.pas; sourceTree = SOURCE_ROOT; };
 		61A4A3A112A5CD56004D81E6 /* uCaptions.pas */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.pascal; name = uCaptions.pas; path = ../../hedgewars/uCaptions.pas; sourceTree = SOURCE_ROOT; };
+		61AC067212B2E32D000B52A2 /* Appirater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Appirater.h; path = Classes/Appirater.h; sourceTree = "<group>"; };
+		61AC067312B2E32D000B52A2 /* Appirater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Appirater.m; path = Classes/Appirater.m; sourceTree = "<group>"; };
 		61C079E211F35A300072BF46 /* EditableCellView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EditableCellView.h; path = Classes/EditableCellView.h; sourceTree = "<group>"; };
 		61C079E311F35A300072BF46 /* EditableCellView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EditableCellView.m; path = Classes/EditableCellView.m; sourceTree = "<group>"; };
 		61D2059F127CDD1100ABD83E /* ObjcExports.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ObjcExports.h; path = Classes/ObjcExports.h; sourceTree = "<group>"; };
@@ -1076,6 +1079,8 @@
 			children = (
 				6165929C11CA9E2F00D6E256 /* SDL_uikitappdelegate.h */,
 				6165929D11CA9E2F00D6E256 /* SDL_uikitappdelegate.m */,
+				61AC067212B2E32D000B52A2 /* Appirater.h */,
+				61AC067312B2E32D000B52A2 /* Appirater.m */,
 				61DE91561258B76800B80214 /* Custom UIs */,
 				32CA4F630368D1EE00C91783 /* Hedgewars_Prefix.pch */,
 				6165922911CA9BD500D6E256 /* PascalImports.h */,
@@ -2412,6 +2417,7 @@
 				61A4A3A212A5CD56004D81E6 /* uCaptions.pas in Sources */,
 				61E5D68D12AB006F00566F29 /* uLandPainted.pas in Sources */,
 				61F544C712AF1748007FD913 /* HoldTableViewCell.m in Sources */,
+				61AC067412B2E32D000B52A2 /* Appirater.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};