Skip to content
This repository was archived by the owner on Nov 15, 2024. It is now read-only.

Commit bb9b46d

Browse files
committed
Merge pull request #13 from pluralsight/multipleplatformsupport
Add support for tv, watch, and Mac
2 parents 79bea7f + d93abd3 commit bb9b46d

File tree

9 files changed

+161
-102
lines changed

9 files changed

+161
-102
lines changed

PSOperations.xcodeproj/project.pbxproj

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@
4040
1D9B43651B6A934A008F7F18 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9B43461B6A934A008F7F18 /* CKContainer+Operations.swift */; };
4141
1D9B43661B6A934A008F7F18 /* UIUserNotifications+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9B43471B6A934A008F7F18 /* UIUserNotifications+Operations.swift */; };
4242
FD39C29E1B8B5F090092008B /* PSOperationsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D9B436D1B6A942C008F7F18 /* PSOperationsTests.swift */; };
43-
FDB1BBBE1B8D67CB00FB9D7A /* NSLock+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB1BBBD1B8D67CB00FB9D7A /* NSLock+Operations.swift */; settings = {ASSET_TAGS = (); }; };
44-
FDB1E8F21BBC2B08006BC157 /* OperationConditionResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB1E8F11BBC2B08006BC157 /* OperationConditionResultTests.swift */; settings = {ASSET_TAGS = (); }; };
45-
FDC6C5291BBCDDC9008FB96E /* URLSessionTaskOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC6C5281BBCDDC9008FB96E /* URLSessionTaskOperationTests.swift */; settings = {ASSET_TAGS = (); }; };
43+
FDB1BBBE1B8D67CB00FB9D7A /* NSLock+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB1BBBD1B8D67CB00FB9D7A /* NSLock+Operations.swift */; };
44+
FDB1E8F21BBC2B08006BC157 /* OperationConditionResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB1E8F11BBC2B08006BC157 /* OperationConditionResultTests.swift */; };
45+
FDC6C5291BBCDDC9008FB96E /* URLSessionTaskOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDC6C5281BBCDDC9008FB96E /* URLSessionTaskOperationTests.swift */; };
4646
/* End PBXBuildFile section */
4747

4848
/* Begin PBXContainerItemProxy section */
@@ -448,7 +448,7 @@
448448
ONLY_ACTIVE_ARCH = YES;
449449
SDKROOT = iphoneos;
450450
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
451-
TARGETED_DEVICE_FAMILY = "1,2";
451+
TARGETED_DEVICE_FAMILY = "1,2,3,4";
452452
VERSIONING_SYSTEM = "apple-generic";
453453
VERSION_INFO_PREFIX = "";
454454
};
@@ -488,7 +488,7 @@
488488
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
489489
MTL_ENABLE_DEBUG_INFO = NO;
490490
SDKROOT = iphoneos;
491-
TARGETED_DEVICE_FAMILY = "1,2";
491+
TARGETED_DEVICE_FAMILY = "1,2,3,4";
492492
VALIDATE_PRODUCT = YES;
493493
VERSIONING_SYSTEM = "apple-generic";
494494
VERSION_INFO_PREFIX = "";
@@ -510,7 +510,9 @@
510510
PRODUCT_BUNDLE_IDENTIFIER = "com.pluralsight.$(PRODUCT_NAME:rfc1034identifier)";
511511
PRODUCT_NAME = "$(TARGET_NAME)";
512512
SKIP_INSTALL = YES;
513+
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos watchos watchsimulator macosx";
513514
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
515+
TARGETED_DEVICE_FAMILY = "1,2,3,4";
514516
};
515517
name = Debug;
516518
};
@@ -529,6 +531,8 @@
529531
PRODUCT_BUNDLE_IDENTIFIER = "com.pluralsight.$(PRODUCT_NAME:rfc1034identifier)";
530532
PRODUCT_NAME = "$(TARGET_NAME)";
531533
SKIP_INSTALL = YES;
534+
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos watchos watchsimulator macosx";
535+
TARGETED_DEVICE_FAMILY = "1,2,3,4";
532536
};
533537
name = Release;
534538
};
@@ -540,6 +544,7 @@
540544
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
541545
PRODUCT_BUNDLE_IDENTIFIER = "com.pluralsight.$(PRODUCT_NAME:rfc1034identifier)";
542546
PRODUCT_NAME = "$(TARGET_NAME)";
547+
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos macosx watchsimulator watchos";
543548
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
544549
};
545550
name = Debug;
@@ -552,6 +557,7 @@
552557
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
553558
PRODUCT_BUNDLE_IDENTIFIER = "com.pluralsight.$(PRODUCT_NAME:rfc1034identifier)";
554559
PRODUCT_NAME = "$(TARGET_NAME)";
560+
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvsimulator appletvos macosx watchsimulator watchos";
555561
};
556562
name = Release;
557563
};

