Fix compile errors
[pithos-ios] / Classes / hoptoadnotifier / HTNotifier.m
1 //
2 //  HTNotifier.m
3 //  HoptoadNotifier
4 //
5 //  Created by Caleb Davenport on 10/2/10.
6 //  Copyright 2010 GUI Cocoa, LLC. All rights reserved.
7 //
8
9 #import "HTNotifier.h"
10 #import "HTNotifier_iOS.h"
11 #import "HTNotifier_Mac.h"
12 #import "HTNotice.h"
13 #import "HTFunctions.h"
14
15 // internal
16 void ht_handle_exception(NSException *);
17 static HTNotifier *sharedNotifier = nil;
18 static NSString *HTNotifierHostName = @"hoptoadapp.com";
19 #define HTNotifierURL [NSURL URLWithString: \
20         [NSString stringWithFormat: \
21         @"%@://%@/notifier_api/v2/notices", \
22         (self.useSSL) ? @"https" : @"http", \
23         HTNotifierHostName]]
24 #define HTIsMultitaskingSupported \
25         [[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)] && \
26         [[UIDevice currentDevice] isMultitaskingSupported]
27 #define HT_IOS_SDK_4 (TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MAX_ALLOWED >= 4000)
28
29 // extern strings
30 NSString *HTNotifierVersion = @"2.1";
31 NSString *HTNotifierBundleName = @"${BUNDLE}";
32 NSString *HTNotifierBundleVersion  = @"${VERSION}";
33 NSString *HTNotifierDevelopmentEnvironment = @"Development";
34 NSString *HTNotifierAdHocEnvironment = @"Ad Hoc";
35 NSString *HTNotifierAppStoreEnvironment = @"App Store";
36 NSString *HTNotifierReleaseEnvironment = @"Release";
37 NSString *HTNotifierAlwaysSendKey = @"AlwaysSendCrashReports";
38
39 #pragma mark -
40 #pragma mark private methods
41 @interface HTNotifier (private)
42
43 // methods to be implemented
44 - (id)initWithAPIKey:(NSString *)key environmentName:(NSString *)name;
45 - (void)checkForNoticesAndReportIfReachable;
46 - (void)postAllNoticesWithAutoreleasePool;
47 - (void)postNoticesWithPaths:(NSArray *)paths;
48 - (void)postNoticeWithPath:(NSString *)path;
49 - (BOOL)isHoptoadReachable;
50
51 // methods to be overridden
52 - (void)showNoticeAlert;
53 - (void)registerNotifications;
54 - (void)unregisterNotifications;
55
56 @end
57 @implementation HTNotifier (private)
58
59 - (id)initWithAPIKey:(NSString *)key environmentName:(NSString *)name {
60         self = [super init];
61         if (self) {
62                 
63                 // create folder
64                 NSString *directory = HTNoticesDirectory();
65                 if (![[NSFileManager defaultManager] fileExistsAtPath:directory]) {
66                         [[NSFileManager defaultManager]
67                          createDirectoryAtPath:directory
68                          withIntermediateDirectories:YES
69                          attributes:nil
70                          error:nil];
71                 }
72                 
73                 // setup values
74                 apiKey = [key copy];
75                 environmentName = [HTStringByReplacingHoptoadVariablesInString(name) retain];
76                 environmentInfo = [[NSMutableDictionary alloc] init];
77                 self.useSSL = NO;
78         HTInitNoticeInfo();
79                 
80                 // register defaults
81                 [[NSUserDefaults standardUserDefaults] registerDefaults:
82                  [NSDictionary dictionaryWithObject:@"NO" forKey:HTNotifierAlwaysSendKey]];
83                 
84                 // setup reachability
85                 reachability = SCNetworkReachabilityCreateWithName(NULL, [HTNotifierHostName UTF8String]);
86                 
87                 // notifications
88                 [self registerNotifications];
89         
90         // start
91                 HTStartHandlers();
92                 
93         }
94         return self;
95 }
96 - (void)checkForNoticesAndReportIfReachable {
97         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
98         
99         if ([self isHoptoadReachable]) {
100                 [self performSelectorOnMainThread:@selector(unregisterNotifications) withObject:nil waitUntilDone:YES];
101                 
102                 NSArray *notices = HTNotices();
103                 if ([notices count] > 0) {
104                         if ([[NSUserDefaults standardUserDefaults] boolForKey:HTNotifierAlwaysSendKey]) {
105                                 [self postNoticesWithPaths:notices];
106                         }
107                         else {
108                                 [self performSelectorOnMainThread:@selector(showNoticeAlert) withObject:nil waitUntilDone:YES];
109                         }
110                 }
111         }
112         
113         [pool drain];
114 }
115 - (void)postAllNoticesWithAutoreleasePool {
116         NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
117         NSArray *paths = HTNotices();
118         [self postNoticesWithPaths:paths];
119         [pool drain];
120 }
121 - (void)postNoticesWithPaths:(NSArray *)paths {
122
123     // notify delegate
124     if ([paths count] > 0) {
125         if ([self.delegate respondsToSelector:@selector(notifierWillPostNotices)]) {
126             [self.delegate notifierWillPostNotices];
127         }
128     }
129     
130 #if HT_IOS_SDK_4
131         
132         if (HTIsMultitaskingSupported) {
133                 
134                 // start background task
135                 UIApplication *app = [UIApplication sharedApplication];
136                 __block BOOL keepPosting = YES;
137                 UIBackgroundTaskIdentifier task = [app beginBackgroundTaskWithExpirationHandler:^{
138                         keepPosting = NO;
139                 }];
140                 
141                 // report each notice
142                 for (NSString *path in paths) {
143                         if (!keepPosting) { break; }
144                         [self postNoticeWithPath:path];
145                 }
146                 
147                 // end background task
148                 if (task != UIBackgroundTaskInvalid) {
149                         [app endBackgroundTask:task];
150                 }
151                 
152         }
153         else {
154                 
155 #endif
156
157                 // report each notice
158                 for (NSString *path in paths) {
159                         [self postNoticeWithPath:path];
160                 }
161                 
162 #if HT_IOS_SDK_4
163                 
164         }
165         
166 #endif
167     
168     // notify delegate
169     if ([paths count] > 0) {
170         if ([self.delegate respondsToSelector:@selector(notifierDidPostNotices)]) {
171             [self.delegate notifierDidPostNotices];
172         }
173     }
174         
175 }
176 - (void)postNoticeWithPath:(NSString *)path {
177     
178         // get notice payload
179         HTNotice *notice = [HTNotice noticeWithContentsOfFile:path];
180 #ifdef DEBUG
181         HTLog(@"%@", notice);
182 #endif
183         NSData *data = [notice hoptoadXMLData];
184         
185         // create url request
186         NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:HTNotifierURL];
187         [request setTimeoutInterval:10.0];
188         [request setValue:@"text/xml" forHTTPHeaderField:@"Content-Type"];
189         [request setHTTPMethod:@"POST"];
190         [request setHTTPBody:data];
191         
192         // perform request
193         NSHTTPURLResponse *response = nil;
194         NSError *error = nil;
195         NSData *responseBody = [NSURLConnection
196                                                         sendSynchronousRequest:request
197                                                         returningResponse:&response
198                                                         error:&error];
199         
200         // error checking
201         if (error == nil) {
202                 [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
203         }
204         else {
205                 HTLog(@"encountered error while posting notice\n%@", error);
206         }
207         
208         // status code checking
209         NSInteger statusCode = [response statusCode];
210         if (statusCode == 200) {
211                 HTLog(@"crash report posted");
212         }
213         else if (responseBody == nil) {
214                 HTLog(@"unexpected response\nstatus code:%ld", (long)statusCode);
215         }
216         else {
217                 NSString *responseString = [[NSString alloc] initWithData:responseBody
218                                                                                                                  encoding:NSUTF8StringEncoding];
219                 HTLog(@"unexpected response\nstatus code:%ld\nresponse body:%@",
220                           (long)statusCode,
221                           responseString);
222                 [responseString release];
223         }
224     
225 }
226 - (BOOL)isHoptoadReachable {
227         SCNetworkReachabilityFlags flags;
228         SCNetworkReachabilityGetFlags(reachability, &flags);
229         return ((flags & kSCNetworkReachabilityFlagsReachable) != 0);
230 }
231
232 - (void)registerNotifications {}
233 - (void)unregisterNotifications {}
234 - (void)showNoticeAlert {}
235
236 @end
237
238 #pragma mark -
239 #pragma mark public implementation
240 @implementation HTNotifier
241
242 @synthesize apiKey;
243 @synthesize environmentName;
244 @synthesize useSSL;
245 @synthesize environmentInfo;
246 @synthesize delegate;
247
248 + (void)startNotifierWithAPIKey:(NSString *)key environmentName:(NSString *)name {
249         if (sharedNotifier == nil) {
250                 
251                 // validate
252                 if (key == nil || [key length] == 0) {
253                         HTLog(@"The provided API key is not valid");
254                         return;
255                 }
256                 if (name == nil || [name length] == 0) {
257                         HTLog(@"The provided environment name is not valid");
258                         return;
259                 }
260         
261         // create
262 #if TARGET_OS_IPHONE
263         sharedNotifier = [[HTNotifier_iOS alloc] initWithAPIKey:key environmentName:name];
264 #elif TARGET_OS_MAC
265         sharedNotifier = [[HTNotifier_Mac alloc] initWithAPIKey:key environmentName:name];
266 #else
267 #error [Hoptoad] unsupported platform
268 #endif
269                 
270                 // log
271         if (sharedNotifier != nil) {
272             HTLog(@"Notifier %@ ready to catch errors", HTNotifierVersion);
273             HTLog(@"Environment \"%@\"", sharedNotifier.environmentName);
274         }
275         }
276 }
277 + (HTNotifier *)sharedNotifier {
278         @synchronized(self) {
279                 return sharedNotifier;
280         }
281 }
282 + (id)allocWithZone:(NSZone *)zone {
283         @synchronized(self) {
284                 if (sharedNotifier == nil) {
285                         sharedNotifier = [super allocWithZone:zone];
286                         return sharedNotifier;
287                 }
288         }
289         return nil;
290 }
291 - (id)copyWithZone:(NSZone *)zone {
292         return self;
293 }
294 - (id)retain {
295         return self;
296 }
297 - (NSUInteger)retainCount {
298         return NSUIntegerMax;
299 }
300 - (oneway void)release {
301         // do nothing
302 }
303 - (id)autorelease {
304         return self;
305 }
306 - (void)dealloc {
307         [self unregisterNotifications];
308         if (reachability != NULL) { CFRelease(reachability);reachability = NULL; }
309         [apiKey release];apiKey = nil;
310         [environmentName release];environmentName = nil;
311         [environmentInfo release];environmentInfo = nil;
312     HTStopHandlers();
313     HTReleaseNoticeInfo();
314         [super dealloc];
315 }
316 - (void)writeTestNotice {
317     NSString *testPath = [HTNoticesDirectory() stringByAppendingPathComponent:@"TEST"];
318     testPath = [testPath stringByAppendingPathExtension:HTNoticePathExtension];
319         if ([[NSFileManager defaultManager] fileExistsAtPath:testPath]) { return; }
320         @try { [NSException raise:@"HTTestException" format:@"This is a test exception"]; }
321         @catch (NSException * e) { ht_handle_exception(e); }
322         NSString *noticePath = [NSString stringWithUTF8String:ht_notice_info.notice_path];
323         [[NSFileManager defaultManager] moveItemAtPath:noticePath toPath:testPath error:nil];
324 }
325
326 @end