-
Notifications
You must be signed in to change notification settings - Fork 0
/
WBWeatherInfoManager.m
176 lines (155 loc) · 6.43 KB
/
WBWeatherInfoManager.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//
// WBWeatherInfoManager.m
// WeatherBoard
//
// Created by Allan Kerr on 2015-07-06.
//
//
#import "WBWeatherInfoManager.h"
#import "WBWeatherInfoManagerInterface.h"
#import "WBInfoWorkerInterface.h"
#import "NSXPCConnection.h"
#import <CoreLocation/CoreLocation.h>
@interface WBWeatherInfoManager ()
@property (readwrite, nonatomic) BOOL isMonitoringCurrentLocationWeatherChanges;
@property (nonatomic, retain) NSMutableArray *monitoredLocations;
@property (nonatomic, retain) id remoteObject;
@property (nonatomic, retain) NSXPCConnection *connection;
@end
@implementation WBWeatherInfoManager
- (id)initWithDelegate:(id <WBWeatherInfoManagerDelegate>)delegate
{
if (self = [super init]) {
self.monitoredLocations = [NSMutableArray array];
self.delegate = delegate;
// Defines the interface between WBWeatherInfoManager and weatherinfod
NSSet *classes = [NSSet setWithObjects:[CLLocation class], nil];
NSXPCInterface *serverInterface = [NSXPCInterface interfaceWithProtocol:@protocol(WBInfoWorkerInterface)];
NSXPCInterface *clientInterface = [NSXPCInterface interfaceWithProtocol:@protocol(WBWeatherInfoManagerInterface)];
[serverInterface setClasses:classes forSelector:@selector(startMonitoringWeatherChangesForLocation:) argumentIndex:0 ofReply:NO];
[serverInterface setClasses:classes forSelector:@selector(stopMonitoringWeatherChangesForLocation:) argumentIndex:0 ofReply:NO];
// Creates the xpc connection the weatherinfod
self.connection = [[[NSXPCConnection alloc] initWithMachServiceName:@"com.theronen.weatherinfod" options:NSXPCConnectionPrivileged] autorelease];
[self.connection setRemoteObjectInterface:serverInterface];
[self.connection setExportedInterface:clientInterface];
[self.connection resume];
}
return self;
}
- (void)_startConnectionIfNeeded
{
// Only start the connection if there isn't a valid remoteObject
if (self.remoteObject == nil) {
self.connection.interruptionHandler = ^(){
// If the daemon crashes re-establish the connection
[self _reestablishConnection];
};
self.remoteObject = [self.connection remoteObjectProxyWithErrorHandler:^(NSError *error) {
if ([self.delegate respondsToSelector:@selector(weatherInfoManager: didFailWithError:)]) {
[self.delegate weatherInfoManager:self didFailWithError:error];
}
}];
[self.connection setExportedObject:self];
}
}
- (void)_stopConnectionIfNeeded
{
// Stop the connection if no locations are being monitored
if (self.isMonitoringCurrentLocationWeatherChanges == NO && self.monitoredLocations.count == 0) {
[self.connection setExportedObject:nil];
self.connection.interruptionHandler = nil;
self.remoteObject = nil;
}
}
- (void)_reestablishConnection
{
// Start updates for all monitored locations and the current location if necessary
NSMutableArray *monitoredLocationsCopy = [self.monitoredLocations mutableCopy];
BOOL isMonitoringCurrentLocationWeatherChangesCopy = self.isMonitoringCurrentLocationWeatherChanges;
self.isMonitoringCurrentLocationWeatherChanges = NO;
[self.monitoredLocations removeAllObjects];
if (isMonitoringCurrentLocationWeatherChangesCopy) {
[self startMonitoringCurrentLocationWeatherChanges];
}
for (CLLocation *location in monitoredLocationsCopy) {
[self startMonitoringWeatherChangesForLocation:location];
}
}
- (NSUInteger)_indexForLocation:(CLLocation *)location
{
// Locations within 3 km of eachother are treated as the same location
return [self.monitoredLocations indexOfObjectPassingTest:^BOOL(CLLocation *otherLocation, NSUInteger index, BOOL *stop) {
return [location distanceFromLocation:otherLocation] < 3000.0f;
}];
}
- (BOOL)isMonitoringWeatherChangesForLocation:(CLLocation *)location
{
return [self _indexForLocation:location] != NSNotFound;
}
- (void)startMonitoringCurrentLocationWeatherChanges
{
if (self.isMonitoringCurrentLocationWeatherChanges == NO) {
[self _startConnectionIfNeeded];
// Send -startMonitoringCurrentLocationWeatherChanges to weatherinfod
[self.remoteObject startMonitoringCurrentLocationWeatherChanges];
self.isMonitoringCurrentLocationWeatherChanges = YES;
}
}
- (void)startMonitoringWeatherChangesForLocation:(CLLocation *)location
{
// Prevent duplication registering of a location
NSUInteger index = [self _indexForLocation:location];
if (index == NSNotFound) {
[self _startConnectionIfNeeded];
// Send -startMonitoringWeatherChangesForLocation: to weatherinfod
[self.remoteObject startMonitoringWeatherChangesForLocation:location];
[self.monitoredLocations addObject:location];
}
}
- (void)stopMonitoringCurrentLocationWeatherChanges
{
if (self.isMonitoringCurrentLocationWeatherChanges == YES) {
[self.remoteObject stopMonitoringCurrentLocationWeatherChanges];
self.isMonitoringCurrentLocationWeatherChanges = NO;
[self _stopConnectionIfNeeded];
}
}
- (void)stopMonitoringWeatherChangesForLocation:(CLLocation *)location
{
// Only stop updating if updates are being recieved for the location
NSUInteger index = [self _indexForLocation:location];
if (index != NSNotFound) {
[self.remoteObject stopMonitoringWeatherChangesForLocation:location];
[self.monitoredLocations removeObject:location];
[self _stopConnectionIfNeeded];
}
}
- (void)didUpdateWeather:(WBCity *)city
{
if ([self.delegate respondsToSelector:@selector(weatherInfoManager: didUpdateWeather:)]) {
//Call -weatherInfoManager:didUpdateWeather: on the main queue
dispatch_queue_t mainQueue = dispatch_get_main_queue();
if (mainQueue == dispatch_get_current_queue()) {
[self.delegate weatherInfoManager:self didUpdateWeather:city];
} else {
dispatch_sync(mainQueue, ^{
[self.delegate weatherInfoManager:self didUpdateWeather:city];
});
}
}
}
- (void)didFailWithError:(NSError *)error
{
if ([self.delegate respondsToSelector:@selector(weatherInfoManager: didFailWithError:)]) {
[self.delegate weatherInfoManager:self didFailWithError:error];
}
}
- (void)dealloc
{
[_connection invalidate];
[_connection release];
[_monitoredLocations release];
[_remoteObject release];
[super dealloc];
}
@end