PSOperations/CKContainer+Operations.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Abstract:
66
A convenient extension to CloudKit.CKContainer.
77
*/
88

9+
#if !os(watchOS)
10+
911
import CloudKit
1012

1113
extension CKContainer {
@@ -78,3 +80,5 @@ private func requestPermission(container: CKContainer, permission: CKApplication
7880
}
7981
}
8082
}
83+
84+
#endif

PSOperations/CalendarCondition.swift

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,76 +6,80 @@ Abstract:
66
This file shows an example of implementing the OperationCondition protocol.
77
*/
88

9-
import EventKit
10-
11-
/// A condition for verifying access to the user's calendar.
12-
public struct CalendarCondition: OperationCondition {
13-
14-
public static let name = "Calendar"
15-
static let entityTypeKey = "EKEntityType"
16-
public static let isMutuallyExclusive = false
9+
#if !os(tvOS)
1710

18-
let entityType: EKEntityType
19-
20-
init(entityType: EKEntityType) {
21-
self.entityType = entityType
22-
}
11+
import EventKit
2312

24-
public func dependencyForOperation(operation: Operation) -> NSOperation? {
25-
return CalendarPermissionOperation(entityType: entityType)
26-
}
27-
28-
public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) {
29-
switch EKEventStore.authorizationStatusForEntityType(entityType) {
13+
/// A condition for verifying access to the user's calendar.
14+
public struct CalendarCondition: OperationCondition {
15+
16+
public static let name = "Calendar"
17+
static let entityTypeKey = "EKEntityType"
18+
public static let isMutuallyExclusive = false
19+
20+
let entityType: EKEntityType
21+
22+
init(entityType: EKEntityType) {
23+
self.entityType = entityType
24+
}
25+
26+
public func dependencyForOperation(operation: Operation) -> NSOperation? {
27+
return CalendarPermissionOperation(entityType: entityType)
28+
}
29+
30+
public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) {
31+
switch EKEventStore.authorizationStatusForEntityType(entityType) {
3032
case .Authorized:
3133
completion(.Satisfied)
32-
34+
3335
default:
3436
// We are not authorized to access entities of this type.
3537
let error = NSError(code: .ConditionFailed, userInfo: [
3638
OperationConditionKey: self.dynamicType.name,
3739
self.dynamicType.entityTypeKey: entityType.rawValue
38-
])
40+
])
3941

4042
completion(.Failed(error))
43+
}
4144
}
4245
}
43-
}
44-
45-
/**
46+
47+
/**
4648
`EKEventStore` takes a while to initialize, so we should create
4749
one and then keep it around for future use, instead of creating
4850
a new one every time a `CalendarPermissionOperation` runs.
49-
*/
50-
private let SharedEventStore = EKEventStore()
51-
52-
/**
53-
A private `Operation` that will request access to the user's Calendar/Reminders,
54-
if it has not already been granted.
55-
*/
56-
class CalendarPermissionOperation: Operation {
57-
let entityType: EKEntityType
51+
*/
52+
private let SharedEventStore = EKEventStore()
5853

59-
init(entityType: EKEntityType) {
60-
self.entityType = entityType
61-
super.init()
62-
addCondition(AlertPresentation())
63-
}
64-
65-
override func execute() {
66-
let status = EKEventStore.authorizationStatusForEntityType(entityType)
54+
/**
55+
A private `Operation` that will request access to the user's Calendar/Reminders,
56+
if it has not already been granted.
57+
*/
58+
class CalendarPermissionOperation: Operation {
59+
let entityType: EKEntityType
60+
61+
init(entityType: EKEntityType) {
62+
self.entityType = entityType
63+
super.init()
64+
addCondition(AlertPresentation())
65+
}
6766

68-
switch status {
67+
override func execute() {
68+
let status = EKEventStore.authorizationStatusForEntityType(entityType)
69+
70+
switch status {
6971
case .NotDetermined:
7072
dispatch_async(dispatch_get_main_queue()) {
7173
SharedEventStore.requestAccessToEntityType(self.entityType) { granted, error in
7274
self.finish()
7375
}
7476
}
75-
77+
7678
default:
7779
finish()
80+
}
7881
}
82+
7983
}
8084

81-
}
85+
#endif

PSOperations/CloudCondition.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ Abstract:
66
This file shows an example of implementing the OperationCondition protocol.
77
*/
88

9+
#if !os(watchOS)
10+
911
import CloudKit
1012

1113
/// A condition describing that the operation requires access to a specific CloudKit container.
@@ -88,3 +90,5 @@ class CloudKitPermissionOperation: Operation {
8890
}
8991

9092
}
93+
94+
#endif

PSOperations/LocationCondition.swift

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,21 @@ Abstract:
66
This file shows an example of implementing the OperationCondition protocol.
77
*/
88

9+
#if !os(OSX)
10+
911
import CoreLocation
1012

1113
/// A condition for verifying access to the user's location.
1214
public struct LocationCondition: OperationCondition {
1315
/**
14-
Declare a new enum instead of using `CLAuthorizationStatus`, because that
15-
enum has more case values than are necessary for our purposes.
16-
*/
16+
Declare a new enum instead of using `CLAuthorizationStatus`, because that
17+
enum has more case values than are necessary for our purposes.
18+
*/
1719
public enum Usage {
1820
case WhenInUse
21+
#if !os(tvOS)
1922
case Always
23+
#endif
2024
}
2125

2226
public static let name = "Location"
@@ -39,33 +43,33 @@ public struct LocationCondition: OperationCondition {
3943
let actual = CLLocationManager.authorizationStatus()
4044

4145
var error: NSError?
42-
46+
4347
// There are several factors to consider when evaluating this condition
4448
switch (enabled, usage, actual) {
45-
case (true, _, .AuthorizedAlways):
46-
// The service is enabled, and we have "Always" permission -> condition satisfied.
47-
break
48-
49-
case (true, .WhenInUse, .AuthorizedWhenInUse):
50-
/*
51-
The service is enabled, and we have and need "WhenInUse"
52-
permission -> condition satisfied.
53-
*/
54-
break
55-
56-
default:
57-
/*
58-
Anything else is an error. Maybe location services are disabled,
59-
or maybe we need "Always" permission but only have "WhenInUse",
60-
or maybe access has been restricted or denied,
61-
or maybe access hasn't been request yet.
62-
63-
The last case would happen if this condition were wrapped in a `SilentCondition`.
64-
*/
65-
error = NSError(code: .ConditionFailed, userInfo: [
66-
OperationConditionKey: self.dynamicType.name,
67-
self.dynamicType.locationServicesEnabledKey: enabled,
68-
self.dynamicType.authorizationStatusKey: Int(actual.rawValue)
49+
case (true, _, .AuthorizedAlways):
50+
// The service is enabled, and we have "Always" permission -> condition satisfied.
51+
break
52+
53+
case (true, .WhenInUse, .AuthorizedWhenInUse):
54+
/*
55+
The service is enabled, and we have and need "WhenInUse"
56+
permission -> condition satisfied.
57+
*/
58+
break
59+
60+
default:
61+
/*
62+
Anything else is an error. Maybe location services are disabled,
63+
or maybe we need "Always" permission but only have "WhenInUse",
64+
or maybe access has been restricted or denied,
65+
or maybe access hasn't been request yet.
66+
67+
The last case would happen if this condition were wrapped in a `SilentCondition`.
68+
*/
69+
error = NSError(code: .ConditionFailed, userInfo: [
70+
OperationConditionKey: self.dynamicType.name,
71+
self.dynamicType.locationServicesEnabledKey: enabled,
72+
self.dynamicType.authorizationStatusKey: Int(actual.rawValue)
6973
])
7074
}
7175

@@ -79,9 +83,9 @@ public struct LocationCondition: OperationCondition {
7983
}
8084

8185
/**
82-
A private `Operation` that will request permission to access the user's location,
83-
if permission has not already been granted.
84-
*/
86+
A private `Operation` that will request permission to access the user's location,
87+
if permission has not already been granted.
88+
*/
8589
class LocationPermissionOperation: Operation {
8690
let usage: LocationCondition.Usage
8791
var manager: CLLocationManager?
@@ -90,43 +94,64 @@ class LocationPermissionOperation: Operation {
9094
self.usage = usage
9195
super.init()
9296
/*
93-
This is an operation that potentially presents an alert so it should
94-
be mutually exclusive with anything else that presents an alert.
97+
This is an operation that potentially presents an alert so it should
98+
be mutually exclusive with anything else that presents an alert.
9599
*/
96100
addCondition(AlertPresentation())
97101
}
98102

99103
override func execute() {
100104
/*
101-
Not only do we need to handle the "Not Determined" case, but we also
102-
need to handle the "upgrade" (.WhenInUse -> .Always) case.
105+
Not only do we need to handle the "Not Determined" case, but we also
106+
need to handle the "upgrade" (.WhenInUse -> .Always) case.
103107
*/
104-
switch (CLLocationManager.authorizationStatus(), usage) {
108+
109+
#if os(tvOS)
110+
switch (CLLocationManager.authorizationStatus(), usage) {
111+
case (.NotDetermined, _):
112+
dispatch_async(dispatch_get_main_queue()) {
113+
self.requestPermission()
114+
}
115+
116+
default:
117+
finish()
118+
}
119+
#else
120+
switch (CLLocationManager.authorizationStatus(), usage) {
105121
case (.NotDetermined, _), (.AuthorizedWhenInUse, .Always):
106122
dispatch_async(dispatch_get_main_queue()) {
107123
self.requestPermission()
108124
}
109-
125+
110126
default:
111127
finish()
112-
}
128+
}
129+
#endif
113130
}
114131

115132
private func requestPermission() {
116133
manager = CLLocationManager()
117134
manager?.delegate = self
118-
135+
119136
let key: String
120137

121-
switch usage {
138+
#if os(tvOS)
139+
switch usage {
122140
case .WhenInUse:
123141
key = "NSLocationWhenInUseUsageDescription"
124142
manager?.requestWhenInUseAuthorization()
125-
143+
}
144+
#else
145+
switch usage {
146+
case .WhenInUse:
147+
key = "NSLocationWhenInUseUsageDescription"
148+
manager?.requestWhenInUseAuthorization()
149+
126150
case .Always:
127151
key = "NSLocationAlwaysUsageDescription"
128152
manager?.requestAlwaysAuthorization()
129-
}
153+
}
154+
#endif
130155

131156
// This is helpful when developing the app.
132157
assert(NSBundle.mainBundle().objectForInfoDictionaryKey(key) != nil, "Requesting location permission requires the \(key) key in your Info.plist")
@@ -141,3 +166,5 @@ extension LocationPermissionOperation: CLLocationManagerDelegate {
141166
}
142167
}
143168
}
169+
170+
#endif

0 commit comments

Comments
 (0)