From 064682a94d67ca31e662b74d89decc338e08ad59 Mon Sep 17 00:00:00 2001 From: PBE Date: Tue, 7 Jul 2015 09:34:11 +0200 Subject: [PATCH 1/6] created physically folders; reformatted code --- ADVOperation.xcodeproj/project.pbxproj | 314 +++++++++--------- .../{ => Conditions}/CalendarCondition.swift | 51 +-- Source/{ => Conditions}/CloudCondition.swift | 40 ++- Source/{ => Conditions}/HealthCondition.swift | 114 ++++--- .../{ => Conditions}/LocationCondition.swift | 102 +++--- .../{ => Conditions}/MutuallyExclusive.swift | 16 +- .../{ => Conditions}/NegatedCondition.swift | 31 +- .../NoCancelledDependencies.swift | 20 +- .../{ => Conditions}/OperationCondition.swift | 36 +- Source/{ => Conditions}/OperationErrors.swift | 2 +- .../{ => Conditions}/PassbookCondition.swift | 21 +- Source/{ => Conditions}/PhotosCondition.swift | 0 .../ReachabilityCondition.swift | 38 +-- .../RemoteNotificationCondition.swift | 57 ++-- Source/{ => Conditions}/SilentCondition.swift | 13 +- .../UserNotificationCondition.swift | 56 ++-- .../CKContainer+Operations.swift | 24 +- .../Dictionary+Operations.swift | 2 +- .../NSOperation+Operations.swift | 3 +- .../UIUserNotifications+Operations.swift | 22 +- Source/{ => Observer}/BlockObserver.swift | 13 +- Source/{ => Observer}/OperationObserver.swift | 9 +- Source/{ => Observer}/TimeoutObserver.swift | 13 +- Source/{ => Operation}/BlockOperation.swift | 14 +- Source/{ => Operation}/DelayOperation.swift | 27 +- Source/{ => Operation}/GroupOperation.swift | 28 +- .../{ => Operation}/LocationOperation.swift | 25 +- Source/{ => Operation}/Operation.swift | 144 ++++---- .../URLSessionTaskOperation.swift | 19 +- .../ExclusivityController.swift | 27 +- .../{ => OperationQueue}/OperationQueue.swift | 96 +++--- 31 files changed, 719 insertions(+), 658 deletions(-) rename Source/{ => Conditions}/CalendarCondition.swift (70%) rename Source/{ => Conditions}/CloudCondition.swift (87%) rename Source/{ => Conditions}/HealthCondition.swift (56%) rename Source/{ => Conditions}/LocationCondition.swift (66%) rename Source/{ => Conditions}/MutuallyExclusive.swift (92%) rename Source/{ => Conditions}/NegatedCondition.swift (72%) rename Source/{ => Conditions}/NoCancelledDependencies.swift (83%) rename Source/{ => Conditions}/OperationCondition.swift (92%) rename Source/{ => Conditions}/OperationErrors.swift (97%) rename Source/{ => Conditions}/PassbookCondition.swift (88%) rename Source/{ => Conditions}/PhotosCondition.swift (100%) rename Source/{ => Conditions}/ReachabilityCondition.swift (88%) rename Source/{ => Conditions}/RemoteNotificationCondition.swift (86%) rename Source/{ => Conditions}/SilentCondition.swift (92%) rename Source/{ => Conditions}/UserNotificationCondition.swift (85%) rename Source/{ => Extensions}/CKContainer+Operations.swift (84%) rename Source/{ => Extensions}/Dictionary+Operations.swift (88%) rename Source/{ => Extensions}/NSOperation+Operations.swift (97%) rename Source/{ => Extensions}/UIUserNotifications+Operations.swift (92%) rename Source/{ => Observer}/BlockObserver.swift (97%) rename Source/{ => Observer}/OperationObserver.swift (97%) rename Source/{ => Observer}/TimeoutObserver.swift (94%) rename Source/{ => Operation}/BlockOperation.swift (96%) rename Source/{ => Operation}/DelayOperation.swift (89%) rename Source/{ => Operation}/GroupOperation.swift (97%) rename Source/{ => Operation}/LocationOperation.swift (90%) rename Source/{ => Operation}/Operation.swift (89%) rename Source/{ => Operation}/URLSessionTaskOperation.swift (91%) rename Source/{ => OperationQueue}/ExclusivityController.swift (93%) rename Source/{ => OperationQueue}/OperationQueue.swift (72%) diff --git a/ADVOperation.xcodeproj/project.pbxproj b/ADVOperation.xcodeproj/project.pbxproj index e3c0f58..62fa287 100644 --- a/ADVOperation.xcodeproj/project.pbxproj +++ b/ADVOperation.xcodeproj/project.pbxproj @@ -7,39 +7,39 @@ objects = { /* Begin PBXBuildFile section */ + 134930FC7911FA67B2B4D7DF /* OperationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A84EC920C5908B9EBA0 /* OperationErrors.swift */; }; + 134931411BBF6370A978DABC /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134938E6D96135B1DBE590FA /* OperationQueue.swift */; }; + 13493201C21925B5246A0246 /* NSOperation+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */; }; + 134932BF46092E111600482B /* NegatedCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BA2D24FE404BC15CBF8 /* NegatedCondition.swift */; }; + 13493338E30D6F010F759FD2 /* SilentCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BAA7B983B2A9DD70153 /* SilentCondition.swift */; }; + 134933B7FB906B44B1607B54 /* NoCancelledDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */; }; + 1349341FFC87A62ED60EE2EF /* OperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493D60F708E7BFD326F144 /* OperationObserver.swift */; }; + 134935195F8C1F85F4302B2C /* PassbookCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134937EC6979C8C804E07AFA /* PassbookCondition.swift */; }; + 1349360903CEC2ACDE4CBC91 /* UIUserNotifications+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493011AE350750A6134743 /* UIUserNotifications+Operations.swift */; }; + 1349360E87176D613F47500D /* CloudCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932F050B4568B14C8F2D3 /* CloudCondition.swift */; }; + 13493711793B138D03F2D29A /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932428BA712B0F210BAA5 /* ExclusivityController.swift */; }; + 1349373A64C53B6C57E57A49 /* Dictionary+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ECC76B568D7691439A3 /* Dictionary+Operations.swift */; }; + 1349373AE6F86D1C8BC8FB3A /* RemoteNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932EF0A14315328EC6A3A /* RemoteNotificationCondition.swift */; }; + 1349375932A0DA51708ACCEE /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A3DA8A5946B70A14674 /* PhotosCondition.swift */; }; + 134938AACE32347FA50E1C6F /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A18E225102B2BB5F75C /* LocationOperation.swift */; }; + 134938EC0526BD7445A952EF /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */; }; + 13493A38C8C16BE598E04969 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493FC92789F5097A896A73 /* HealthCondition.swift */; }; + 13493A98CA8E7A43C7FC23A9 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */; }; + 13493AE54263F87E42D5B799 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F87EE5CB20941A934F2 /* BlockOperation.swift */; }; + 13493B409710FCD8B3596BD6 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */; }; + 13493BDEBF2CDEED6AD2028A /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493B673624213866B375D7 /* Operation.swift */; }; + 13493BEBAA8A2614FBCDBF0E /* URLSessionTaskOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493B097F3E57A358999B78 /* URLSessionTaskOperation.swift */; }; + 13493C5D50A6087A92FDD508 /* BlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134938B6B589E03B1377C6FD /* BlockObserver.swift */; }; + 13493C5FA625EC02B3D0A7A8 /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */; }; + 13493CB2964452FAEEE4E28B /* DelayOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493D01646023977702042A /* DelayOperation.swift */; }; + 13493DF93070CB84CD5A58B9 /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932D5FE660F99325CD4C0 /* GroupOperation.swift */; }; + 13493E6507EEA35308B23182 /* UserNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A3B178B50C93EB4A857 /* UserNotificationCondition.swift */; }; + 13493ECEA49AE331A40DA484 /* LocationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A642DCBC93D7AFCF84A /* LocationCondition.swift */; }; + 13493ED6E7A06C6B9DCE6ACD /* TimeoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */; }; + 13493FCD68D71F2EB0BFD4D9 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493405578740A4F865B7EC /* CKContainer+Operations.swift */; }; AA8215681B2F23FA00A04622 /* ADVOperation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA82155D1B2F23FA00A04622 /* ADVOperation.framework */; }; AA8215851B2F260200A04622 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AA8215831B2F260200A04622 /* Info.plist */; }; AA8215891B2F417E00A04622 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AA8215881B2F417E00A04622 /* Info.plist */; }; - AA8215AA1B2F422C00A04622 /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82158C1B2F422C00A04622 /* GroupOperation.swift */; }; - AA8215AB1B2F422C00A04622 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82158D1B2F422C00A04622 /* BlockOperation.swift */; }; - AA8215AC1B2F422C00A04622 /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82158E1B2F422C00A04622 /* OperationQueue.swift */; }; - AA8215AD1B2F422C00A04622 /* BlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82158F1B2F422C00A04622 /* BlockObserver.swift */; }; - AA8215AE1B2F422C00A04622 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215901B2F422C00A04622 /* CalendarCondition.swift */; }; - AA8215AF1B2F422C00A04622 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215911B2F422C00A04622 /* CKContainer+Operations.swift */; }; - AA8215B01B2F422C00A04622 /* CloudCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215921B2F422C00A04622 /* CloudCondition.swift */; }; - AA8215B11B2F422C00A04622 /* DelayOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215931B2F422C00A04622 /* DelayOperation.swift */; }; - AA8215B21B2F422C00A04622 /* Dictionary+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215941B2F422C00A04622 /* Dictionary+Operations.swift */; }; - AA8215B31B2F422C00A04622 /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215951B2F422C00A04622 /* ExclusivityController.swift */; }; - AA8215B41B2F422C00A04622 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215961B2F422C00A04622 /* HealthCondition.swift */; }; - AA8215B51B2F422C00A04622 /* LocationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215971B2F422C00A04622 /* LocationCondition.swift */; }; - AA8215B61B2F422C00A04622 /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215981B2F422C00A04622 /* LocationOperation.swift */; }; - AA8215B71B2F422C00A04622 /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215991B2F422C00A04622 /* MutuallyExclusive.swift */; }; - AA8215B81B2F422C00A04622 /* NegatedCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82159A1B2F422C00A04622 /* NegatedCondition.swift */; }; - AA8215B91B2F422C00A04622 /* NoCancelledDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82159B1B2F422C00A04622 /* NoCancelledDependencies.swift */; }; - AA8215BA1B2F422C00A04622 /* NSOperation+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82159C1B2F422C00A04622 /* NSOperation+Operations.swift */; }; - AA8215BB1B2F422C00A04622 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82159D1B2F422C00A04622 /* Operation.swift */; }; - AA8215BC1B2F422C00A04622 /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82159E1B2F422C00A04622 /* OperationCondition.swift */; }; - AA8215BD1B2F422C00A04622 /* OperationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA82159F1B2F422C00A04622 /* OperationErrors.swift */; }; - AA8215BE1B2F422C00A04622 /* OperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A01B2F422C00A04622 /* OperationObserver.swift */; }; - AA8215BF1B2F422C00A04622 /* PassbookCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A11B2F422C00A04622 /* PassbookCondition.swift */; }; - AA8215C01B2F422C00A04622 /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A21B2F422C00A04622 /* PhotosCondition.swift */; }; - AA8215C11B2F422C00A04622 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A31B2F422C00A04622 /* ReachabilityCondition.swift */; }; - AA8215C21B2F422C00A04622 /* RemoteNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A41B2F422C00A04622 /* RemoteNotificationCondition.swift */; }; - AA8215C31B2F422C00A04622 /* SilentCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A51B2F422C00A04622 /* SilentCondition.swift */; }; - AA8215C41B2F422C00A04622 /* TimeoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A61B2F422C00A04622 /* TimeoutObserver.swift */; }; - AA8215C51B2F422C00A04622 /* UIUserNotifications+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A71B2F422C00A04622 /* UIUserNotifications+Operations.swift */; }; - AA8215C61B2F422C00A04622 /* URLSessionTaskOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A81B2F422C00A04622 /* URLSessionTaskOperation.swift */; }; - AA8215C71B2F422C00A04622 /* UserNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA8215A91B2F422C00A04622 /* UserNotificationCondition.swift */; }; AA9A92321B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA9A92311B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift */; }; /* End PBXBuildFile section */ @@ -54,41 +54,41 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 13493011AE350750A6134743 /* UIUserNotifications+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIUserNotifications+Operations.swift"; sourceTree = ""; }; + 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityCondition.swift; sourceTree = ""; }; + 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationCondition.swift; sourceTree = ""; }; + 134932428BA712B0F210BAA5 /* ExclusivityController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExclusivityController.swift; sourceTree = ""; }; + 134932D5FE660F99325CD4C0 /* GroupOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupOperation.swift; sourceTree = ""; }; + 134932EF0A14315328EC6A3A /* RemoteNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteNotificationCondition.swift; sourceTree = ""; }; + 134932F050B4568B14C8F2D3 /* CloudCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudCondition.swift; sourceTree = ""; }; + 13493405578740A4F865B7EC /* CKContainer+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CKContainer+Operations.swift"; sourceTree = ""; }; + 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutuallyExclusive.swift; sourceTree = ""; }; + 134937EC6979C8C804E07AFA /* PassbookCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassbookCondition.swift; sourceTree = ""; }; + 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoCancelledDependencies.swift; sourceTree = ""; }; + 134938B6B589E03B1377C6FD /* BlockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockObserver.swift; sourceTree = ""; }; + 134938E6D96135B1DBE590FA /* OperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = ""; }; + 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSOperation+Operations.swift"; sourceTree = ""; }; + 13493A18E225102B2BB5F75C /* LocationOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationOperation.swift; sourceTree = ""; }; + 13493A3B178B50C93EB4A857 /* UserNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotificationCondition.swift; sourceTree = ""; }; + 13493A3DA8A5946B70A14674 /* PhotosCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosCondition.swift; sourceTree = ""; }; + 13493A642DCBC93D7AFCF84A /* LocationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationCondition.swift; sourceTree = ""; }; + 13493A84EC920C5908B9EBA0 /* OperationErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationErrors.swift; sourceTree = ""; }; + 13493B097F3E57A358999B78 /* URLSessionTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionTaskOperation.swift; sourceTree = ""; }; + 13493B673624213866B375D7 /* Operation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; + 13493BA2D24FE404BC15CBF8 /* NegatedCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NegatedCondition.swift; sourceTree = ""; }; + 13493BAA7B983B2A9DD70153 /* SilentCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SilentCondition.swift; sourceTree = ""; }; + 13493D01646023977702042A /* DelayOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelayOperation.swift; sourceTree = ""; }; + 13493D60F708E7BFD326F144 /* OperationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationObserver.swift; sourceTree = ""; }; + 13493ECC76B568D7691439A3 /* Dictionary+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Operations.swift"; sourceTree = ""; }; + 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarCondition.swift; sourceTree = ""; }; + 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeoutObserver.swift; sourceTree = ""; }; + 13493F87EE5CB20941A934F2 /* BlockOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockOperation.swift; sourceTree = ""; }; + 13493FC92789F5097A896A73 /* HealthCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HealthCondition.swift; sourceTree = ""; }; AA82155D1B2F23FA00A04622 /* ADVOperation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ADVOperation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AA8215671B2F23FA00A04622 /* ADVOperationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ADVOperationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AA8215831B2F260200A04622 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AA8215871B2F417E00A04622 /* ADVOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ADVOperation.h; sourceTree = ""; }; AA8215881B2F417E00A04622 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - AA82158C1B2F422C00A04622 /* GroupOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupOperation.swift; sourceTree = ""; }; - AA82158D1B2F422C00A04622 /* BlockOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockOperation.swift; sourceTree = ""; }; - AA82158E1B2F422C00A04622 /* OperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = ""; }; - AA82158F1B2F422C00A04622 /* BlockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockObserver.swift; sourceTree = ""; }; - AA8215901B2F422C00A04622 /* CalendarCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarCondition.swift; sourceTree = ""; }; - AA8215911B2F422C00A04622 /* CKContainer+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CKContainer+Operations.swift"; sourceTree = ""; }; - AA8215921B2F422C00A04622 /* CloudCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudCondition.swift; sourceTree = ""; }; - AA8215931B2F422C00A04622 /* DelayOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DelayOperation.swift; sourceTree = ""; }; - AA8215941B2F422C00A04622 /* Dictionary+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Operations.swift"; sourceTree = ""; }; - AA8215951B2F422C00A04622 /* ExclusivityController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExclusivityController.swift; sourceTree = ""; }; - AA8215961B2F422C00A04622 /* HealthCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HealthCondition.swift; sourceTree = ""; }; - AA8215971B2F422C00A04622 /* LocationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationCondition.swift; sourceTree = ""; }; - AA8215981B2F422C00A04622 /* LocationOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationOperation.swift; sourceTree = ""; }; - AA8215991B2F422C00A04622 /* MutuallyExclusive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutuallyExclusive.swift; sourceTree = ""; }; - AA82159A1B2F422C00A04622 /* NegatedCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NegatedCondition.swift; sourceTree = ""; }; - AA82159B1B2F422C00A04622 /* NoCancelledDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoCancelledDependencies.swift; sourceTree = ""; }; - AA82159C1B2F422C00A04622 /* NSOperation+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSOperation+Operations.swift"; sourceTree = ""; }; - AA82159D1B2F422C00A04622 /* Operation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Operation.swift; sourceTree = ""; }; - AA82159E1B2F422C00A04622 /* OperationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationCondition.swift; sourceTree = ""; }; - AA82159F1B2F422C00A04622 /* OperationErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationErrors.swift; sourceTree = ""; }; - AA8215A01B2F422C00A04622 /* OperationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationObserver.swift; sourceTree = ""; }; - AA8215A11B2F422C00A04622 /* PassbookCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassbookCondition.swift; sourceTree = ""; }; - AA8215A21B2F422C00A04622 /* PhotosCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosCondition.swift; sourceTree = ""; }; - AA8215A31B2F422C00A04622 /* ReachabilityCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityCondition.swift; sourceTree = ""; }; - AA8215A41B2F422C00A04622 /* RemoteNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteNotificationCondition.swift; sourceTree = ""; }; - AA8215A51B2F422C00A04622 /* SilentCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SilentCondition.swift; sourceTree = ""; }; - AA8215A61B2F422C00A04622 /* TimeoutObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeoutObserver.swift; sourceTree = ""; }; - AA8215A71B2F422C00A04622 /* UIUserNotifications+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIUserNotifications+Operations.swift"; sourceTree = ""; }; - AA8215A81B2F422C00A04622 /* URLSessionTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionTaskOperation.swift; sourceTree = ""; }; - AA8215A91B2F422C00A04622 /* UserNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotificationCondition.swift; sourceTree = ""; }; AA9A92311B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionTaskOperationTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -111,126 +111,126 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - AA8215531B2F23FA00A04622 = { + 13493063DF9812FFE481F145 /* Extensions */ = { isa = PBXGroup; children = ( - AA8215861B2F417E00A04622 /* Source */, - AA8215811B2F260200A04622 /* Tests */, - AA82155E1B2F23FA00A04622 /* Products */, + 13493405578740A4F865B7EC /* CKContainer+Operations.swift */, + 13493ECC76B568D7691439A3 /* Dictionary+Operations.swift */, + 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */, + 13493011AE350750A6134743 /* UIUserNotifications+Operations.swift */, ); + path = Extensions; sourceTree = ""; }; - AA82155E1B2F23FA00A04622 /* Products */ = { + 134933275DC3425D864C5967 /* Operation */ = { isa = PBXGroup; children = ( - AA82155D1B2F23FA00A04622 /* ADVOperation.framework */, - AA8215671B2F23FA00A04622 /* ADVOperationTests.xctest */, + 13493F87EE5CB20941A934F2 /* BlockOperation.swift */, + 13493D01646023977702042A /* DelayOperation.swift */, + 134932D5FE660F99325CD4C0 /* GroupOperation.swift */, + 13493A18E225102B2BB5F75C /* LocationOperation.swift */, + 13493B673624213866B375D7 /* Operation.swift */, + 13493B097F3E57A358999B78 /* URLSessionTaskOperation.swift */, ); - name = Products; + path = Operation; sourceTree = ""; }; - AA8215811B2F260200A04622 /* Tests */ = { + 13493A1CA8F733B3147721E4 /* Observer */ = { isa = PBXGroup; children = ( - AA9A92311B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift */, - AA82158B1B2F41D100A04622 /* Supporting Files */, + 134938B6B589E03B1377C6FD /* BlockObserver.swift */, + 13493D60F708E7BFD326F144 /* OperationObserver.swift */, + 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */, ); - path = Tests; + path = Observer; sourceTree = ""; }; - AA8215861B2F417E00A04622 /* Source */ = { + 13493A247F0CFCC8A9E6B161 /* Conditions */ = { isa = PBXGroup; children = ( - AA8215C81B2F425F00A04622 /* Operation Queue */, - AA9A922D1B2F431F003CF5D2 /* Operations */, - AA9A922E1B2F434A003CF5D2 /* Observers */, - AA9A92301B2F438D003CF5D2 /* Conditions */, - AA9A922F1B2F436C003CF5D2 /* Convenience Extensions */, - AA82158A1B2F418400A04622 /* Supporting Files */, + 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */, + 134932F050B4568B14C8F2D3 /* CloudCondition.swift */, + 13493FC92789F5097A896A73 /* HealthCondition.swift */, + 13493A642DCBC93D7AFCF84A /* LocationCondition.swift */, + 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */, + 13493BA2D24FE404BC15CBF8 /* NegatedCondition.swift */, + 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */, + 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */, + 13493A84EC920C5908B9EBA0 /* OperationErrors.swift */, + 134937EC6979C8C804E07AFA /* PassbookCondition.swift */, + 13493A3DA8A5946B70A14674 /* PhotosCondition.swift */, + 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */, + 134932EF0A14315328EC6A3A /* RemoteNotificationCondition.swift */, + 13493BAA7B983B2A9DD70153 /* SilentCondition.swift */, + 13493A3B178B50C93EB4A857 /* UserNotificationCondition.swift */, ); - path = Source; + path = Conditions; sourceTree = ""; }; - AA82158A1B2F418400A04622 /* Supporting Files */ = { + 13493E72AC03FFD260E62889 /* OperationQueue */ = { isa = PBXGroup; children = ( - AA8215871B2F417E00A04622 /* ADVOperation.h */, - AA8215881B2F417E00A04622 /* Info.plist */, + 134932428BA712B0F210BAA5 /* ExclusivityController.swift */, + 134938E6D96135B1DBE590FA /* OperationQueue.swift */, ); - name = "Supporting Files"; + path = OperationQueue; sourceTree = ""; }; - AA82158B1B2F41D100A04622 /* Supporting Files */ = { + AA8215531B2F23FA00A04622 = { isa = PBXGroup; children = ( - AA8215831B2F260200A04622 /* Info.plist */, + AA8215861B2F417E00A04622 /* Source */, + AA8215811B2F260200A04622 /* Tests */, + AA82155E1B2F23FA00A04622 /* Products */, ); - name = "Supporting Files"; sourceTree = ""; }; - AA8215C81B2F425F00A04622 /* Operation Queue */ = { + AA82155E1B2F23FA00A04622 /* Products */ = { isa = PBXGroup; children = ( - AA8215951B2F422C00A04622 /* ExclusivityController.swift */, - AA82158E1B2F422C00A04622 /* OperationQueue.swift */, + AA82155D1B2F23FA00A04622 /* ADVOperation.framework */, + AA8215671B2F23FA00A04622 /* ADVOperationTests.xctest */, ); - name = "Operation Queue"; + name = Products; sourceTree = ""; }; - AA9A922D1B2F431F003CF5D2 /* Operations */ = { + AA8215811B2F260200A04622 /* Tests */ = { isa = PBXGroup; children = ( - AA82159D1B2F422C00A04622 /* Operation.swift */, - AA82158D1B2F422C00A04622 /* BlockOperation.swift */, - AA82158C1B2F422C00A04622 /* GroupOperation.swift */, - AA8215A81B2F422C00A04622 /* URLSessionTaskOperation.swift */, - AA8215981B2F422C00A04622 /* LocationOperation.swift */, - AA8215931B2F422C00A04622 /* DelayOperation.swift */, + AA9A92311B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift */, + AA82158B1B2F41D100A04622 /* Supporting Files */, ); - name = Operations; + path = Tests; sourceTree = ""; }; - AA9A922E1B2F434A003CF5D2 /* Observers */ = { + AA8215861B2F417E00A04622 /* Source */ = { isa = PBXGroup; children = ( - AA8215A01B2F422C00A04622 /* OperationObserver.swift */, - AA82158F1B2F422C00A04622 /* BlockObserver.swift */, - AA8215A61B2F422C00A04622 /* TimeoutObserver.swift */, + AA82158A1B2F418400A04622 /* Supporting Files */, + 13493A247F0CFCC8A9E6B161 /* Conditions */, + 13493063DF9812FFE481F145 /* Extensions */, + 13493A1CA8F733B3147721E4 /* Observer */, + 13493E72AC03FFD260E62889 /* OperationQueue */, + 134933275DC3425D864C5967 /* Operation */, ); - name = Observers; + path = Source; sourceTree = ""; }; - AA9A922F1B2F436C003CF5D2 /* Convenience Extensions */ = { + AA82158A1B2F418400A04622 /* Supporting Files */ = { isa = PBXGroup; children = ( - AA8215941B2F422C00A04622 /* Dictionary+Operations.swift */, - AA82159C1B2F422C00A04622 /* NSOperation+Operations.swift */, - AA8215911B2F422C00A04622 /* CKContainer+Operations.swift */, - AA8215A71B2F422C00A04622 /* UIUserNotifications+Operations.swift */, + AA8215871B2F417E00A04622 /* ADVOperation.h */, + AA8215881B2F417E00A04622 /* Info.plist */, ); - name = "Convenience Extensions"; + name = "Supporting Files"; sourceTree = ""; }; - AA9A92301B2F438D003CF5D2 /* Conditions */ = { + AA82158B1B2F41D100A04622 /* Supporting Files */ = { isa = PBXGroup; children = ( - AA8215901B2F422C00A04622 /* CalendarCondition.swift */, - AA8215921B2F422C00A04622 /* CloudCondition.swift */, - AA8215961B2F422C00A04622 /* HealthCondition.swift */, - AA8215971B2F422C00A04622 /* LocationCondition.swift */, - AA8215991B2F422C00A04622 /* MutuallyExclusive.swift */, - AA82159A1B2F422C00A04622 /* NegatedCondition.swift */, - AA82159B1B2F422C00A04622 /* NoCancelledDependencies.swift */, - AA82159E1B2F422C00A04622 /* OperationCondition.swift */, - AA82159F1B2F422C00A04622 /* OperationErrors.swift */, - AA8215A11B2F422C00A04622 /* PassbookCondition.swift */, - AA8215A21B2F422C00A04622 /* PhotosCondition.swift */, - AA8215A31B2F422C00A04622 /* ReachabilityCondition.swift */, - AA8215A41B2F422C00A04622 /* RemoteNotificationCondition.swift */, - AA8215A51B2F422C00A04622 /* SilentCondition.swift */, - AA8215A91B2F422C00A04622 /* UserNotificationCondition.swift */, + AA8215831B2F260200A04622 /* Info.plist */, ); - name = Conditions; + name = "Supporting Files"; sourceTree = ""; }; /* End PBXGroup section */ @@ -348,37 +348,37 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - AA8215BA1B2F422C00A04622 /* NSOperation+Operations.swift in Sources */, - AA8215B81B2F422C00A04622 /* NegatedCondition.swift in Sources */, - AA8215AB1B2F422C00A04622 /* BlockOperation.swift in Sources */, - AA8215AE1B2F422C00A04622 /* CalendarCondition.swift in Sources */, - AA8215BD1B2F422C00A04622 /* OperationErrors.swift in Sources */, - AA8215B11B2F422C00A04622 /* DelayOperation.swift in Sources */, - AA8215C71B2F422C00A04622 /* UserNotificationCondition.swift in Sources */, - AA8215B21B2F422C00A04622 /* Dictionary+Operations.swift in Sources */, - AA8215C61B2F422C00A04622 /* URLSessionTaskOperation.swift in Sources */, - AA8215B61B2F422C00A04622 /* LocationOperation.swift in Sources */, - AA8215AC1B2F422C00A04622 /* OperationQueue.swift in Sources */, - AA8215BB1B2F422C00A04622 /* Operation.swift in Sources */, - AA8215B91B2F422C00A04622 /* NoCancelledDependencies.swift in Sources */, - AA8215C01B2F422C00A04622 /* PhotosCondition.swift in Sources */, - AA8215B51B2F422C00A04622 /* LocationCondition.swift in Sources */, - AA8215C21B2F422C00A04622 /* RemoteNotificationCondition.swift in Sources */, - AA8215AA1B2F422C00A04622 /* GroupOperation.swift in Sources */, - AA8215B31B2F422C00A04622 /* ExclusivityController.swift in Sources */, - AA8215B71B2F422C00A04622 /* MutuallyExclusive.swift in Sources */, - AA8215B01B2F422C00A04622 /* CloudCondition.swift in Sources */, - AA8215C41B2F422C00A04622 /* TimeoutObserver.swift in Sources */, - AA8215BF1B2F422C00A04622 /* PassbookCondition.swift in Sources */, AA9A92321B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift in Sources */, - AA8215B41B2F422C00A04622 /* HealthCondition.swift in Sources */, - AA8215AF1B2F422C00A04622 /* CKContainer+Operations.swift in Sources */, - AA8215AD1B2F422C00A04622 /* BlockObserver.swift in Sources */, - AA8215C31B2F422C00A04622 /* SilentCondition.swift in Sources */, - AA8215C51B2F422C00A04622 /* UIUserNotifications+Operations.swift in Sources */, - AA8215BC1B2F422C00A04622 /* OperationCondition.swift in Sources */, - AA8215BE1B2F422C00A04622 /* OperationObserver.swift in Sources */, - AA8215C11B2F422C00A04622 /* ReachabilityCondition.swift in Sources */, + 13493B409710FCD8B3596BD6 /* CalendarCondition.swift in Sources */, + 1349360E87176D613F47500D /* CloudCondition.swift in Sources */, + 13493A38C8C16BE598E04969 /* HealthCondition.swift in Sources */, + 13493ECEA49AE331A40DA484 /* LocationCondition.swift in Sources */, + 13493C5FA625EC02B3D0A7A8 /* MutuallyExclusive.swift in Sources */, + 134932BF46092E111600482B /* NegatedCondition.swift in Sources */, + 134933B7FB906B44B1607B54 /* NoCancelledDependencies.swift in Sources */, + 134938EC0526BD7445A952EF /* OperationCondition.swift in Sources */, + 134930FC7911FA67B2B4D7DF /* OperationErrors.swift in Sources */, + 134935195F8C1F85F4302B2C /* PassbookCondition.swift in Sources */, + 1349375932A0DA51708ACCEE /* PhotosCondition.swift in Sources */, + 13493A98CA8E7A43C7FC23A9 /* ReachabilityCondition.swift in Sources */, + 1349373AE6F86D1C8BC8FB3A /* RemoteNotificationCondition.swift in Sources */, + 13493338E30D6F010F759FD2 /* SilentCondition.swift in Sources */, + 13493E6507EEA35308B23182 /* UserNotificationCondition.swift in Sources */, + 13493FCD68D71F2EB0BFD4D9 /* CKContainer+Operations.swift in Sources */, + 1349373A64C53B6C57E57A49 /* Dictionary+Operations.swift in Sources */, + 13493201C21925B5246A0246 /* NSOperation+Operations.swift in Sources */, + 1349360903CEC2ACDE4CBC91 /* UIUserNotifications+Operations.swift in Sources */, + 13493C5D50A6087A92FDD508 /* BlockObserver.swift in Sources */, + 1349341FFC87A62ED60EE2EF /* OperationObserver.swift in Sources */, + 13493ED6E7A06C6B9DCE6ACD /* TimeoutObserver.swift in Sources */, + 13493711793B138D03F2D29A /* ExclusivityController.swift in Sources */, + 134931411BBF6370A978DABC /* OperationQueue.swift in Sources */, + 13493AE54263F87E42D5B799 /* BlockOperation.swift in Sources */, + 13493CB2964452FAEEE4E28B /* DelayOperation.swift in Sources */, + 13493DF93070CB84CD5A58B9 /* GroupOperation.swift in Sources */, + 134938AACE32347FA50E1C6F /* LocationOperation.swift in Sources */, + 13493BDEBF2CDEED6AD2028A /* Operation.swift in Sources */, + 13493BEBAA8A2614FBCDBF0E /* URLSessionTaskOperation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/CalendarCondition.swift b/Source/Conditions/CalendarCondition.swift similarity index 70% rename from Source/CalendarCondition.swift rename to Source/Conditions/CalendarCondition.swift index 8977608..02cda70 100644 --- a/Source/CalendarCondition.swift +++ b/Source/Conditions/CalendarCondition.swift @@ -9,35 +9,36 @@ This file shows an example of implementing the OperationCondition protocol. import EventKit /// A condition for verifying access to the user's calendar. + struct CalendarCondition: OperationCondition { - + static let name = "Calendar" static let entityTypeKey = "EKEntityType" static let isMutuallyExclusive = false - + let entityType: EKEntityType - + init(entityType: EKEntityType) { self.entityType = entityType } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return CalendarPermissionOperation(entityType: entityType) } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { switch EKEventStore.authorizationStatusForEntityType(entityType) { - case .Authorized: - completion(.Satisfied) + case .Authorized: + completion(.Satisfied) - default: - // We are not authorized to access entities of this type. - let error = NSError(code: .ConditionFailed, userInfo: [ + default: + // We are not authorized to access entities of this type. + let error = NSError(code: .ConditionFailed, userInfo: [ OperationConditionKey: self.dynamicType.name, self.dynamicType.entityTypeKey: entityType.rawValue - ]) - - completion(.Failed(error)) + ]) + + completion(.Failed(error)) } } } @@ -46,30 +47,32 @@ struct CalendarCondition: OperationCondition { A private `Operation` that will request access to the user's Calendar/Reminders, if it has not already been granted. */ + private class CalendarPermissionOperation: Operation { let entityType: EKEntityType let store = EKEventStore() - + init(entityType: EKEntityType) { self.entityType = entityType super.init() addCondition(AlertPresentation()) } - + override func execute() { let status = EKEventStore.authorizationStatusForEntityType(entityType) - + switch status { - case .NotDetermined: - dispatch_async(dispatch_get_main_queue()) { - self.store.requestAccessToEntityType(self.entityType) { granted, error in - self.finish() - } + case .NotDetermined: + dispatch_async(dispatch_get_main_queue()) { + self.store.requestAccessToEntityType(self.entityType) { + granted, error in + self.finish() } + } - default: - finish() + default: + finish() } } - + } diff --git a/Source/CloudCondition.swift b/Source/Conditions/CloudCondition.swift similarity index 87% rename from Source/CloudCondition.swift rename to Source/Conditions/CloudCondition.swift index 5c303cc..be64f29 100644 --- a/Source/CloudCondition.swift +++ b/Source/Conditions/CloudCondition.swift @@ -9,22 +9,24 @@ This file shows an example of implementing the OperationCondition protocol. import CloudKit /// A condition describing that the operation requires access to a specific CloudKit container. + struct CloudContainerCondition: OperationCondition { - + static let name = "CloudContainer" static let containerKey = "CKContainer" - + /* CloudKit has no problem handling multiple operations at the same time so we will allow operations that use CloudKit to be concurrent with each other. */ static let isMutuallyExclusive = false - - let container: CKContainer // this is the container to which you need access. + + let container: CKContainer + // this is the container to which you need access. let permission: CKApplicationPermissions - + /** - parameter container: the `CKContainer` to which you need access. - parameter permission: the `CKApplicationPermissions` you need for the @@ -35,23 +37,23 @@ struct CloudContainerCondition: OperationCondition { self.container = container self.permission = permission } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return CloudKitPermissionOperation(container: container, permission: permission) } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - container.verifyPermission(permission, requestingIfNecessary: false) { error in + container.verifyPermission(permission, requestingIfNecessary: false) { + error in if let error = error { let conditionError = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.containerKey: self.container, - NSUnderlyingErrorKey: error + OperationConditionKey: self.dynamicType.name, + self.dynamicType.containerKey: self.container, + NSUnderlyingErrorKey: error ]) completion(.Failed(conditionError)) - } - else { + } else { completion(.Satisfied) } } @@ -62,15 +64,16 @@ struct CloudContainerCondition: OperationCondition { This operation asks the user for permission to use CloudKit, if necessary. If permission has already been granted, this operation will quickly finish. */ + private class CloudKitPermissionOperation: Operation { let container: CKContainer let permission: CKApplicationPermissions - + init(container: CKContainer, permission: CKApplicationPermissions) { self.container = container self.permission = permission super.init() - + if permission != [] { /* Requesting non-zero permissions means that this potentially presents @@ -80,11 +83,12 @@ private class CloudKitPermissionOperation: Operation { addCondition(AlertPresentation()) } } - + override func execute() { - container.verifyPermission(permission, requestingIfNecessary: true) { error in + container.verifyPermission(permission, requestingIfNecessary: true) { + error in self.finishWithError(error) } } - + } diff --git a/Source/HealthCondition.swift b/Source/Conditions/HealthCondition.swift similarity index 56% rename from Source/HealthCondition.swift rename to Source/Conditions/HealthCondition.swift index 0e35826..a4e5318 100644 --- a/Source/HealthCondition.swift +++ b/Source/Conditions/HealthCondition.swift @@ -7,7 +7,7 @@ This file shows an example of implementing the OperationCondition protocol. */ #if os(iOS) - + import HealthKit import UIKit @@ -15,15 +15,16 @@ import UIKit A condition to indicate an `Operation` requires access to the user's health data. */ + struct HealthCondition: OperationCondition { static let name = "Health" static let healthDataAvailable = "HealthDataAvailable" static let unauthorizedShareTypesKey = "UnauthorizedShareTypes" static let isMutuallyExclusive = false - + let shareTypes: Set let readTypes: Set - + /** The designated initializer. @@ -37,79 +38,85 @@ struct HealthCondition: OperationCondition { shareTypes = typesToWrite readTypes = typesToRead } - + func dependencyForOperation(operation: Operation) -> NSOperation? { - guard HKHealthStore.isHealthDataAvailable() else { - return nil - } - - guard !shareTypes.isEmpty || !readTypes.isEmpty else { - return nil - } + guard HKHealthStore.isHealthDataAvailable() - return HealthPermissionOperation(shareTypes: shareTypes, readTypes: readTypes) - } - - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - guard HKHealthStore.isHealthDataAvailable() else { - failed(shareTypes, completion: completion) - return - } +else { + return nil +} - let store = HKHealthStore() - /* - Note that we cannot check to see if access to the "typesToRead" - has been granted or not, as that is sensitive data. For example, - a person with diabetes may choose to not allow access to Blood Glucose - data, and the fact that this request has denied is itself an indicator - that the user may have diabetes. - - Thus, we can only check to see if we've been given permission to - write data to HealthKit. - */ - let unauthorizedShareTypes = shareTypes.filter { shareType in - return store.authorizationStatusForType(shareType) != .SharingAuthorized - } +guard !shareTypes.isEmpty || !readTypes.isEmpty else { + return nil +} - if !unauthorizedShareTypes.isEmpty { - failed(Set(unauthorizedShareTypes), completion: completion) - } - else { - completion(.Satisfied) - } - } - - // Break this out in to its own method so we don't clutter up the evaluate... method. - private func failed(unauthorizedShareTypes: Set, completion: OperationConditionResult -> Void) { - let error = NSError(code: .ConditionFailed, userInfo: [ +return HealthPermissionOperation(shareTypes: shareTypes, readTypes: readTypes) +} + +func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + guard HKHealthStore.isHealthDataAvailable() + +else { + failed(shareTypes, completion: completion) + return +} + +let store = HKHealthStore() +/* + Note that we cannot check to see if access to the "typesToRead" + has been granted or not, as that is sensitive data. For example, + a person with diabetes may choose to not allow access to Blood Glucose + data, and the fact that this request has denied is itself an indicator + that the user may have diabetes. + + Thus, we can only check to see if we've been given permission to + write data to HealthKit. +*/ +let unauthorizedShareTypes = shareTypes.filter { + shareType in + return store.authorizationStatusForType(shareType) != .SharingAuthorized +} + +if !unauthorizedShareTypes.isEmpty { + failed(Set(unauthorizedShareTypes), completion: completion) +} else { + completion(.Satisfied) +} +} + +// Break this out in to its own method so we don't clutter up the evaluate... method. +private func failed(unauthorizedShareTypes: Set, completion: OperationConditionResult -> Void) { + let error = NSError(code: .ConditionFailed, userInfo: [ OperationConditionKey: self.dynamicType.name, self.dynamicType.healthDataAvailable: HKHealthStore.isHealthDataAvailable(), self.dynamicType.unauthorizedShareTypesKey: unauthorizedShareTypes - ]) + ]) + + completion(.Failed(error)) +} - completion(.Failed(error)) - } } /** A private `Operation` that will request access to the user's health data, if it has not already been granted. */ + private class HealthPermissionOperation: Operation { let shareTypes: Set let readTypes: Set - + init(shareTypes: Set, readTypes: Set) { self.shareTypes = shareTypes self.readTypes = readTypes - + super.init() addCondition(MutuallyExclusive()) addCondition(MutuallyExclusive()) addCondition(AlertPresentation()) } - + override func execute() { dispatch_async(dispatch_get_main_queue()) { let store = HKHealthStore() @@ -118,12 +125,13 @@ private class HealthPermissionOperation: Operation { has already been granted. */ - store.requestAuthorizationToShareTypes(self.shareTypes, readTypes: self.readTypes) { completed, error in + store.requestAuthorizationToShareTypes(self.shareTypes, readTypes: self.readTypes) { + completed, error in self.finish() } } } - + } - + #endif diff --git a/Source/LocationCondition.swift b/Source/Conditions/LocationCondition.swift similarity index 66% rename from Source/LocationCondition.swift rename to Source/Conditions/LocationCondition.swift index c382e89..e29b7c0 100644 --- a/Source/LocationCondition.swift +++ b/Source/Conditions/LocationCondition.swift @@ -9,70 +9,71 @@ This file shows an example of implementing the OperationCondition protocol. import CoreLocation /// A condition for verifying access to the user's location. + struct LocationCondition: OperationCondition { /** Declare a new enum instead of using `CLAuthorizationStatus`, because that enum has more case values than are necessary for our purposes. */ + enum Usage { case WhenInUse case Always } - + static let name = "Location" static let locationServicesEnabledKey = "CLLocationServicesEnabled" static let authorizationStatusKey = "CLAuthorizationStatus" static let isMutuallyExclusive = false - + let usage: Usage - + init(usage: Usage) { self.usage = usage } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return LocationPermissionOperation(usage: usage) } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { let enabled = CLLocationManager.locationServicesEnabled() let actual = CLLocationManager.authorizationStatus() - + var error: NSError? // There are several factors to consider when evaluating this condition switch (enabled, usage, actual) { - case (true, _, .AuthorizedAlways): - // The service is enabled, and we have "Always" permission -> condition satisfied. - break - - case (true, .WhenInUse, .AuthorizedWhenInUse): - /* - The service is enabled, and we have and need "WhenInUse" - permission -> condition satisfied. - */ - break - - default: - /* - Anything else is an error. Maybe location services are disabled, - or maybe we need "Always" permission but only have "WhenInUse", - or maybe access has been restricted or denied, - or maybe access hasn't been request yet. - - The last case would happen if this condition were wrapped in a `SilentCondition`. - */ - error = NSError(code: .ConditionFailed, userInfo: [ + case (true, _, .AuthorizedAlways): + // The service is enabled, and we have "Always" permission -> condition satisfied. + break + + case (true, .WhenInUse, .AuthorizedWhenInUse): + /* + The service is enabled, and we have and need "WhenInUse" + permission -> condition satisfied. + */ + break + + default: + /* + Anything else is an error. Maybe location services are disabled, + or maybe we need "Always" permission but only have "WhenInUse", + or maybe access has been restricted or denied, + or maybe access hasn't been request yet. + + The last case would happen if this condition were wrapped in a `SilentCondition`. + */ + error = NSError(code: .ConditionFailed, userInfo: [ OperationConditionKey: self.dynamicType.name, self.dynamicType.locationServicesEnabledKey: enabled, self.dynamicType.authorizationStatusKey: Int(actual.rawValue) - ]) + ]) } - + if let error = error { completion(.Failed(error)) - } - else { + } else { completion(.Satisfied) } } @@ -82,10 +83,11 @@ struct LocationCondition: OperationCondition { A private `Operation` that will request permission to access the user's location, if permission has not already been granted. */ + private class LocationPermissionOperation: Operation { let usage: LocationCondition.Usage var manager: CLLocationManager? - + init(usage: LocationCondition.Usage) { self.usage = usage super.init() @@ -95,43 +97,43 @@ private class LocationPermissionOperation: Operation { */ addCondition(AlertPresentation()) } - + override func execute() { /* Not only do we need to handle the "Not Determined" case, but we also need to handle the "upgrade" (.WhenInUse -> .Always) case. */ switch (CLLocationManager.authorizationStatus(), usage) { - case (.NotDetermined, _), (.AuthorizedWhenInUse, .Always): - dispatch_async(dispatch_get_main_queue()) { - self.requestPermission() - } + case (.NotDetermined, _ ), (.AuthorizedWhenInUse, .Always): + dispatch_async(dispatch_get_main_queue()) { + self.requestPermission() + } - default: - finish() + default: + finish() } } - + private func requestPermission() { manager = CLLocationManager() manager?.delegate = self let key: String - + switch usage { - case .WhenInUse: - key = "NSLocationWhenInUseUsageDescription" - manager?.requestWhenInUseAuthorization() - - case .Always: - key = "NSLocationAlwaysUsageDescription" - manager?.requestAlwaysAuthorization() + case .WhenInUse: + key = "NSLocationWhenInUseUsageDescription" + manager?.requestWhenInUseAuthorization() + + case .Always: + key = "NSLocationAlwaysUsageDescription" + manager?.requestAlwaysAuthorization() } - + // This is helpful when developing the app. assert(NSBundle.mainBundle().objectForInfoDictionaryKey(key) != nil, "Requesting location permission requires the \(key) key in your Info.plist") } - + } extension LocationPermissionOperation: CLLocationManagerDelegate { diff --git a/Source/MutuallyExclusive.swift b/Source/Conditions/MutuallyExclusive.swift similarity index 92% rename from Source/MutuallyExclusive.swift rename to Source/Conditions/MutuallyExclusive.swift index c332af1..5654eec 100644 --- a/Source/MutuallyExclusive.swift +++ b/Source/Conditions/MutuallyExclusive.swift @@ -9,21 +9,23 @@ This file shows an example of implementing the OperationCondition protocol. import Foundation /// A generic condition for describing kinds of operations that may not execute concurrently. + struct MutuallyExclusive: OperationCondition { - static var name: String { + static var name: String { return "MutuallyExclusive<\(T.self)>" } static var isMutuallyExclusive: Bool { return true } - - init() { } - + + init() { + } + func dependencyForOperation(operation: Operation) -> NSOperation? { return nil } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { completion(.Satisfied) } @@ -33,7 +35,9 @@ struct MutuallyExclusive: OperationCondition { The purpose of this enum is to simply provide a non-constructible type to be used with `MutuallyExclusive`. */ -enum Alert { } + +enum Alert { +} /// A condition describing that the targeted operation may present an alert. typealias AlertPresentation = MutuallyExclusive diff --git a/Source/NegatedCondition.swift b/Source/Conditions/NegatedCondition.swift similarity index 72% rename from Source/NegatedCondition.swift rename to Source/Conditions/NegatedCondition.swift index cd57dd1..1eb27d2 100644 --- a/Source/NegatedCondition.swift +++ b/Source/Conditions/NegatedCondition.swift @@ -13,42 +13,43 @@ import Foundation This is useful (for example) if you want to only execute an operation if the network is NOT reachable. */ -struct NegatedCondition: OperationCondition { - static var name: String { + +struct NegatedCondition: OperationCondition { + static var name: String { return "Not<\(T.name)>" } - - static var negatedConditionKey: String { + + static var negatedConditionKey: String { return "NegatedCondition" } - - static var isMutuallyExclusive: Bool { + + static var isMutuallyExclusive: Bool { return T.isMutuallyExclusive } - + let condition: T init(condition: T) { self.condition = condition } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return condition.dependencyForOperation(operation) } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - condition.evaluateForOperation(operation) { result in + condition.evaluateForOperation(operation) { + result in if result.error == nil { // If the composed condition failed, then this one succeeded. completion(.Satisfied) - } - else { + } else { // If the composed condition succeeded, then this one failed. let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.negatedConditionKey: self.condition.dynamicType.name + OperationConditionKey: self.dynamicType.name, + self.dynamicType.negatedConditionKey: self.condition.dynamicType.name ]) - + completion(.Failed(error)) } } diff --git a/Source/NoCancelledDependencies.swift b/Source/Conditions/NoCancelledDependencies.swift similarity index 83% rename from Source/NoCancelledDependencies.swift rename to Source/Conditions/NoCancelledDependencies.swift index d88d2f1..018f319 100644 --- a/Source/NoCancelledDependencies.swift +++ b/Source/Conditions/NoCancelledDependencies.swift @@ -13,33 +13,35 @@ import Foundation If any dependency was cancelled, the target operation will be cancelled as well. */ + struct NoCancelledDependencies: OperationCondition { static let name = "NoCancelledDependencies" static let cancelledDependenciesKey = "CancelledDependencies" static let isMutuallyExclusive = false - + init() { // No op. } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return nil } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { // Verify that all of the dependencies executed. - let cancelled = operation.dependencies.filter { $0.cancelled } + let cancelled = operation.dependencies.filter { + $0.cancelled + } if !cancelled.isEmpty { // At least one dependency was cancelled; the condition was not satisfied. let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.cancelledDependenciesKey: cancelled + OperationConditionKey: self.dynamicType.name, + self.dynamicType.cancelledDependenciesKey: cancelled ]) - + completion(.Failed(error)) - } - else { + } else { completion(.Satisfied) } } diff --git a/Source/OperationCondition.swift b/Source/Conditions/OperationCondition.swift similarity index 92% rename from Source/OperationCondition.swift rename to Source/Conditions/OperationCondition.swift index 4829971..270f4ab 100644 --- a/Source/OperationCondition.swift +++ b/Source/Conditions/OperationCondition.swift @@ -14,19 +14,20 @@ let OperationConditionKey = "OperationCondition" A protocol for defining conditions that must be satisfied in order for an operation to begin execution. */ + protocol OperationCondition { /** The name of the condition. This is used in userInfo dictionaries of `.ConditionFailed` errors as the value of the `OperationConditionKey` key. */ static var name: String { get } - + /** Specifies whether multiple instances of the conditionalized operation may be executing simultaneously. */ static var isMutuallyExclusive: Bool { get } - + /** Some conditions may have the ability to satisfy the condition if another operation is executed first. Use this method to return an operation that @@ -40,26 +41,26 @@ protocol OperationCondition { a single `GroupOperation` that executes multiple operations internally. */ func dependencyForOperation(operation: Operation) -> NSOperation? - + /// Evaluate the condition, to see if it has been satisfied or not. func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) } /** - An enum to indicate whether an `OperationCondition` was satisfied, or if it + An enum to indicate whether an `OperationCondition` was satisfied, or if it failed with an error. */ enum OperationConditionResult { case Satisfied case Failed(NSError) - + var error: NSError? { if case .Failed(let error) = self { - return error - } - - return nil - } + return error +} + +return nil +} } // MARK: Evaluate Conditions @@ -70,21 +71,24 @@ struct OperationConditionEvaluator { let conditionGroup = dispatch_group_create() var results = [OperationConditionResult?](count: conditions.count, repeatedValue: nil) - + // Ask each condition to evaluate and store its result in the "results" array. for (index, condition) in conditions.enumerate() { dispatch_group_enter(conditionGroup) - condition.evaluateForOperation(operation) { result in + condition.evaluateForOperation(operation) { + result in results[index] = result dispatch_group_leave(conditionGroup) } } - + // After all the conditions have evaluated, this block will execute. dispatch_group_notify(conditionGroup, dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)) { // Aggregate the errors that occurred, in order. - var failures = results.flatMap { $0?.error } - + var failures = results.flatMap { + $0?.error + } + /* If any of the conditions caused this operation to be cancelled, check for that. @@ -92,7 +96,7 @@ struct OperationConditionEvaluator { if operation.cancelled { failures.append(NSError(code: .ConditionFailed)) } - + completion(failures) } } diff --git a/Source/OperationErrors.swift b/Source/Conditions/OperationErrors.swift similarity index 97% rename from Source/OperationErrors.swift rename to Source/Conditions/OperationErrors.swift index a1cd6e2..d6bc217 100644 --- a/Source/OperationErrors.swift +++ b/Source/Conditions/OperationErrors.swift @@ -16,7 +16,7 @@ enum OperationErrorCode: Int { } extension NSError { - convenience init(code: OperationErrorCode, userInfo: [NSObject: AnyObject]? = nil) { + convenience init(code: OperationErrorCode, userInfo: [NSObject:AnyObject]? = nil) { self.init(domain: OperationErrorDomain, code: code.rawValue, userInfo: userInfo) } } diff --git a/Source/PassbookCondition.swift b/Source/Conditions/PassbookCondition.swift similarity index 88% rename from Source/PassbookCondition.swift rename to Source/Conditions/PassbookCondition.swift index eb0cce1..423e785 100644 --- a/Source/PassbookCondition.swift +++ b/Source/Conditions/PassbookCondition.swift @@ -7,17 +7,19 @@ This file shows an example of implementing the OperationCondition protocol. */ #if os(iOS) - + import PassKit /// A condition for verifying that Passbook exists and is accessible. + struct PassbookCondition: OperationCondition { - + static let name = "Passbook" static let isMutuallyExclusive = false - - init() { } - + + init() { + } + func dependencyForOperation(operation: Operation) -> NSOperation? { /* There's nothing you can do to make Passbook available if it's not @@ -25,19 +27,18 @@ struct PassbookCondition: OperationCondition { */ return nil } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { if PKPassLibrary.isPassLibraryAvailable() { completion(.Satisfied) - } - else { + } else { let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name + OperationConditionKey: self.dynamicType.name ]) completion(.Failed(error)) } } } - + #endif diff --git a/Source/PhotosCondition.swift b/Source/Conditions/PhotosCondition.swift similarity index 100% rename from Source/PhotosCondition.swift rename to Source/Conditions/PhotosCondition.swift diff --git a/Source/ReachabilityCondition.swift b/Source/Conditions/ReachabilityCondition.swift similarity index 88% rename from Source/ReachabilityCondition.swift rename to Source/Conditions/ReachabilityCondition.swift index 0166de9..0cdf6e5 100644 --- a/Source/ReachabilityCondition.swift +++ b/Source/Conditions/ReachabilityCondition.swift @@ -14,46 +14,48 @@ import SystemConfiguration It does *not* perform a long-running reachability check, nor does it respond to changes in reachability. Reachability is evaluated once when the operation to which this is attached is asked about its readiness. */ + struct ReachabilityCondition: OperationCondition { static let hostKey = "Host" static let name = "Reachability" static let isMutuallyExclusive = false - + let host: NSURL - - + + init(host: NSURL) { self.host = host } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return nil } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - ReachabilityController.requestReachability(host) { reachable in + ReachabilityController.requestReachability(host) { + reachable in if reachable { completion(.Satisfied) - } - else { + } else { let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.hostKey: self.host + OperationConditionKey: self.dynamicType.name, + self.dynamicType.hostKey: self.host ]) - + completion(.Failed(error)) } } } - + } /// A private singleton that maintains a basic cache of `SCNetworkReachability` objects. + private class ReachabilityController { static var reachabilityRefs = [String: SCNetworkReachability]() static let reachabilityQueue = dispatch_queue_create("Operations.Reachability", DISPATCH_QUEUE_SERIAL) - + static func requestReachability(url: NSURL, completionHandler: (Bool) -> Void) { if let host = url.host { dispatch_async(reachabilityQueue) { @@ -63,10 +65,10 @@ private class ReachabilityController { let hostString = host as NSString ref = SCNetworkReachabilityCreateWithName(nil, hostString.UTF8String) } - + if let ref = ref { self.reachabilityRefs[host] = ref - + var reachable = false var flags: SCNetworkReachabilityFlags = [] if SCNetworkReachabilityGetFlags(ref, &flags) != 0 { @@ -79,13 +81,11 @@ private class ReachabilityController { reachable = flags.contains(.Reachable) } completionHandler(reachable) - } - else { + } else { completionHandler(false) } } - } - else { + } else { completionHandler(false) } } diff --git a/Source/RemoteNotificationCondition.swift b/Source/Conditions/RemoteNotificationCondition.swift similarity index 86% rename from Source/RemoteNotificationCondition.swift rename to Source/Conditions/RemoteNotificationCondition.swift index b0bca99..7321f9d 100644 --- a/Source/RemoteNotificationCondition.swift +++ b/Source/Conditions/RemoteNotificationCondition.swift @@ -9,7 +9,7 @@ This file shows an example of implementing the OperationCondition protocol. #if os(iOS) import UIKit - + private let RemoteNotificationQueue = OperationQueue() private let RemoteNotificationName = "RemoteNotificationPermissionNotification" @@ -19,49 +19,51 @@ private enum RemoteRegistrationResult { } /// A condition for verifying that the app has the ability to receive push notifications. + struct RemoteNotificationCondition: OperationCondition { static let name = "RemoteNotification" static let isMutuallyExclusive = false - + static func didReceiveNotificationToken(token: NSData) { NSNotificationCenter.defaultCenter().postNotificationName(RemoteNotificationName, object: nil, userInfo: [ - "token": token + "token": token ]) } - + static func didFailToRegister(error: NSError) { NSNotificationCenter.defaultCenter().postNotificationName(RemoteNotificationName, object: nil, userInfo: [ - "error": error + "error": error ]) } - + let application: UIApplication - + init(application: UIApplication) { self.application = application } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return RemoteNotificationPermissionOperation(application: application, handler: { _ in }) } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { /* Since evaluation requires executing an operation, use a private operation queue. */ - RemoteNotificationQueue.addOperation(RemoteNotificationPermissionOperation(application: application) { result in + RemoteNotificationQueue.addOperation(RemoteNotificationPermissionOperation(application: application) { + result in switch result { - case .Token(_): - completion(.Satisfied) + case .Token(_ ): + completion(.Satisfied) - case .Error(let underlyingError): - let error = NSError(code: .ConditionFailed, userInfo: [ + case .Error(let underlyingError): + let error = NSError(code: .ConditionFailed, userInfo: [ OperationConditionKey: self.dynamicType.name, NSUnderlyingErrorKey: underlyingError - ]) + ]) - completion(.Failed(error)) + completion(.Failed(error)) } }) } @@ -78,50 +80,49 @@ struct RemoteNotificationCondition: OperationCondition { `RemoteNotificationCondition.didFailToRegister(_:)` in the appropriate `UIApplicationDelegate` method, as shown in the `AppDelegate.swift` file. */ + private class RemoteNotificationPermissionOperation: Operation { let application: UIApplication private let handler: RemoteRegistrationResult -> Void - + private init(application: UIApplication, handler: RemoteRegistrationResult -> Void) { self.application = application self.handler = handler super.init() - + /* This operation cannot run at the same time as any other remote notification permission operation. */ addCondition(MutuallyExclusive()) } - + override func execute() { dispatch_async(dispatch_get_main_queue()) { let notificationCenter = NSNotificationCenter.defaultCenter() - + notificationCenter.addObserver(self, selector: "didReceiveResponse:", name: RemoteNotificationName, object: nil) - + self.application.registerForRemoteNotifications() } } - + @objc func didReceiveResponse(notification: NSNotification) { NSNotificationCenter.defaultCenter().removeObserver(self) - + let userInfo = notification.userInfo if let token = userInfo?["token"] as? NSData { handler(.Token(token)) - } - else if let error = userInfo?["error"] as? NSError { + } else if let error = userInfo?["error"] as? NSError { handler(.Error(error)) - } - else { + } else { fatalError("Received a notification without a token and without an error.") } finish() } } - + #endif diff --git a/Source/SilentCondition.swift b/Source/Conditions/SilentCondition.swift similarity index 92% rename from Source/SilentCondition.swift rename to Source/Conditions/SilentCondition.swift index 3ed5a2a..3014493 100644 --- a/Source/SilentCondition.swift +++ b/Source/Conditions/SilentCondition.swift @@ -14,26 +14,27 @@ import Foundation the user's location, but you do not want to prompt them for permission if you do not already have it. */ -struct SilentCondition: OperationCondition { + +struct SilentCondition: OperationCondition { let condition: T - + static var name: String { return "Silent<\(T.name)>" } - + static var isMutuallyExclusive: Bool { return T.isMutuallyExclusive } - + init(condition: T) { self.condition = condition } - + func dependencyForOperation(operation: Operation) -> NSOperation? { // Returning nil means we will never a dependency to be generated. return nil } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { condition.evaluateForOperation(operation, completion: completion) } diff --git a/Source/UserNotificationCondition.swift b/Source/Conditions/UserNotificationCondition.swift similarity index 85% rename from Source/UserNotificationCondition.swift rename to Source/Conditions/UserNotificationCondition.swift index b4e7505..fe71ad3 100644 --- a/Source/UserNotificationCondition.swift +++ b/Source/Conditions/UserNotificationCondition.swift @@ -14,8 +14,9 @@ import UIKit A condition for verifying that we can present alerts to the user via `UILocalNotification` and/or remote notifications. */ + struct UserNotificationCondition: OperationCondition { - + enum Behavior { /// Merge the new `UIUserNotificationSettings` with the `currentUserNotificationSettings`. case Merge @@ -23,16 +24,16 @@ struct UserNotificationCondition: OperationCondition { /// Replace the `currentUserNotificationSettings` with the new `UIUserNotificationSettings`. case Replace } - + static let name = "UserNotification" static let currentSettings = "CurrentUserNotificationSettings" static let desiredSettings = "DesiredUserNotificationSettigns" static let isMutuallyExclusive = false - + let settings: UIUserNotificationSettings let application: UIApplication let behavior: Behavior - + /** The designated initializer. @@ -53,30 +54,30 @@ struct UserNotificationCondition: OperationCondition { self.application = application self.behavior = behavior } - + func dependencyForOperation(operation: Operation) -> NSOperation? { return UserNotificationPermissionOperation(settings: settings, application: application, behavior: behavior) } - + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { let result: OperationConditionResult - + let current = application.currentUserNotificationSettings() - switch (current, settings) { - case (let current?, let settings) where current.contains(settings): - result = .Satisfied + switch (current, settings) { + case (let current?, let settings) where current.contains(settings): + result = .Satisfied - default: - let error = NSError(code: .ConditionFailed, userInfo: [ + default: + let error = NSError(code: .ConditionFailed, userInfo: [ OperationConditionKey: self.dynamicType.name, self.dynamicType.currentSettings: current ?? NSNull(), self.dynamicType.desiredSettings: settings - ]) - - result = .Failed(error) + ]) + + result = .Failed(error) } - + completion(result) } } @@ -85,38 +86,39 @@ struct UserNotificationCondition: OperationCondition { A private `Operation` subclass to register a `UIUserNotificationSettings` object with a `UIApplication`, prompting the user for permission if necessary. */ + private class UserNotificationPermissionOperation: Operation { let settings: UIUserNotificationSettings let application: UIApplication let behavior: UserNotificationCondition.Behavior - + init(settings: UIUserNotificationSettings, application: UIApplication, behavior: UserNotificationCondition.Behavior) { self.settings = settings self.application = application self.behavior = behavior - + super.init() addCondition(AlertPresentation()) } - + override func execute() { dispatch_async(dispatch_get_main_queue()) { let current = self.application.currentUserNotificationSettings() - + let settingsToRegister: UIUserNotificationSettings - + switch (current, self.behavior) { - case (let currentSettings?, .Merge): - settingsToRegister = currentSettings.settingsByMerging(self.settings) + case (let currentSettings?, .Merge): + settingsToRegister = currentSettings.settingsByMerging(self.settings) - default: - settingsToRegister = self.settings + default: + settingsToRegister = self.settings } - + self.application.registerUserNotificationSettings(settingsToRegister) } } } - + #endif diff --git a/Source/CKContainer+Operations.swift b/Source/Extensions/CKContainer+Operations.swift similarity index 84% rename from Source/CKContainer+Operations.swift rename to Source/Extensions/CKContainer+Operations.swift index 8d6e564..7ec4afe 100644 --- a/Source/CKContainer+Operations.swift +++ b/Source/Extensions/CKContainer+Operations.swift @@ -34,30 +34,28 @@ extension CKContainer { `CKContainer`. */ private func verifyAccountStatus(container: CKContainer, permission: CKApplicationPermissions, shouldRequest: Bool, completion: NSError? -> Void) { - container.accountStatusWithCompletionHandler { accountStatus, accountError in + container.accountStatusWithCompletionHandler { + accountStatus, accountError in if accountStatus == .Available { if permission != [] { verifyPermission(container, permission: permission, shouldRequest: shouldRequest, completion: completion) - } - else { + } else { completion(nil) } - } - else { + } else { completion(accountError) } } } private func verifyPermission(container: CKContainer, permission: CKApplicationPermissions, shouldRequest: Bool, completion: NSError? -> Void) { - container.statusForApplicationPermission(permission) { permissionStatus, permissionError in + container.statusForApplicationPermission(permission) { + permissionStatus, permissionError in if permissionStatus == .Granted { completion(nil) - } - else if permissionStatus == .InitialState && shouldRequest { + } else if permissionStatus == .InitialState && shouldRequest { requestPermission(container, permission: permission, completion: completion) - } - else { + } else { completion(permissionError) } } @@ -65,11 +63,11 @@ private func verifyPermission(container: CKContainer, permission: CKApplicationP private func requestPermission(container: CKContainer, permission: CKApplicationPermissions, completion: NSError? -> Void) { dispatch_async(dispatch_get_main_queue()) { - container.requestApplicationPermission(permission) { requestStatus, requestError in + container.requestApplicationPermission(permission) { + requestStatus, requestError in if requestStatus == .Granted { completion(nil) - } - else { + } else { completion(requestError) } } diff --git a/Source/Dictionary+Operations.swift b/Source/Extensions/Dictionary+Operations.swift similarity index 88% rename from Source/Dictionary+Operations.swift rename to Source/Extensions/Dictionary+Operations.swift index 8ec8d80..48dda6a 100644 --- a/Source/Dictionary+Operations.swift +++ b/Source/Extensions/Dictionary+Operations.swift @@ -19,7 +19,7 @@ extension Dictionary { be used as the key for the value in the `Dictionary`. If the closure returns `nil`, then the value will be omitted from the `Dictionary`. */ - init(sequence: Sequence, keyMapper: Value -> Key?) { + init(sequence: Sequence, keyMapper: Value -> Key?) { self.init() for item in sequence { diff --git a/Source/NSOperation+Operations.swift b/Source/Extensions/NSOperation+Operations.swift similarity index 97% rename from Source/NSOperation+Operations.swift rename to Source/Extensions/NSOperation+Operations.swift index e5c44a7..2c4f961 100644 --- a/Source/NSOperation+Operations.swift +++ b/Source/Extensions/NSOperation+Operations.swift @@ -23,8 +23,7 @@ extension NSOperation { existing() block() } - } - else { + } else { completionBlock = block } } diff --git a/Source/UIUserNotifications+Operations.swift b/Source/Extensions/UIUserNotifications+Operations.swift similarity index 92% rename from Source/UIUserNotifications+Operations.swift rename to Source/Extensions/UIUserNotifications+Operations.swift index cc1af71..69b6e0d 100644 --- a/Source/UIUserNotifications+Operations.swift +++ b/Source/Extensions/UIUserNotifications+Operations.swift @@ -17,30 +17,34 @@ extension UIUserNotificationSettings { if !types.contains(settings.types) { return false } - + let otherCategories = settings.categories ?? [] let myCategories = categories ?? [] - + return myCategories.isSupersetOf(otherCategories) } - + /** Merge two Settings objects together. `UIUserNotificationCategories` with the same identifier are considered equal. */ func settingsByMerging(settings: UIUserNotificationSettings) -> UIUserNotificationSettings { let mergedTypes = types.union(settings.types) - + let myCategories = categories ?? [] - var existingCategoriesByIdentifier = Dictionary(sequence: myCategories) { $0.identifier } - + var existingCategoriesByIdentifier = Dictionary(sequence: myCategories) { + $0.identifier + } + let newCategories = settings.categories ?? [] - let newCategoriesByIdentifier = Dictionary(sequence: newCategories) { $0.identifier } - + let newCategoriesByIdentifier = Dictionary(sequence: newCategories) { + $0.identifier + } + for (newIdentifier, newCategory) in newCategoriesByIdentifier { existingCategoriesByIdentifier[newIdentifier] = newCategory } - + let mergedCategories = Set(existingCategoriesByIdentifier.values) return UIUserNotificationSettings(forTypes: mergedTypes, categories: mergedCategories) } diff --git a/Source/BlockObserver.swift b/Source/Observer/BlockObserver.swift similarity index 97% rename from Source/BlockObserver.swift rename to Source/Observer/BlockObserver.swift index 826ede5..d563bbe 100644 --- a/Source/BlockObserver.swift +++ b/Source/Observer/BlockObserver.swift @@ -12,29 +12,30 @@ import Foundation The `BlockObserver` is a way to attach arbitrary blocks to significant events in an `Operation`'s lifecycle. */ + struct BlockObserver: OperationObserver { // MARK: Properties - + private let startHandler: (Operation -> Void)? private let produceHandler: ((Operation, NSOperation) -> Void)? private let finishHandler: ((Operation, [NSError]) -> Void)? - + init(startHandler: (Operation -> Void)? = nil, produceHandler: ((Operation, NSOperation) -> Void)? = nil, finishHandler: ((Operation, [NSError]) -> Void)? = nil) { self.startHandler = startHandler self.produceHandler = produceHandler self.finishHandler = finishHandler } - + // MARK: OperationObserver - + func operationDidStart(operation: Operation) { startHandler?(operation) } - + func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { produceHandler?(operation, newOperation) } - + func operationDidFinish(operation: Operation, errors: [NSError]) { finishHandler?(operation, errors) } diff --git a/Source/OperationObserver.swift b/Source/Observer/OperationObserver.swift similarity index 97% rename from Source/OperationObserver.swift rename to Source/Observer/OperationObserver.swift index ee2b994..8daa3c8 100644 --- a/Source/OperationObserver.swift +++ b/Source/Observer/OperationObserver.swift @@ -12,18 +12,19 @@ import Foundation The protocol that types may implement if they wish to be notified of significant operation lifecycle events. */ + protocol OperationObserver { - + /// Invoked immediately prior to the `Operation`'s `execute()` method. func operationDidStart(operation: Operation) - + /// Invoked when `Operation.produceOperation(_:)` is executed. func operation(operation: Operation, didProduceOperation newOperation: NSOperation) - + /** Invoked as an `Operation` finishes, along with any errors produced during execution (or readiness evaluation). */ func operationDidFinish(operation: Operation, errors: [NSError]) - + } diff --git a/Source/TimeoutObserver.swift b/Source/Observer/TimeoutObserver.swift similarity index 94% rename from Source/TimeoutObserver.swift rename to Source/Observer/TimeoutObserver.swift index 76c0d37..c337bd4 100644 --- a/Source/TimeoutObserver.swift +++ b/Source/Observer/TimeoutObserver.swift @@ -12,21 +12,22 @@ import Foundation `TimeoutObserver` is a way to make an `Operation` automatically time out and cancel after a specified time interval. */ + struct TimeoutObserver: OperationObserver { // MARK: Properties static let timeoutKey = "Timeout" - + private let timeout: NSTimeInterval - + // MARK: Initialization - + init(timeout: NSTimeInterval) { self.timeout = timeout } - + // MARK: OperationObserver - + func operationDidStart(operation: Operation) { // When the operation starts, queue up a block to cause it to time out. let when = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * Double(NSEC_PER_SEC))) @@ -38,7 +39,7 @@ struct TimeoutObserver: OperationObserver { */ if !operation.finished && !operation.cancelled { let error = NSError(code: .ExecutionFailed, userInfo: [ - self.dynamicType.timeoutKey: self.timeout + self.dynamicType.timeoutKey: self.timeout ]) operation.cancelWithError(error) diff --git a/Source/BlockOperation.swift b/Source/Operation/BlockOperation.swift similarity index 96% rename from Source/BlockOperation.swift rename to Source/Operation/BlockOperation.swift index abc4bac..a6c14f6 100644 --- a/Source/BlockOperation.swift +++ b/Source/Operation/BlockOperation.swift @@ -12,9 +12,10 @@ import Foundation typealias OperationBlock = (Void -> Void) -> Void /// A sublcass of `Operation` to execute a closure. + class BlockOperation: Operation { private let block: OperationBlock? - + /** The designated initializer. @@ -28,7 +29,7 @@ class BlockOperation: Operation { self.block = block super.init() } - + /** A convenience initializer to execute a block on the main queue. @@ -38,22 +39,25 @@ class BlockOperation: Operation { after the `mainQueueBlock` is executed. */ convenience init(mainQueueBlock: dispatch_block_t) { - self.init(block: { continuation in + self.init(block: { + continuation in dispatch_async(dispatch_get_main_queue()) { mainQueueBlock() continuation() } }) } - + + override func execute() { guard let block = block else { finish() return } - + block { self.finish() } } + } diff --git a/Source/DelayOperation.swift b/Source/Operation/DelayOperation.swift similarity index 89% rename from Source/DelayOperation.swift rename to Source/Operation/DelayOperation.swift index e366840..3b6572f 100644 --- a/Source/DelayOperation.swift +++ b/Source/Operation/DelayOperation.swift @@ -20,6 +20,7 @@ import Foundation If the interval is negative, or the `NSDate` is in the past, then this operation immediately finishes. */ + class DelayOperation: Operation { // MARK: Types @@ -27,35 +28,35 @@ class DelayOperation: Operation { case Interval(NSTimeInterval) case Date(NSDate) } - + // MARK: Properties - + private let delay: Delay - + // MARK: Initialization - + init(interval: NSTimeInterval) { delay = .Interval(interval) super.init() } - + init(until date: NSDate) { delay = .Date(date) super.init() } - + override func execute() { let interval: NSTimeInterval - + // Figure out how long we should wait for. switch delay { - case .Interval(let theInterval): - interval = theInterval + case .Interval(let theInterval): + interval = theInterval - case .Date(let date): - interval = date.timeIntervalSinceNow + case .Date(let date): + interval = date.timeIntervalSinceNow } - + guard interval > 0 else { finish() return @@ -69,7 +70,7 @@ class DelayOperation: Operation { } } } - + override func cancel() { super.cancel() // Cancelling the operation means we don't want to wait anymore. diff --git a/Source/GroupOperation.swift b/Source/Operation/GroupOperation.swift similarity index 97% rename from Source/GroupOperation.swift rename to Source/Operation/GroupOperation.swift index 88ff78c..939588a 100644 --- a/Source/GroupOperation.swift +++ b/Source/Operation/GroupOperation.swift @@ -21,42 +21,43 @@ import Foundation subsequent operations (still within the outer `GroupOperation`) that will all be executed before the rest of the operations in the initial chain of operations. */ + class GroupOperation: Operation { private let internalQueue = OperationQueue() private let finishingOperation = NSBlockOperation(block: {}) private var aggregatedErrors = [NSError]() - + convenience init(operations: NSOperation...) { self.init(operations: operations) } - + init(operations: [NSOperation]) { super.init() - + internalQueue.suspended = true - + internalQueue.delegate = self for operation in operations { internalQueue.addOperation(operation) } } - + override func cancel() { internalQueue.cancelAllOperations() super.cancel() } - + override func execute() { internalQueue.suspended = false internalQueue.addOperation(finishingOperation) } - + func addOperation(operation: NSOperation) { internalQueue.addOperation(operation) } - + /** Note that some part of execution has produced an error. Errors aggregated through this method will be included in the final array @@ -65,7 +66,7 @@ class GroupOperation: Operation { final func aggregateError(error: NSError) { aggregatedErrors.append(error) } - + func operationDidFinish(operation: NSOperation, withErrors errors: [NSError]) { // For use by subclassers. } @@ -74,7 +75,7 @@ class GroupOperation: Operation { extension GroupOperation: OperationQueueDelegate { final func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) { assert(!finishingOperation.finished && !finishingOperation.executing, "cannot add new operations to a group after the group has completed") - + /* Some operation in this group has produced a new operation to execute. We want to allow that operation to execute before the group completes, @@ -84,15 +85,14 @@ extension GroupOperation: OperationQueueDelegate { finishingOperation.addDependency(operation) } } - + final func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) { aggregatedErrors.extend(errors) - + if operation === finishingOperation { internalQueue.suspended = true finish(aggregatedErrors) - } - else { + } else { operationDidFinish(operation, withErrors: errors) } } diff --git a/Source/LocationOperation.swift b/Source/Operation/LocationOperation.swift similarity index 90% rename from Source/LocationOperation.swift rename to Source/Operation/LocationOperation.swift index 5aee710..6164d0d 100644 --- a/Source/LocationOperation.swift +++ b/Source/Operation/LocationOperation.swift @@ -15,15 +15,16 @@ import CoreLocation prompt for `WhenInUse` location authorization, if the app does not already have it. */ + class LocationOperation: Operation, CLLocationManagerDelegate { // MARK: Properties - + private let accuracy: CLLocationAccuracy private var manager: CLLocationManager? private let handler: CLLocation -> Void - + // MARK: Initialization - + init(accuracy: CLLocationAccuracy, locationHandler: CLLocation -> Void) { self.accuracy = accuracy self.handler = locationHandler @@ -31,7 +32,7 @@ class LocationOperation: Operation, CLLocationManagerDelegate { addCondition(LocationCondition(usage: .WhenInUse)) addCondition(MutuallyExclusive()) } - + override func execute() { dispatch_async(dispatch_get_main_queue()) { /* @@ -42,33 +43,33 @@ class LocationOperation: Operation, CLLocationManagerDelegate { manager.desiredAccuracy = self.accuracy manager.delegate = self manager.startUpdatingLocation() - + self.manager = manager } } - + override func cancel() { dispatch_async(dispatch_get_main_queue()) { self.stopLocationUpdates() super.cancel() } } - + private func stopLocationUpdates() { manager?.stopUpdatingLocation() manager = nil } - + // MARK: CLLocationManagerDelegate - - func locationManager(manager: CLLocationManager, didUpdateLocations locations: [AnyObject]) { - if let locations = locations as? [CLLocation], location = locations.last where location.horizontalAccuracy <= accuracy { + + func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + if let location = locations.last where location.horizontalAccuracy <= accuracy { stopLocationUpdates() handler(location) finish() } } - + func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { stopLocationUpdates() finishWithError(error) diff --git a/Source/Operation.swift b/Source/Operation/Operation.swift similarity index 89% rename from Source/Operation.swift rename to Source/Operation/Operation.swift index dbd689c..aa572a8 100644 --- a/Source/Operation.swift +++ b/Source/Operation/Operation.swift @@ -14,59 +14,60 @@ import Foundation extended readiness requirements, as well as notify many interested parties about interesting operation state changes */ + class Operation: NSOperation { - + // use the KVO mechanism to indicate that changes to "state" affect other properties as well class func keyPathsForValuesAffectingIsReady() -> Set { return ["state"] } - + class func keyPathsForValuesAffectingIsExecuting() -> Set { return ["state"] } - + class func keyPathsForValuesAffectingIsFinished() -> Set { return ["state"] } - + class func keyPathsForValuesAffectingIsCancelled() -> Set { return ["state"] } - + // MARK: State Management - + private enum State: Int, Comparable { /// The initial state of an `Operation`. case Initialized - + /// The `Operation` is ready to begin evaluating conditions. case Pending - + /// The `Operation` is evaluating conditions. case EvaluatingConditions - + /** The `Operation`'s conditions have all been satisfied, and it is ready to execute. */ case Ready - + /// The `Operation` is executing. case Executing - + /** Execution of the `Operation` has finished, but it has not yet notified the queue of this. */ case Finishing - + /// The `Operation` has finished executing. case Finished - + /// The `Operation` has been cancelled. case Cancelled } - + /** Indicates that the Operation can now begin to evaluate readiness conditions, if appropriate. @@ -74,7 +75,7 @@ class Operation: NSOperation { func willEnqueue() { state = .Pending } - + /// Private storage for the `state` property that will be KVO observed. private var _state = State.Initialized @@ -82,44 +83,44 @@ class Operation: NSOperation { get { return _state } - + set(newState) { // Manually fire the KVO notifications for state change, since this is "private". willChangeValueForKey("state") switch (_state, newState) { - case (.Cancelled, _): - break // cannot leave the cancelled state - case (.Finished, _): - break // cannot leave the finished state - default: - assert(_state != newState, "Performing invalid cyclic state transition.") - _state = newState + case (.Cancelled, _ ): + break // cannot leave the cancelled state + case (.Finished, _ ): + break // cannot leave the finished state + default: + assert(_state != newState, "Performing invalid cyclic state transition.") + _state = newState } - + didChangeValueForKey("state") } } - + // Here is where we extend our definition of "readiness". override var ready: Bool { switch state { - case .Pending: - if super.ready { - evaluateConditions() - } - - return false - - case .Ready: - return super.ready - - default: - return false + case .Pending: + if super.ready { + evaluateConditions() + } + + return false + + case .Ready: + return super.ready + + default: + return false } } - + var userInitiated: Bool { get { return qualityOfService == .UserInitiated @@ -131,38 +132,38 @@ class Operation: NSOperation { qualityOfService = newValue ? .UserInitiated : .Default } } - + override var executing: Bool { return state == .Executing } - + override var finished: Bool { return state == .Finished } - + override var cancelled: Bool { return state == .Cancelled } - + private func evaluateConditions() { assert(state == .Pending, "evaluateConditions() was called out-of-order") state = .EvaluatingConditions - - OperationConditionEvaluator.evaluate(conditions, operation: self) { failures in + + OperationConditionEvaluator.evaluate(conditions, operation: self) { + failures in if failures.isEmpty { // If there were no errors, we may proceed. self.state = .Ready - } - else { + } else { self.state = .Cancelled self.finish(failures) } } } - + // MARK: Observers and Conditions - + private(set) var conditions = [OperationCondition]() func addCondition(condition: OperationCondition) { @@ -170,35 +171,35 @@ class Operation: NSOperation { conditions.append(condition) } - + private(set) var observers = [OperationObserver]() - + func addObserver(observer: OperationObserver) { assert(state < .Executing, "Cannot modify observers after execution has begun.") - + observers.append(observer) } - + override func addDependency(operation: NSOperation) { assert(state <= .Executing, "Dependencies cannot be modified after execution has begun.") super.addDependency(operation) } - + // MARK: Execution and Cancellation - + override final func start() { assert(state == .Ready, "This operation must be performed on an operation queue.") state = .Executing - + for observer in observers { observer.operationDidStart(self) } - + execute() } - + /** `execute()` is the entry point of execution for all `Operation` subclasses. If you subclass `Operation` and wish to customize its execution, you would @@ -214,28 +215,28 @@ class Operation: NSOperation { finish() } - + private var _internalErrors = [NSError]() override func cancel() { cancelWithError() } - + func cancelWithError(error: NSError? = nil) { if let error = error { _internalErrors.append(error) } - + state = .Cancelled } - + final func produceOperation(operation: NSOperation) { for observer in observers { observer.operation(self, didProduceOperation: operation) } } - + // MARK: Finishing - + /** Most operations may finish with a single error, if they have one at all. This is a convenience method to simplify calling the actual `finish()` @@ -247,12 +248,11 @@ class Operation: NSOperation { final func finishWithError(error: NSError?) { if let error = error { finish([error]) - } - else { + } else { finish() } } - + /** A private property to ensure we only notify the observers once that the operation has finished. @@ -262,18 +262,18 @@ class Operation: NSOperation { if !hasFinishedAlready { hasFinishedAlready = true state = .Finishing - + let combinedErrors = _internalErrors + errors finished(combinedErrors) - + for observer in observers { observer.operationDidFinish(self, errors: combinedErrors) } - + state = .Finished } } - + /** Subclasses may override `finished(_:)` if they wish to react to the operation finishing with errors. For example, the `LoadModelOperation` implements @@ -283,7 +283,7 @@ class Operation: NSOperation { func finished(errors: [NSError]) { // No op. } - + override func waitUntilFinished() { /* Waiting on operations is almost NEVER the right thing to do. It is @@ -298,7 +298,7 @@ class Operation: NSOperation { */ fatalError("Waiting on operations is an anti-pattern. Remove this ONLY if you're absolutely sure there is No Other Way™.") } - + } // Simple operator functions to simplify the assertions used above. diff --git a/Source/URLSessionTaskOperation.swift b/Source/Operation/URLSessionTaskOperation.swift similarity index 91% rename from Source/URLSessionTaskOperation.swift rename to Source/Operation/URLSessionTaskOperation.swift index b0f9e76..2549fd6 100644 --- a/Source/URLSessionTaskOperation.swift +++ b/Source/Operation/URLSessionTaskOperation.swift @@ -21,32 +21,35 @@ private var URLSessionTaksOperationKVOContext = 0 An example usage of `URLSessionTaskOperation` can be seen in the `DownloadEarthquakesOperation`. */ + class URLSessionTaskOperation: Operation { let task: NSURLSessionTask - + init(task: NSURLSessionTask) { assert(task.state == .Suspended, "Tasks must be suspended.") self.task = task super.init() } - + override func execute() { assert(task.state == .Suspended, "Task was resumed by something other than \(self).") task.addObserver(self, forKeyPath: "state", options: [], context: &URLSessionTaksOperationKVOContext) - + task.resume() } - - override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject : AnyObject]?, context: UnsafeMutablePointer) { - guard context == &URLSessionTaksOperationKVOContext else { return } - + + override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject:AnyObject]?, context: UnsafeMutablePointer) { + guard context == &URLSessionTaksOperationKVOContext else { + return + } + if object === task && keyPath == "state" && task.state == .Completed { task.removeObserver(self, forKeyPath: "state") finish() } } - + override func cancel() { task.cancel() super.cancel() diff --git a/Source/ExclusivityController.swift b/Source/OperationQueue/ExclusivityController.swift similarity index 93% rename from Source/ExclusivityController.swift rename to Source/OperationQueue/ExclusivityController.swift index 5353063..cac53e7 100644 --- a/Source/ExclusivityController.swift +++ b/Source/OperationQueue/ExclusivityController.swift @@ -14,19 +14,20 @@ import Foundation We use a singleton because mutual exclusivity must be enforced across the entire app, regardless of the `OperationQueue` on which an `Operation` was executed. */ + class ExclusivityController { static let sharedExclusivityController = ExclusivityController() - + private let serialQueue = dispatch_queue_create("Operations.ExclusivityController", DISPATCH_QUEUE_SERIAL) - private var operations: [String: [Operation]] = [:] - + private var operations: [String:[Operation]] = [:] + private init() { /* A private initializer effectively prevents any other part of the app from accidentally creating an instance. */ } - + /// Registers an operation as being mutually exclusive func addOperation(operation: Operation, categories: [String]) { /* @@ -40,7 +41,7 @@ class ExclusivityController { } } } - + /// Unregisters an operation from being mutually exclusive. func removeOperation(operation: Operation, categories: [String]) { dispatch_async(serialQueue) { @@ -49,31 +50,31 @@ class ExclusivityController { } } } - - + + // MARK: Operation Management - + private func noqueue_addOperation(operation: Operation, category: String) { var operationsWithThisCategory = operations[category] ?? [] - + if let last = operationsWithThisCategory.last { operation.addDependency(last) } - + operationsWithThisCategory.append(operation) operations[category] = operationsWithThisCategory } - + private func noqueue_removeOperation(operation: Operation, category: String) { let matchingOperations = operations[category] if var operationsWithThisCategory = matchingOperations, - let index = operationsWithThisCategory.indexOf(operation) { + let index = operationsWithThisCategory.indexOf(operation) { operationsWithThisCategory.removeAtIndex(index) operations[category] = operationsWithThisCategory } } - + } diff --git a/Source/OperationQueue.swift b/Source/OperationQueue/OperationQueue.swift similarity index 72% rename from Source/OperationQueue.swift rename to Source/OperationQueue/OperationQueue.swift index 724024e..aef98d0 100644 --- a/Source/OperationQueue.swift +++ b/Source/OperationQueue/OperationQueue.swift @@ -18,8 +18,10 @@ import Foundation For example, `GroupOperation` is the delegate of its own internal `OperationQueue` and uses it to manage dependencies. */ + @objc protocol OperationQueueDelegate: NSObjectProtocol { optional func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) + optional func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) } @@ -31,43 +33,49 @@ import Foundation - Extracting generated dependencies from operation conditions - Setting up dependencies to enforce mutual exclusivity */ + class OperationQueue: NSOperationQueue { weak var delegate: OperationQueueDelegate? - + override func addOperation(operation: NSOperation) { if let op = operation as? Operation { // Set up a `BlockObserver` to invoke the `OperationQueueDelegate` method. let delegate = BlockObserver( - startHandler: nil, - produceHandler: { [weak self] in - self?.addOperation($1) - }, - finishHandler: { [weak self] in - if let q = self { - q.delegate?.operationQueue?(q, operationDidFinish: $0, withErrors: $1) + startHandler: nil, + produceHandler: { + [weak self] in + self?.addOperation($1) + }, + finishHandler: { + [weak self] in + if let q = self { + q.delegate?.operationQueue?(q, operationDidFinish: $0, withErrors: $1) + } } - } ) op.addObserver(delegate) - + // Extract any dependencies needed by this operation. let dependencies = op.conditions.flatMap { $0.dependencyForOperation(op) } - + for dependency in dependencies { op.addDependency(dependency) self.addOperation(dependency) } - + /* With condition dependencies added, we can now see if this needs dependencies to enforce mutual exclusivity. */ - let concurrencyCategories: [String] = op.conditions.flatMap { condition in - if !condition.dynamicType.isMutuallyExclusive { return nil } - + let concurrencyCategories: [String] = op.conditions.flatMap { + condition in + if !condition.dynamicType.isMutuallyExclusive { + return nil + } + return "\(condition.dynamicType)" } @@ -76,49 +84,55 @@ class OperationQueue: NSOperationQueue { let exclusivityController = ExclusivityController.sharedExclusivityController exclusivityController.addOperation(op, categories: concurrencyCategories) - - op.addObserver(BlockObserver { operation, _ in + + op.addObserver(BlockObserver { + operation, _ in exclusivityController.removeOperation(operation, categories: concurrencyCategories) }) } - + /* Indicate to the operation that we've finished our extra work on it and it's now it a state where it can proceed with evaluating conditions, if appropriate. */ op.willEnqueue() - } - else { + } else { /* For regular `NSOperation`s, we'll manually call out to the queue's delegate we don't want to just capture "operation" because that would lead to the operation strongly referencing itself and that's the pure definition of a memory leak. */ - operation.addCompletionBlock { [weak self, weak operation] in - guard let queue = self, let operation = operation else { return } - queue.delegate?.operationQueue?(queue, operationDidFinish: operation, withErrors: []) - } - } - - delegate?.operationQueue?(self, willAddOperation: operation) - super.addOperation(operation) + operation.addCompletionBlock { + [weak self, weak operation] in + guard let queue = self, let operation = operation + +else { + return +} +queue.delegate?.operationQueue?(queue, operationDidFinish: operation, withErrors: []) +} +} + +delegate?.operationQueue?(self, willAddOperation: operation) +super.addOperation(operation) +} + +override func addOperations(operations: [NSOperation], waitUntilFinished wait: Bool) { + /* + The base implementation of this method does not call `addOperation()`, + so we'll call it ourselves. + */ + for operation in operations { + addOperation(operation) } - - override func addOperations(operations: [NSOperation], waitUntilFinished wait: Bool) { - /* - The base implementation of this method does not call `addOperation()`, - so we'll call it ourselves. - */ + + if wait { for operation in operations { - addOperation(operation) - } - - if wait { - for operation in operations { - operation.waitUntilFinished() - } + operation.waitUntilFinished() } } } + +} From a8b68f742c4feb3437a0b36eb06f73b365f046d2 Mon Sep 17 00:00:00 2001 From: PBE Date: Tue, 7 Jul 2015 09:36:46 +0200 Subject: [PATCH 2/6] added NetworkObserver and AlertOperation --- ADVOperation.xcodeproj/project.pbxproj | 8 ++ Source/Observer/NetworkObserver.swift | 120 +++++++++++++++++++++++++ Source/Operation/AlertOperation.swift | 96 ++++++++++++++++++++ 3 files changed, 224 insertions(+) create mode 100644 Source/Observer/NetworkObserver.swift create mode 100644 Source/Operation/AlertOperation.swift diff --git a/ADVOperation.xcodeproj/project.pbxproj b/ADVOperation.xcodeproj/project.pbxproj index 62fa287..3f50c36 100644 --- a/ADVOperation.xcodeproj/project.pbxproj +++ b/ADVOperation.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 134930FC7911FA67B2B4D7DF /* OperationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A84EC920C5908B9EBA0 /* OperationErrors.swift */; }; 134931411BBF6370A978DABC /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134938E6D96135B1DBE590FA /* OperationQueue.swift */; }; + 134931FBCFCFBCF1A36AAB63 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F5C1CF7D0460544720C /* NetworkObserver.swift */; }; 13493201C21925B5246A0246 /* NSOperation+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */; }; 134932BF46092E111600482B /* NegatedCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BA2D24FE404BC15CBF8 /* NegatedCondition.swift */; }; 13493338E30D6F010F759FD2 /* SilentCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BAA7B983B2A9DD70153 /* SilentCondition.swift */; }; @@ -24,6 +25,7 @@ 134938AACE32347FA50E1C6F /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A18E225102B2BB5F75C /* LocationOperation.swift */; }; 134938EC0526BD7445A952EF /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */; }; 13493A38C8C16BE598E04969 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493FC92789F5097A896A73 /* HealthCondition.swift */; }; + 13493A7E694EAFE49CAD2BEC /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493858DE7778C37EADB241 /* AlertOperation.swift */; }; 13493A98CA8E7A43C7FC23A9 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */; }; 13493AE54263F87E42D5B799 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F87EE5CB20941A934F2 /* BlockOperation.swift */; }; 13493B409710FCD8B3596BD6 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */; }; @@ -65,6 +67,7 @@ 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutuallyExclusive.swift; sourceTree = ""; }; 134937EC6979C8C804E07AFA /* PassbookCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassbookCondition.swift; sourceTree = ""; }; 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoCancelledDependencies.swift; sourceTree = ""; }; + 13493858DE7778C37EADB241 /* AlertOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertOperation.swift; sourceTree = ""; }; 134938B6B589E03B1377C6FD /* BlockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockObserver.swift; sourceTree = ""; }; 134938E6D96135B1DBE590FA /* OperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = ""; }; 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSOperation+Operations.swift"; sourceTree = ""; }; @@ -82,6 +85,7 @@ 13493ECC76B568D7691439A3 /* Dictionary+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Dictionary+Operations.swift"; sourceTree = ""; }; 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CalendarCondition.swift; sourceTree = ""; }; 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeoutObserver.swift; sourceTree = ""; }; + 13493F5C1CF7D0460544720C /* NetworkObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = ""; }; 13493F87EE5CB20941A934F2 /* BlockOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockOperation.swift; sourceTree = ""; }; 13493FC92789F5097A896A73 /* HealthCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HealthCondition.swift; sourceTree = ""; }; AA82155D1B2F23FA00A04622 /* ADVOperation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ADVOperation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -131,6 +135,7 @@ 13493A18E225102B2BB5F75C /* LocationOperation.swift */, 13493B673624213866B375D7 /* Operation.swift */, 13493B097F3E57A358999B78 /* URLSessionTaskOperation.swift */, + 13493858DE7778C37EADB241 /* AlertOperation.swift */, ); path = Operation; sourceTree = ""; @@ -141,6 +146,7 @@ 134938B6B589E03B1377C6FD /* BlockObserver.swift */, 13493D60F708E7BFD326F144 /* OperationObserver.swift */, 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */, + 13493F5C1CF7D0460544720C /* NetworkObserver.swift */, ); path = Observer; sourceTree = ""; @@ -341,6 +347,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 134931FBCFCFBCF1A36AAB63 /* NetworkObserver.swift in Sources */, + 13493A7E694EAFE49CAD2BEC /* AlertOperation.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Source/Observer/NetworkObserver.swift b/Source/Observer/NetworkObserver.swift new file mode 100644 index 0000000..7e571d0 --- /dev/null +++ b/Source/Observer/NetworkObserver.swift @@ -0,0 +1,120 @@ +/* +Copyright (C) 2015 Apple Inc. All Rights Reserved. +See LICENSE.txt for this sample’s licensing information + +Abstract: +Contains the code to manage the visibility of the network activity indicator +*/ + +import UIKit + +/** +An `OperationObserver` that will cause the network activity indicator to appear +as long as the `Operation` to which it is attached is executing. +*/ + +struct NetworkObserver: OperationObserver { + + init() { + } + + func operationDidStart(operation: Operation) { + dispatch_async(dispatch_get_main_queue()) { + // increment the network indicator's "retain count" + NetworkIndicatorController.sharedIndicatorController.networkActivityDidStart() + } + } + + func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { + } + + func operationDidFinish(operation: Operation, errors: [NSError]) { + dispatch_async(dispatch_get_main_queue()) { + // Decrement the network indicator's "reference count". + NetworkIndicatorController.sharedIndicatorController.networkActivityDidEnd() + } + } + +} + +/// A singleton to manage a visual "reference count" on the network activity indicator. + +private class NetworkIndicatorController { + // MARK: Properties + + static let sharedIndicatorController = NetworkIndicatorController() + + private var activityCount = 0 + + private var visibilityTimer: Timer? + + // MARK: Methods + + func networkActivityDidStart() { + assert(NSThread.isMainThread(), "Altering network activity indicator state can only be done on the main thread.") + + activityCount++ + + updateIndicatorVisibility() + } + + func networkActivityDidEnd() { + assert(NSThread.isMainThread(), "Altering network activity indicator state can only be done on the main thread.") + + activityCount-- + + updateIndicatorVisibility() + } + + private func updateIndicatorVisibility() { + if activityCount > 0 { + showIndicator() + } else { + /* + To prevent the indicator from flickering on and off, we delay the + hiding of the indicator by one second. This provides the chance + to come in and invalidate the timer before it fires. + */ + visibilityTimer = Timer(interval: 1.0) { + self.hideIndicator() + } + } + } + + private func showIndicator() { + visibilityTimer?.cancel() + visibilityTimer = nil + UIApplication.sharedApplication().networkActivityIndicatorVisible = true + } + + private func hideIndicator() { + visibilityTimer?.cancel() + visibilityTimer = nil + UIApplication.sharedApplication().networkActivityIndicatorVisible = false + } +} + +/// Essentially a cancellable `dispatch_after`. + +class Timer { + // MARK: Properties + + private var isCancelled = false + + // MARK: Initialization + + init(interval: NSTimeInterval, handler: dispatch_block_t) { + let when = dispatch_time(DISPATCH_TIME_NOW, Int64(interval * Double(NSEC_PER_SEC))) + + dispatch_after(when, dispatch_get_main_queue()) { + [weak self] in + if self?.isCancelled != true { + handler() + } + } + } + + func cancel() { + isCancelled = true + } +} \ No newline at end of file diff --git a/Source/Operation/AlertOperation.swift b/Source/Operation/AlertOperation.swift new file mode 100644 index 0000000..c6264c2 --- /dev/null +++ b/Source/Operation/AlertOperation.swift @@ -0,0 +1,96 @@ +// +// AlertOperation.swift +// LMOperations +// +// Created by Phillipp Bertram on 26/06/15. +// Copyright (c) 2015 LMIS AG. All rights reserved. +// + +#if os(iOS) + +import UIKit + +class AlertOperation: Operation { + + private let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .Alert) + private let presentationContext: UIViewController? + + /// title of alert + var title: String? { + get { + return alertController.title + } + + set { + alertController.title = newValue + name = newValue + } + } + + /// message of alert + var message: String? { + get { + return alertController.message + } + + set { + alertController.message = newValue + } + } + + // MARK: Initialization + + init(presentationContext: UIViewController? = nil) { + self.presentationContext = presentationContext ?? UIApplication.sharedApplication().keyWindow?.rootViewController + + super.init() + + addCondition(AlertPresentation()) + + /* + This operation modifies the view controller hierarchy. + Doing this while other such operations are executing can lead to + inconsistencies in UIKit. So, let's make them mutally exclusive. + */ + addCondition(MutuallyExclusive()) + } + + + /** + Adds an action to the alert. + + - parameter title: The Title for the alert. + - parameter style: The Alertstyle. If none if given `.Default` will be used + - parameter handler: The handler that will be invoked. + */ + func addAction(title: String, style: UIAlertActionStyle = .Default, handler: AlertOperation -> Void = { + _ in }) { + let action = UIAlertAction(title: title, style: style) { + [weak self] _ in + if let strongSelf = self { + handler(strongSelf) + } + + self?.finish() + } + + alertController.addAction(action) + } + + + override func execute() { + if let presentationContext = presentationContext { + dispatch_async(dispatch_get_main_queue()) { + if self.alertController.actions.isEmpty { + self.addAction("OK") + } + + presentationContext.presentViewController(self.alertController, animated: true, completion: nil) + } + } else { + finish() + } + } +} + +#endif From 7a53cf9c6db2a7dd2cf5a4d61d3d98c0572e54ea Mon Sep 17 00:00:00 2001 From: PBE Date: Tue, 7 Jul 2015 09:57:18 +0200 Subject: [PATCH 3/6] created demo target; changed access modifiers from source classes and methods --- ADVOperation.xcodeproj/project.pbxproj | 254 +++++++++++++++++- .../AppIcon.appiconset/Contents.json | 38 +++ Demo/Base.lproj/LaunchScreen.storyboard | 49 ++++ Demo/Base.lproj/Main.storyboard | 56 ++++ Demo/Classes/AppDelegate.swift | 22 ++ Demo/Classes/ViewController.swift | 32 +++ Demo/Info.plist | 40 +++ Source/Observer/BlockObserver.swift | 8 +- Source/Observer/NetworkObserver.swift | 9 +- Source/Observer/OperationObserver.swift | 2 +- Source/Observer/TimeoutObserver.swift | 8 +- Source/Operation/AlertOperation.swift | 2 +- Source/Operation/BlockOperation.swift | 2 +- Source/Operation/DelayOperation.swift | 4 +- Source/Operation/GroupOperation.swift | 4 +- Source/Operation/LocationOperation.swift | 8 +- Source/Operation/Operation.swift | 18 +- .../Operation/URLSessionTaskOperation.swift | 6 +- .../ExclusivityController.swift | 2 +- Source/OperationQueue/OperationQueue.swift | 48 ++-- 20 files changed, 542 insertions(+), 70 deletions(-) create mode 100644 Demo/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Demo/Base.lproj/LaunchScreen.storyboard create mode 100644 Demo/Base.lproj/Main.storyboard create mode 100644 Demo/Classes/AppDelegate.swift create mode 100644 Demo/Classes/ViewController.swift create mode 100644 Demo/Info.plist diff --git a/ADVOperation.xcodeproj/project.pbxproj b/ADVOperation.xcodeproj/project.pbxproj index 3f50c36..7f4bc45 100644 --- a/ADVOperation.xcodeproj/project.pbxproj +++ b/ADVOperation.xcodeproj/project.pbxproj @@ -8,13 +8,14 @@ /* Begin PBXBuildFile section */ 134930FC7911FA67B2B4D7DF /* OperationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A84EC920C5908B9EBA0 /* OperationErrors.swift */; }; + 134931087A4BE6A019937AB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 134932E651583716948CE833 /* Assets.xcassets */; }; 134931411BBF6370A978DABC /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134938E6D96135B1DBE590FA /* OperationQueue.swift */; }; - 134931FBCFCFBCF1A36AAB63 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F5C1CF7D0460544720C /* NetworkObserver.swift */; }; 13493201C21925B5246A0246 /* NSOperation+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */; }; 134932BF46092E111600482B /* NegatedCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BA2D24FE404BC15CBF8 /* NegatedCondition.swift */; }; 13493338E30D6F010F759FD2 /* SilentCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BAA7B983B2A9DD70153 /* SilentCondition.swift */; }; 134933B7FB906B44B1607B54 /* NoCancelledDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */; }; 1349341FFC87A62ED60EE2EF /* OperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493D60F708E7BFD326F144 /* OperationObserver.swift */; }; + 1349346F044013C41928C1FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 13493049D3DD2461C2FD577D /* Main.storyboard */; }; 134935195F8C1F85F4302B2C /* PassbookCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134937EC6979C8C804E07AFA /* PassbookCondition.swift */; }; 1349360903CEC2ACDE4CBC91 /* UIUserNotifications+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493011AE350750A6134743 /* UIUserNotifications+Operations.swift */; }; 1349360E87176D613F47500D /* CloudCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932F050B4568B14C8F2D3 /* CloudCondition.swift */; }; @@ -22,11 +23,12 @@ 1349373A64C53B6C57E57A49 /* Dictionary+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ECC76B568D7691439A3 /* Dictionary+Operations.swift */; }; 1349373AE6F86D1C8BC8FB3A /* RemoteNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932EF0A14315328EC6A3A /* RemoteNotificationCondition.swift */; }; 1349375932A0DA51708ACCEE /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A3DA8A5946B70A14674 /* PhotosCondition.swift */; }; + 134937F107AF1DE8A0F4729A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134936F116358B00B7C19D70 /* ViewController.swift */; }; 134938AACE32347FA50E1C6F /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A18E225102B2BB5F75C /* LocationOperation.swift */; }; 134938EC0526BD7445A952EF /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */; }; 13493A38C8C16BE598E04969 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493FC92789F5097A896A73 /* HealthCondition.swift */; }; - 13493A7E694EAFE49CAD2BEC /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493858DE7778C37EADB241 /* AlertOperation.swift */; }; 13493A98CA8E7A43C7FC23A9 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */; }; + 13493AAF2E22246D33908905 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A5F6B2DD167BD0C5DF9 /* AppDelegate.swift */; }; 13493AE54263F87E42D5B799 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F87EE5CB20941A934F2 /* BlockOperation.swift */; }; 13493B409710FCD8B3596BD6 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */; }; 13493BDEBF2CDEED6AD2028A /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493B673624213866B375D7 /* Operation.swift */; }; @@ -34,18 +36,60 @@ 13493C5D50A6087A92FDD508 /* BlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134938B6B589E03B1377C6FD /* BlockObserver.swift */; }; 13493C5FA625EC02B3D0A7A8 /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */; }; 13493CB2964452FAEEE4E28B /* DelayOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493D01646023977702042A /* DelayOperation.swift */; }; + 13493DC402D9C1782CF671E2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1349319B6E547AABDD1F5BCC /* LaunchScreen.storyboard */; }; 13493DF93070CB84CD5A58B9 /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932D5FE660F99325CD4C0 /* GroupOperation.swift */; }; 13493E6507EEA35308B23182 /* UserNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A3B178B50C93EB4A857 /* UserNotificationCondition.swift */; }; 13493ECEA49AE331A40DA484 /* LocationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A642DCBC93D7AFCF84A /* LocationCondition.swift */; }; 13493ED6E7A06C6B9DCE6ACD /* TimeoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */; }; 13493FCD68D71F2EB0BFD4D9 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493405578740A4F865B7EC /* CKContainer+Operations.swift */; }; + 3FBF72F41B4BBA7A00914526 /* ADVOperation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA82155D1B2F23FA00A04622 /* ADVOperation.framework */; }; + 3FBF72F51B4BBA7A00914526 /* ADVOperation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = AA82155D1B2F23FA00A04622 /* ADVOperation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3FBF72F91B4BBB4C00914526 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */; }; + 3FBF72FA1B4BBB4C00914526 /* CloudCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932F050B4568B14C8F2D3 /* CloudCondition.swift */; }; + 3FBF72FB1B4BBB4C00914526 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493FC92789F5097A896A73 /* HealthCondition.swift */; }; + 3FBF72FC1B4BBB4C00914526 /* LocationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A642DCBC93D7AFCF84A /* LocationCondition.swift */; }; + 3FBF72FD1B4BBB4C00914526 /* MutuallyExclusive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */; }; + 3FBF72FE1B4BBB4C00914526 /* NegatedCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BA2D24FE404BC15CBF8 /* NegatedCondition.swift */; }; + 3FBF72FF1B4BBB4C00914526 /* NoCancelledDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */; }; + 3FBF73001B4BBB4C00914526 /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */; }; + 3FBF73011B4BBB4C00914526 /* OperationErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A84EC920C5908B9EBA0 /* OperationErrors.swift */; }; + 3FBF73021B4BBB4C00914526 /* PassbookCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134937EC6979C8C804E07AFA /* PassbookCondition.swift */; }; + 3FBF73031B4BBB4C00914526 /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A3DA8A5946B70A14674 /* PhotosCondition.swift */; }; + 3FBF73041B4BBB4C00914526 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */; }; + 3FBF73051B4BBB4C00914526 /* RemoteNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932EF0A14315328EC6A3A /* RemoteNotificationCondition.swift */; }; + 3FBF73061B4BBB4C00914526 /* SilentCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493BAA7B983B2A9DD70153 /* SilentCondition.swift */; }; + 3FBF73071B4BBB4C00914526 /* UserNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A3B178B50C93EB4A857 /* UserNotificationCondition.swift */; }; + 3FBF73081B4BBB5100914526 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493405578740A4F865B7EC /* CKContainer+Operations.swift */; }; + 3FBF73091B4BBB5100914526 /* Dictionary+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ECC76B568D7691439A3 /* Dictionary+Operations.swift */; }; + 3FBF730A1B4BBB5100914526 /* NSOperation+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */; }; + 3FBF730B1B4BBB5100914526 /* UIUserNotifications+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493011AE350750A6134743 /* UIUserNotifications+Operations.swift */; }; + 3FBF730F1B4BBB5900914526 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F5C1CF7D0460544720C /* NetworkObserver.swift */; }; + 3FBF73101B4BBB5900914526 /* BlockObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134938B6B589E03B1377C6FD /* BlockObserver.swift */; }; + 3FBF73111B4BBB5900914526 /* OperationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493D60F708E7BFD326F144 /* OperationObserver.swift */; }; + 3FBF73121B4BBB5900914526 /* TimeoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */; }; + 3FBF73131B4BBB5900914526 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F5C1CF7D0460544720C /* NetworkObserver.swift */; }; + 3FBF73141B4BBB5E00914526 /* ExclusivityController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932428BA712B0F210BAA5 /* ExclusivityController.swift */; }; + 3FBF73151B4BBB5E00914526 /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134938E6D96135B1DBE590FA /* OperationQueue.swift */; }; + 3FBF731C1B4BBB6300914526 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F87EE5CB20941A934F2 /* BlockOperation.swift */; }; + 3FBF731D1B4BBB6300914526 /* DelayOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493D01646023977702042A /* DelayOperation.swift */; }; + 3FBF731E1B4BBB6300914526 /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932D5FE660F99325CD4C0 /* GroupOperation.swift */; }; + 3FBF731F1B4BBB6300914526 /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A18E225102B2BB5F75C /* LocationOperation.swift */; }; + 3FBF73201B4BBB6300914526 /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493B673624213866B375D7 /* Operation.swift */; }; + 3FBF73211B4BBB6300914526 /* URLSessionTaskOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493B097F3E57A358999B78 /* URLSessionTaskOperation.swift */; }; + 3FBF73221B4BBB6300914526 /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493858DE7778C37EADB241 /* AlertOperation.swift */; }; + 3FBF73231B4BBB6300914526 /* AlertOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493858DE7778C37EADB241 /* AlertOperation.swift */; }; AA8215681B2F23FA00A04622 /* ADVOperation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA82155D1B2F23FA00A04622 /* ADVOperation.framework */; }; - AA8215851B2F260200A04622 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AA8215831B2F260200A04622 /* Info.plist */; }; - AA8215891B2F417E00A04622 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = AA8215881B2F417E00A04622 /* Info.plist */; }; AA9A92321B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA9A92311B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 3FBF72F61B4BBA7A00914526 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = AA8215541B2F23FA00A04622 /* Project object */; + proxyType = 1; + remoteGlobalIDString = AA82155C1B2F23FA00A04622; + remoteInfo = ADVOperation; + }; AA8215691B2F23FA00A04622 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = AA8215541B2F23FA00A04622 /* Project object */; @@ -55,25 +99,45 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 3FBF72F81B4BBA7B00914526 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3FBF72F51B4BBA7A00914526 /* ADVOperation.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 13493011AE350750A6134743 /* UIUserNotifications+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIUserNotifications+Operations.swift"; sourceTree = ""; }; 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityCondition.swift; sourceTree = ""; }; + 134931F7658131E92511027A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationCondition.swift; sourceTree = ""; }; 134932428BA712B0F210BAA5 /* ExclusivityController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExclusivityController.swift; sourceTree = ""; }; 134932D5FE660F99325CD4C0 /* GroupOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupOperation.swift; sourceTree = ""; }; + 134932E651583716948CE833 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 134932EF0A14315328EC6A3A /* RemoteNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteNotificationCondition.swift; sourceTree = ""; }; 134932F050B4568B14C8F2D3 /* CloudCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudCondition.swift; sourceTree = ""; }; 13493405578740A4F865B7EC /* CKContainer+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CKContainer+Operations.swift"; sourceTree = ""; }; 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutuallyExclusive.swift; sourceTree = ""; }; + 134936F116358B00B7C19D70 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 1349378344214C06DCA78DC4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 134937EC6979C8C804E07AFA /* PassbookCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassbookCondition.swift; sourceTree = ""; }; 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoCancelledDependencies.swift; sourceTree = ""; }; 13493858DE7778C37EADB241 /* AlertOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertOperation.swift; sourceTree = ""; }; 134938B6B589E03B1377C6FD /* BlockObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockObserver.swift; sourceTree = ""; }; 134938E6D96135B1DBE590FA /* OperationQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = ""; }; + 13493940730F0891BFF91B91 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.info; path = Info.plist; sourceTree = ""; }; 134939BD12D892DAF26E1F5D /* NSOperation+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSOperation+Operations.swift"; sourceTree = ""; }; 13493A18E225102B2BB5F75C /* LocationOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationOperation.swift; sourceTree = ""; }; 13493A3B178B50C93EB4A857 /* UserNotificationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotificationCondition.swift; sourceTree = ""; }; 13493A3DA8A5946B70A14674 /* PhotosCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotosCondition.swift; sourceTree = ""; }; + 13493A5F6B2DD167BD0C5DF9 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 13493A642DCBC93D7AFCF84A /* LocationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationCondition.swift; sourceTree = ""; }; 13493A84EC920C5908B9EBA0 /* OperationErrors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationErrors.swift; sourceTree = ""; }; 13493B097F3E57A358999B78 /* URLSessionTaskOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionTaskOperation.swift; sourceTree = ""; }; @@ -88,6 +152,7 @@ 13493F5C1CF7D0460544720C /* NetworkObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = ""; }; 13493F87EE5CB20941A934F2 /* BlockOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockOperation.swift; sourceTree = ""; }; 13493FC92789F5097A896A73 /* HealthCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HealthCondition.swift; sourceTree = ""; }; + 3FBF72E21B4BBA4C00914526 /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; AA82155D1B2F23FA00A04622 /* ADVOperation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ADVOperation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; AA8215671B2F23FA00A04622 /* ADVOperationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ADVOperationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; AA8215831B2F260200A04622 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -97,6 +162,14 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 3FBF72DF1B4BBA4C00914526 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3FBF72F41B4BBA7A00914526 /* ADVOperation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AA8215591B2F23FA00A04622 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -140,6 +213,26 @@ path = Operation; sourceTree = ""; }; + 1349341AE04A878C559A67CF /* Ressources */ = { + isa = PBXGroup; + children = ( + 1349319B6E547AABDD1F5BCC /* LaunchScreen.storyboard */, + 13493049D3DD2461C2FD577D /* Main.storyboard */, + 134932E651583716948CE833 /* Assets.xcassets */, + 13493940730F0891BFF91B91 /* Info.plist */, + ); + name = Ressources; + sourceTree = ""; + }; + 13493992B6D1BB9D1AA5D60E /* Classes */ = { + isa = PBXGroup; + children = ( + 13493A5F6B2DD167BD0C5DF9 /* AppDelegate.swift */, + 134936F116358B00B7C19D70 /* ViewController.swift */, + ); + path = Classes; + sourceTree = ""; + }; 13493A1CA8F733B3147721E4 /* Observer */ = { isa = PBXGroup; children = ( @@ -182,9 +275,19 @@ path = OperationQueue; sourceTree = ""; }; + 3FBF72E31B4BBA4C00914526 /* Demo */ = { + isa = PBXGroup; + children = ( + 1349341AE04A878C559A67CF /* Ressources */, + 13493992B6D1BB9D1AA5D60E /* Classes */, + ); + path = Demo; + sourceTree = ""; + }; AA8215531B2F23FA00A04622 = { isa = PBXGroup; children = ( + 3FBF72E31B4BBA4C00914526 /* Demo */, AA8215861B2F417E00A04622 /* Source */, AA8215811B2F260200A04622 /* Tests */, AA82155E1B2F23FA00A04622 /* Products */, @@ -196,6 +299,7 @@ children = ( AA82155D1B2F23FA00A04622 /* ADVOperation.framework */, AA8215671B2F23FA00A04622 /* ADVOperationTests.xctest */, + 3FBF72E21B4BBA4C00914526 /* Demo.app */, ); name = Products; sourceTree = ""; @@ -252,6 +356,25 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 3FBF72E11B4BBA4C00914526 /* Demo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3FBF72F31B4BBA4C00914526 /* Build configuration list for PBXNativeTarget "Demo" */; + buildPhases = ( + 3FBF72DE1B4BBA4C00914526 /* Sources */, + 3FBF72DF1B4BBA4C00914526 /* Frameworks */, + 3FBF72E01B4BBA4C00914526 /* Resources */, + 3FBF72F81B4BBA7B00914526 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 3FBF72F71B4BBA7A00914526 /* PBXTargetDependency */, + ); + name = Demo; + productName = Demo; + productReference = 3FBF72E21B4BBA4C00914526 /* Demo.app */; + productType = "com.apple.product-type.application"; + }; AA82155C1B2F23FA00A04622 /* ADVOperation */ = { isa = PBXNativeTarget; buildConfigurationList = AA8215711B2F23FA00A04622 /* Build configuration list for PBXNativeTarget "ADVOperation" */; @@ -297,6 +420,9 @@ LastUpgradeCheck = 0700; ORGANIZATIONNAME = "Advanced Operation"; TargetAttributes = { + 3FBF72E11B4BBA4C00914526 = { + CreatedOnToolsVersion = 7.0; + }; AA82155C1B2F23FA00A04622 = { CreatedOnToolsVersion = 7.0; }; @@ -311,12 +437,14 @@ hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = AA8215531B2F23FA00A04622; productRefGroup = AA82155E1B2F23FA00A04622 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( + 3FBF72E11B4BBA4C00914526 /* Demo */, AA82155C1B2F23FA00A04622 /* ADVOperation */, AA8215661B2F23FA00A04622 /* ADVOperationTests */, ); @@ -324,6 +452,16 @@ /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 3FBF72E01B4BBA4C00914526 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13493DC402D9C1782CF671E2 /* LaunchScreen.storyboard in Resources */, + 1349346F044013C41928C1FC /* Main.storyboard in Resources */, + 134931087A4BE6A019937AB4 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AA82155B1B2F23FA00A04622 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -335,20 +473,57 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - AA8215891B2F417E00A04622 /* Info.plist in Resources */, - AA8215851B2F260200A04622 /* Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 3FBF72DE1B4BBA4C00914526 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13493AAF2E22246D33908905 /* AppDelegate.swift in Sources */, + 134937F107AF1DE8A0F4729A /* ViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; AA8215581B2F23FA00A04622 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 134931FBCFCFBCF1A36AAB63 /* NetworkObserver.swift in Sources */, - 13493A7E694EAFE49CAD2BEC /* AlertOperation.swift in Sources */, + 3FBF72FE1B4BBB4C00914526 /* NegatedCondition.swift in Sources */, + 3FBF73011B4BBB4C00914526 /* OperationErrors.swift in Sources */, + 3FBF731D1B4BBB6300914526 /* DelayOperation.swift in Sources */, + 3FBF73091B4BBB5100914526 /* Dictionary+Operations.swift in Sources */, + 3FBF73151B4BBB5E00914526 /* OperationQueue.swift in Sources */, + 3FBF730B1B4BBB5100914526 /* UIUserNotifications+Operations.swift in Sources */, + 3FBF73051B4BBB4C00914526 /* RemoteNotificationCondition.swift in Sources */, + 3FBF73121B4BBB5900914526 /* TimeoutObserver.swift in Sources */, + 3FBF73071B4BBB4C00914526 /* UserNotificationCondition.swift in Sources */, + 3FBF73211B4BBB6300914526 /* URLSessionTaskOperation.swift in Sources */, + 3FBF72FD1B4BBB4C00914526 /* MutuallyExclusive.swift in Sources */, + 3FBF72FF1B4BBB4C00914526 /* NoCancelledDependencies.swift in Sources */, + 3FBF73101B4BBB5900914526 /* BlockObserver.swift in Sources */, + 3FBF73131B4BBB5900914526 /* NetworkObserver.swift in Sources */, + 3FBF73061B4BBB4C00914526 /* SilentCondition.swift in Sources */, + 3FBF72FA1B4BBB4C00914526 /* CloudCondition.swift in Sources */, + 3FBF72FC1B4BBB4C00914526 /* LocationCondition.swift in Sources */, + 3FBF73021B4BBB4C00914526 /* PassbookCondition.swift in Sources */, + 3FBF731F1B4BBB6300914526 /* LocationOperation.swift in Sources */, + 3FBF730A1B4BBB5100914526 /* NSOperation+Operations.swift in Sources */, + 3FBF73041B4BBB4C00914526 /* ReachabilityCondition.swift in Sources */, + 3FBF72F91B4BBB4C00914526 /* CalendarCondition.swift in Sources */, + 3FBF73031B4BBB4C00914526 /* PhotosCondition.swift in Sources */, + 3FBF72FB1B4BBB4C00914526 /* HealthCondition.swift in Sources */, + 3FBF73221B4BBB6300914526 /* AlertOperation.swift in Sources */, + 3FBF73201B4BBB6300914526 /* Operation.swift in Sources */, + 3FBF73001B4BBB4C00914526 /* OperationCondition.swift in Sources */, + 3FBF73081B4BBB5100914526 /* CKContainer+Operations.swift in Sources */, + 3FBF731C1B4BBB6300914526 /* BlockOperation.swift in Sources */, + 3FBF731E1B4BBB6300914526 /* GroupOperation.swift in Sources */, + 3FBF73111B4BBB5900914526 /* OperationObserver.swift in Sources */, + 3FBF73141B4BBB5E00914526 /* ExclusivityController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -358,6 +533,7 @@ files = ( AA9A92321B301FE8003CF5D2 /* URLSessionTaskOperationTests.swift in Sources */, 13493B409710FCD8B3596BD6 /* CalendarCondition.swift in Sources */, + 3FBF730F1B4BBB5900914526 /* NetworkObserver.swift in Sources */, 1349360E87176D613F47500D /* CloudCondition.swift in Sources */, 13493A38C8C16BE598E04969 /* HealthCondition.swift in Sources */, 13493ECEA49AE331A40DA484 /* LocationCondition.swift in Sources */, @@ -375,6 +551,7 @@ 13493FCD68D71F2EB0BFD4D9 /* CKContainer+Operations.swift in Sources */, 1349373A64C53B6C57E57A49 /* Dictionary+Operations.swift in Sources */, 13493201C21925B5246A0246 /* NSOperation+Operations.swift in Sources */, + 3FBF73231B4BBB6300914526 /* AlertOperation.swift in Sources */, 1349360903CEC2ACDE4CBC91 /* UIUserNotifications+Operations.swift in Sources */, 13493C5D50A6087A92FDD508 /* BlockObserver.swift in Sources */, 1349341FFC87A62ED60EE2EF /* OperationObserver.swift in Sources */, @@ -393,6 +570,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 3FBF72F71B4BBA7A00914526 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = AA82155C1B2F23FA00A04622 /* ADVOperation */; + targetProxy = 3FBF72F61B4BBA7A00914526 /* PBXContainerItemProxy */; + }; AA82156A1B2F23FA00A04622 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = AA82155C1B2F23FA00A04622 /* ADVOperation */; @@ -400,7 +582,50 @@ }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + 13493049D3DD2461C2FD577D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1349378344214C06DCA78DC4 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 1349319B6E547AABDD1F5BCC /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 134931F7658131E92511027A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ + 3FBF72F11B4BBA4C00914526 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; + INFOPLIST_FILE = Demo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = de.lmis.Demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 3FBF72F21B4BBA4C00914526 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; + INFOPLIST_FILE = Demo/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = de.lmis.Demo; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; AA82156F1B2F23FA00A04622 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -438,7 +663,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -480,7 +705,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -545,6 +770,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 3FBF72F31B4BBA4C00914526 /* Build configuration list for PBXNativeTarget "Demo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3FBF72F11B4BBA4C00914526 /* Debug */, + 3FBF72F21B4BBA4C00914526 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; AA8215571B2F23FA00A04622 /* Build configuration list for PBXProject "ADVOperation" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json b/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..118c98f --- /dev/null +++ b/Demo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Demo/Base.lproj/LaunchScreen.storyboard b/Demo/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..cd5aa4d --- /dev/null +++ b/Demo/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/Base.lproj/Main.storyboard b/Demo/Base.lproj/Main.storyboard new file mode 100644 index 0000000..37195ab --- /dev/null +++ b/Demo/Base.lproj/Main.storyboard @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Demo/Classes/AppDelegate.swift b/Demo/Classes/AppDelegate.swift new file mode 100644 index 0000000..d8290f5 --- /dev/null +++ b/Demo/Classes/AppDelegate.swift @@ -0,0 +1,22 @@ +// +// AppDelegate.swift +// Demo +// +// Created by Phillipp (LMIS) on 07/07/15. +// Copyright © 2015 Advanced Operation. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject:AnyObject]?) -> Bool { + // Override point for customization after application launch. + return true + } + +} + diff --git a/Demo/Classes/ViewController.swift b/Demo/Classes/ViewController.swift new file mode 100644 index 0000000..61c4727 --- /dev/null +++ b/Demo/Classes/ViewController.swift @@ -0,0 +1,32 @@ +// +// ViewController.swift +// Demo +// +// Created by Phillipp on 07/07/15. +// Copyright © 2015 Advanced Operation. All rights reserved. +// + +import UIKit +import ADVOperation + +class ViewController: UITableViewController { + + let operationQueue = OperationQueue() + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + + + + + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + +} + diff --git a/Demo/Info.plist b/Demo/Info.plist new file mode 100644 index 0000000..9cced26 --- /dev/null +++ b/Demo/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + DEV + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Source/Observer/BlockObserver.swift b/Source/Observer/BlockObserver.swift index d563bbe..6ecbbc3 100644 --- a/Source/Observer/BlockObserver.swift +++ b/Source/Observer/BlockObserver.swift @@ -13,7 +13,7 @@ import Foundation in an `Operation`'s lifecycle. */ -struct BlockObserver: OperationObserver { +public struct BlockObserver: OperationObserver { // MARK: Properties private let startHandler: (Operation -> Void)? @@ -28,15 +28,15 @@ struct BlockObserver: OperationObserver { // MARK: OperationObserver - func operationDidStart(operation: Operation) { + public func operationDidStart(operation: Operation) { startHandler?(operation) } - func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { + public func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { produceHandler?(operation, newOperation) } - func operationDidFinish(operation: Operation, errors: [NSError]) { + public func operationDidFinish(operation: Operation, errors: [NSError]) { finishHandler?(operation, errors) } } diff --git a/Source/Observer/NetworkObserver.swift b/Source/Observer/NetworkObserver.swift index 7e571d0..c0cdbb7 100644 --- a/Source/Observer/NetworkObserver.swift +++ b/Source/Observer/NetworkObserver.swift @@ -13,22 +13,23 @@ An `OperationObserver` that will cause the network activity indicator to appear as long as the `Operation` to which it is attached is executing. */ -struct NetworkObserver: OperationObserver { +public struct NetworkObserver: OperationObserver { init() { } - func operationDidStart(operation: Operation) { + public func operationDidStart(operation: Operation) { dispatch_async(dispatch_get_main_queue()) { // increment the network indicator's "retain count" NetworkIndicatorController.sharedIndicatorController.networkActivityDidStart() } } - func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { + public func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { + } - func operationDidFinish(operation: Operation, errors: [NSError]) { + public func operationDidFinish(operation: Operation, errors: [NSError]) { dispatch_async(dispatch_get_main_queue()) { // Decrement the network indicator's "reference count". NetworkIndicatorController.sharedIndicatorController.networkActivityDidEnd() diff --git a/Source/Observer/OperationObserver.swift b/Source/Observer/OperationObserver.swift index 8daa3c8..b8cbcec 100644 --- a/Source/Observer/OperationObserver.swift +++ b/Source/Observer/OperationObserver.swift @@ -13,7 +13,7 @@ import Foundation operation lifecycle events. */ -protocol OperationObserver { +public protocol OperationObserver { /// Invoked immediately prior to the `Operation`'s `execute()` method. func operationDidStart(operation: Operation) diff --git a/Source/Observer/TimeoutObserver.swift b/Source/Observer/TimeoutObserver.swift index c337bd4..b203a90 100644 --- a/Source/Observer/TimeoutObserver.swift +++ b/Source/Observer/TimeoutObserver.swift @@ -13,7 +13,7 @@ import Foundation cancel after a specified time interval. */ -struct TimeoutObserver: OperationObserver { +public struct TimeoutObserver: OperationObserver { // MARK: Properties static let timeoutKey = "Timeout" @@ -28,7 +28,7 @@ struct TimeoutObserver: OperationObserver { // MARK: OperationObserver - func operationDidStart(operation: Operation) { + public func operationDidStart(operation: Operation) { // When the operation starts, queue up a block to cause it to time out. let when = dispatch_time(DISPATCH_TIME_NOW, Int64(timeout * Double(NSEC_PER_SEC))) @@ -47,11 +47,11 @@ struct TimeoutObserver: OperationObserver { } } - func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { + public func operation(operation: Operation, didProduceOperation newOperation: NSOperation) { // No op. } - func operationDidFinish(operation: Operation, errors: [NSError]) { + public func operationDidFinish(operation: Operation, errors: [NSError]) { // No op. } } diff --git a/Source/Operation/AlertOperation.swift b/Source/Operation/AlertOperation.swift index c6264c2..2660d2a 100644 --- a/Source/Operation/AlertOperation.swift +++ b/Source/Operation/AlertOperation.swift @@ -10,7 +10,7 @@ import UIKit -class AlertOperation: Operation { +public class AlertOperation: Operation { private let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .Alert) private let presentationContext: UIViewController? diff --git a/Source/Operation/BlockOperation.swift b/Source/Operation/BlockOperation.swift index a6c14f6..b201b19 100644 --- a/Source/Operation/BlockOperation.swift +++ b/Source/Operation/BlockOperation.swift @@ -13,7 +13,7 @@ typealias OperationBlock = (Void -> Void) -> Void /// A sublcass of `Operation` to execute a closure. -class BlockOperation: Operation { +public class BlockOperation: Operation { private let block: OperationBlock? /** diff --git a/Source/Operation/DelayOperation.swift b/Source/Operation/DelayOperation.swift index 3b6572f..9933821 100644 --- a/Source/Operation/DelayOperation.swift +++ b/Source/Operation/DelayOperation.swift @@ -21,7 +21,7 @@ import Foundation immediately finishes. */ -class DelayOperation: Operation { +public class DelayOperation: Operation { // MARK: Types private enum Delay { @@ -71,7 +71,7 @@ class DelayOperation: Operation { } } - override func cancel() { + override public func cancel() { super.cancel() // Cancelling the operation means we don't want to wait anymore. self.finish() diff --git a/Source/Operation/GroupOperation.swift b/Source/Operation/GroupOperation.swift index 939588a..f797dc6 100644 --- a/Source/Operation/GroupOperation.swift +++ b/Source/Operation/GroupOperation.swift @@ -22,7 +22,7 @@ import Foundation be executed before the rest of the operations in the initial chain of operations. */ -class GroupOperation: Operation { +public class GroupOperation: Operation { private let internalQueue = OperationQueue() private let finishingOperation = NSBlockOperation(block: {}) @@ -44,7 +44,7 @@ class GroupOperation: Operation { } } - override func cancel() { + override public func cancel() { internalQueue.cancelAllOperations() super.cancel() } diff --git a/Source/Operation/LocationOperation.swift b/Source/Operation/LocationOperation.swift index 6164d0d..d536ada 100644 --- a/Source/Operation/LocationOperation.swift +++ b/Source/Operation/LocationOperation.swift @@ -16,7 +16,7 @@ import CoreLocation have it. */ -class LocationOperation: Operation, CLLocationManagerDelegate { +public class LocationOperation: Operation, CLLocationManagerDelegate { // MARK: Properties private let accuracy: CLLocationAccuracy @@ -48,7 +48,7 @@ class LocationOperation: Operation, CLLocationManagerDelegate { } } - override func cancel() { + override public func cancel() { dispatch_async(dispatch_get_main_queue()) { self.stopLocationUpdates() super.cancel() @@ -62,7 +62,7 @@ class LocationOperation: Operation, CLLocationManagerDelegate { // MARK: CLLocationManagerDelegate - func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + public func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { if let location = locations.last where location.horizontalAccuracy <= accuracy { stopLocationUpdates() handler(location) @@ -70,7 +70,7 @@ class LocationOperation: Operation, CLLocationManagerDelegate { } } - func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { + public func locationManager(manager: CLLocationManager, didFailWithError error: NSError) { stopLocationUpdates() finishWithError(error) } diff --git a/Source/Operation/Operation.swift b/Source/Operation/Operation.swift index aa572a8..bc4825e 100644 --- a/Source/Operation/Operation.swift +++ b/Source/Operation/Operation.swift @@ -15,7 +15,7 @@ import Foundation about interesting operation state changes */ -class Operation: NSOperation { +public class Operation: NSOperation { // use the KVO mechanism to indicate that changes to "state" affect other properties as well class func keyPathsForValuesAffectingIsReady() -> Set { @@ -104,7 +104,7 @@ class Operation: NSOperation { } // Here is where we extend our definition of "readiness". - override var ready: Bool { + override public var ready: Bool { switch state { case .Pending: if super.ready { @@ -133,15 +133,15 @@ class Operation: NSOperation { } } - override var executing: Bool { + override public var executing: Bool { return state == .Executing } - override var finished: Bool { + override public var finished: Bool { return state == .Finished } - override var cancelled: Bool { + override public var cancelled: Bool { return state == .Cancelled } @@ -180,7 +180,7 @@ class Operation: NSOperation { observers.append(observer) } - override func addDependency(operation: NSOperation) { + override public func addDependency(operation: NSOperation) { assert(state <= .Executing, "Dependencies cannot be modified after execution has begun.") super.addDependency(operation) @@ -188,7 +188,7 @@ class Operation: NSOperation { // MARK: Execution and Cancellation - override final func start() { + override public final func start() { assert(state == .Ready, "This operation must be performed on an operation queue.") state = .Executing @@ -217,7 +217,7 @@ class Operation: NSOperation { } private var _internalErrors = [NSError]() - override func cancel() { + override public func cancel() { cancelWithError() } @@ -284,7 +284,7 @@ class Operation: NSOperation { // No op. } - override func waitUntilFinished() { + override public func waitUntilFinished() { /* Waiting on operations is almost NEVER the right thing to do. It is usually superior to use proper locking constructs, such as `dispatch_semaphore_t` diff --git a/Source/Operation/URLSessionTaskOperation.swift b/Source/Operation/URLSessionTaskOperation.swift index 2549fd6..915a1b8 100644 --- a/Source/Operation/URLSessionTaskOperation.swift +++ b/Source/Operation/URLSessionTaskOperation.swift @@ -22,7 +22,7 @@ private var URLSessionTaksOperationKVOContext = 0 An example usage of `URLSessionTaskOperation` can be seen in the `DownloadEarthquakesOperation`. */ -class URLSessionTaskOperation: Operation { +public class URLSessionTaskOperation: Operation { let task: NSURLSessionTask init(task: NSURLSessionTask) { @@ -39,7 +39,7 @@ class URLSessionTaskOperation: Operation { task.resume() } - override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject:AnyObject]?, context: UnsafeMutablePointer) { + override public func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject:AnyObject]?, context: UnsafeMutablePointer) { guard context == &URLSessionTaksOperationKVOContext else { return } @@ -50,7 +50,7 @@ class URLSessionTaskOperation: Operation { } } - override func cancel() { + override public func cancel() { task.cancel() super.cancel() } diff --git a/Source/OperationQueue/ExclusivityController.swift b/Source/OperationQueue/ExclusivityController.swift index cac53e7..6be2142 100644 --- a/Source/OperationQueue/ExclusivityController.swift +++ b/Source/OperationQueue/ExclusivityController.swift @@ -15,7 +15,7 @@ import Foundation app, regardless of the `OperationQueue` on which an `Operation` was executed. */ -class ExclusivityController { +public class ExclusivityController { static let sharedExclusivityController = ExclusivityController() private let serialQueue = dispatch_queue_create("Operations.ExclusivityController", DISPATCH_QUEUE_SERIAL) diff --git a/Source/OperationQueue/OperationQueue.swift b/Source/OperationQueue/OperationQueue.swift index aef98d0..f001006 100644 --- a/Source/OperationQueue/OperationQueue.swift +++ b/Source/OperationQueue/OperationQueue.swift @@ -34,11 +34,13 @@ import Foundation - Setting up dependencies to enforce mutual exclusivity */ -class OperationQueue: NSOperationQueue { +public class OperationQueue: NSOperationQueue { weak var delegate: OperationQueueDelegate? - override func addOperation(operation: NSOperation) { + override public func addOperation(operation: NSOperation) { + if let op = operation as? Operation { + // Set up a `BlockObserver` to invoke the `OperationQueueDelegate` method. let delegate = BlockObserver( startHandler: nil, @@ -106,33 +108,31 @@ class OperationQueue: NSOperationQueue { */ operation.addCompletionBlock { [weak self, weak operation] in - guard let queue = self, let operation = operation - -else { - return -} -queue.delegate?.operationQueue?(queue, operationDidFinish: operation, withErrors: []) -} -} - -delegate?.operationQueue?(self, willAddOperation: operation) -super.addOperation(operation) -} + guard let queue = self, let operation = operation else { + return + } + queue.delegate?.operationQueue?(queue, operationDidFinish: operation, withErrors: []) + } + } -override func addOperations(operations: [NSOperation], waitUntilFinished wait: Bool) { - /* - The base implementation of this method does not call `addOperation()`, - so we'll call it ourselves. - */ - for operation in operations { - addOperation(operation) + delegate?.operationQueue?(self, willAddOperation: operation) + super.addOperation(operation) } - if wait { + override public func addOperations(operations: [NSOperation], waitUntilFinished wait: Bool) { + /* + The base implementation of this method does not call `addOperation()`, + so we'll call it ourselves. + */ for operation in operations { - operation.waitUntilFinished() + addOperation(operation) + } + + if wait { + for operation in operations { + operation.waitUntilFinished() + } } } -} } From caf58c2711efd89964c18a86d57030d07805a8b8 Mon Sep 17 00:00:00 2001 From: PBE Date: Tue, 7 Jul 2015 12:54:05 +0200 Subject: [PATCH 4/6] implemented simple demo; added NSAppTransportSecurity to info.plist; updated access modifier from api, --- ADVOperation.xcodeproj/project.pbxproj | 28 ++++- Demo/Base.lproj/Main.storyboard | 8 +- .../Operations/HTTPBinNetworkOperation.swift | 113 ++++++++++++++++++ Demo/Classes/ViewController.swift | 32 ----- .../ViewControllers/ViewController.swift | 76 ++++++++++++ Demo/Info.plist | 5 + Source/Conditions/OperationCondition.swift | 13 +- Source/Conditions/ReachabilityCondition.swift | 14 +-- Source/Observer/NetworkObserver.swift | 2 +- Source/Operation/AlertOperation.swift | 10 +- Source/Operation/BlockOperation.swift | 2 +- Source/Operation/DelayOperation.swift | 2 +- Source/Operation/GroupOperation.swift | 16 +-- Source/Operation/LocationOperation.swift | 2 +- Source/Operation/Operation.swift | 16 +-- .../Operation/URLSessionTaskOperation.swift | 7 +- Source/OperationQueue/OperationQueue.swift | 5 +- 17 files changed, 270 insertions(+), 81 deletions(-) create mode 100644 Demo/Classes/Operations/HTTPBinNetworkOperation.swift delete mode 100644 Demo/Classes/ViewController.swift create mode 100644 Demo/Classes/ViewControllers/ViewController.swift diff --git a/ADVOperation.xcodeproj/project.pbxproj b/ADVOperation.xcodeproj/project.pbxproj index 7f4bc45..98d8856 100644 --- a/ADVOperation.xcodeproj/project.pbxproj +++ b/ADVOperation.xcodeproj/project.pbxproj @@ -23,12 +23,12 @@ 1349373A64C53B6C57E57A49 /* Dictionary+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ECC76B568D7691439A3 /* Dictionary+Operations.swift */; }; 1349373AE6F86D1C8BC8FB3A /* RemoteNotificationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932EF0A14315328EC6A3A /* RemoteNotificationCondition.swift */; }; 1349375932A0DA51708ACCEE /* PhotosCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A3DA8A5946B70A14674 /* PhotosCondition.swift */; }; - 134937F107AF1DE8A0F4729A /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134936F116358B00B7C19D70 /* ViewController.swift */; }; 134938AACE32347FA50E1C6F /* LocationOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A18E225102B2BB5F75C /* LocationOperation.swift */; }; 134938EC0526BD7445A952EF /* OperationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */; }; 13493A38C8C16BE598E04969 /* HealthCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493FC92789F5097A896A73 /* HealthCondition.swift */; }; 13493A98CA8E7A43C7FC23A9 /* ReachabilityCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */; }; 13493AAF2E22246D33908905 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A5F6B2DD167BD0C5DF9 /* AppDelegate.swift */; }; + 13493ACEE507EF3D8B2A3676 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134936FFFC75FF2FD1D19228 /* ViewController.swift */; }; 13493AE54263F87E42D5B799 /* BlockOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493F87EE5CB20941A934F2 /* BlockOperation.swift */; }; 13493B409710FCD8B3596BD6 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */; }; 13493BDEBF2CDEED6AD2028A /* Operation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493B673624213866B375D7 /* Operation.swift */; }; @@ -42,6 +42,7 @@ 13493ECEA49AE331A40DA484 /* LocationCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493A642DCBC93D7AFCF84A /* LocationCondition.swift */; }; 13493ED6E7A06C6B9DCE6ACD /* TimeoutObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493EDCE9202ADB66218B97 /* TimeoutObserver.swift */; }; 13493FCD68D71F2EB0BFD4D9 /* CKContainer+Operations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493405578740A4F865B7EC /* CKContainer+Operations.swift */; }; + 3F8D74A01B4BC06D0049FBD0 /* HTTPBinNetworkOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 134931D4EAC34EED6631C161 /* HTTPBinNetworkOperation.swift */; }; 3FBF72F41B4BBA7A00914526 /* ADVOperation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA82155D1B2F23FA00A04622 /* ADVOperation.framework */; }; 3FBF72F51B4BBA7A00914526 /* ADVOperation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = AA82155D1B2F23FA00A04622 /* ADVOperation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3FBF72F91B4BBB4C00914526 /* CalendarCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13493ED11D23BAEFDCC5146A /* CalendarCondition.swift */; }; @@ -116,6 +117,7 @@ /* Begin PBXFileReference section */ 13493011AE350750A6134743 /* UIUserNotifications+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIUserNotifications+Operations.swift"; sourceTree = ""; }; 1349301BB36EA46A258584E3 /* ReachabilityCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReachabilityCondition.swift; sourceTree = ""; }; + 134931D4EAC34EED6631C161 /* HTTPBinNetworkOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPBinNetworkOperation.swift; sourceTree = ""; }; 134931F7658131E92511027A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 134932126ECD95BB92B6A6E5 /* OperationCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationCondition.swift; sourceTree = ""; }; 134932428BA712B0F210BAA5 /* ExclusivityController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExclusivityController.swift; sourceTree = ""; }; @@ -125,7 +127,7 @@ 134932F050B4568B14C8F2D3 /* CloudCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CloudCondition.swift; sourceTree = ""; }; 13493405578740A4F865B7EC /* CKContainer+Operations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CKContainer+Operations.swift"; sourceTree = ""; }; 1349349EF54CDBAC63C60197 /* MutuallyExclusive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MutuallyExclusive.swift; sourceTree = ""; }; - 134936F116358B00B7C19D70 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + 134936FFFC75FF2FD1D19228 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 1349378344214C06DCA78DC4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 134937EC6979C8C804E07AFA /* PassbookCondition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PassbookCondition.swift; sourceTree = ""; }; 1349381A537AEBCEB914ECBD /* NoCancelledDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NoCancelledDependencies.swift; sourceTree = ""; }; @@ -224,11 +226,20 @@ name = Ressources; sourceTree = ""; }; + 134937951FAD670D064AD595 /* ViewControllers */ = { + isa = PBXGroup; + children = ( + 134936FFFC75FF2FD1D19228 /* ViewController.swift */, + ); + path = ViewControllers; + sourceTree = ""; + }; 13493992B6D1BB9D1AA5D60E /* Classes */ = { isa = PBXGroup; children = ( 13493A5F6B2DD167BD0C5DF9 /* AppDelegate.swift */, - 134936F116358B00B7C19D70 /* ViewController.swift */, + 134937951FAD670D064AD595 /* ViewControllers */, + 13493B17335AE2EB89AA1D7F /* Operations */, ); path = Classes; sourceTree = ""; @@ -266,6 +277,14 @@ path = Conditions; sourceTree = ""; }; + 13493B17335AE2EB89AA1D7F /* Operations */ = { + isa = PBXGroup; + children = ( + 134931D4EAC34EED6631C161 /* HTTPBinNetworkOperation.swift */, + ); + path = Operations; + sourceTree = ""; + }; 13493E72AC03FFD260E62889 /* OperationQueue */ = { isa = PBXGroup; children = ( @@ -484,7 +503,8 @@ buildActionMask = 2147483647; files = ( 13493AAF2E22246D33908905 /* AppDelegate.swift in Sources */, - 134937F107AF1DE8A0F4729A /* ViewController.swift in Sources */, + 3F8D74A01B4BC06D0049FBD0 /* HTTPBinNetworkOperation.swift in Sources */, + 13493ACEE507EF3D8B2A3676 /* ViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Demo/Base.lproj/Main.storyboard b/Demo/Base.lproj/Main.storyboard index 37195ab..dbe1adf 100644 --- a/Demo/Base.lproj/Main.storyboard +++ b/Demo/Base.lproj/Main.storyboard @@ -28,7 +28,13 @@ - + + + + + + + diff --git a/Demo/Classes/Operations/HTTPBinNetworkOperation.swift b/Demo/Classes/Operations/HTTPBinNetworkOperation.swift new file mode 100644 index 0000000..be67144 --- /dev/null +++ b/Demo/Classes/Operations/HTTPBinNetworkOperation.swift @@ -0,0 +1,113 @@ +// +// Created by Phillipp (LMIS) on 07/07/15. +// Copyright (c) 2015 Advanced Operation. All rights reserved. +// + +import Foundation +import ADVOperation + + +final class HTTPBinNetworkOperation: GroupOperation { + + let cacheFile:NSURL + + init(cacheFile:NSURL) { + + self.cacheFile = cacheFile + + super.init(operations: []) + self.name = "HTTPBin Operation" + + let url = NSURL(string: "http://httpbin.org/image/png")! + let session = NSURLSession.sharedSession() + + let task = session.downloadTaskWithURL(url) { + url, response, error in + self.downloadFinished(url, response: response as? NSHTTPURLResponse, error: error) + } + + if let task = task { + let taskOperation = URLSessionTaskOperation(task: task) + + let reachabilityCondition = ReachabilityCondition(host: url) + taskOperation.addCondition(reachabilityCondition) + + let networkObserver = NetworkObserver() + taskOperation.addObserver(networkObserver) + + addOperation(taskOperation) + } + + } + + private func downloadFinished(url: NSURL?, response: NSHTTPURLResponse?, error: NSError?) { + + if let error = error { + print("download failed \(error)") + } else { + print("download succeeded") + } + + if let localURL = url { + do { + /* + If we already have a file at this location, just delete it. + Also, swallow the error, because we don't really care about it. + */ + try NSFileManager.defaultManager().removeItemAtURL(cacheFile) + } + catch { + } + + do { + try NSFileManager.defaultManager().moveItemAtURL(localURL, toURL: cacheFile) + print("data successfully moved to \(cacheFile.absoluteString)") + } + catch let error as NSError { + aggregateError(error) + } + + } else if let error = error { + aggregateError(error) + } else { + // Do nothing, and the operation will automatically finish. + } + + + } + + override func finished(errors: [NSError]) { + guard let firstError = errors.first where userInitiated else { return } + + /* + We failed to load the model on a user initiated operation try and present + an error. + */ + + let alert = AlertOperation() + + alert.title = "Unable to load download image" + + alert.message = "An error occurred while downloading image. \(firstError.localizedDescription). Please try again later." + + // No custom action for this button. + alert.addAction("Retry Later", style: .Cancel) + + /* + For this operation, the `loadHandler` is only ever invoked if there are + no errors, so if we get to this point we know that it was not executed. + This means that we can offer to the user to try loading the model again, + simply by creating a new copy of the operation and giving it the same + loadHandler. + */ + alert.addAction("Retry Now") { alertOperation in + let retryOperation = HTTPBinNetworkOperation(cacheFile:self.cacheFile) + retryOperation.userInitiated = true + alertOperation.produceOperation(retryOperation) + } + + produceOperation(alert) + } + + +} \ No newline at end of file diff --git a/Demo/Classes/ViewController.swift b/Demo/Classes/ViewController.swift deleted file mode 100644 index 61c4727..0000000 --- a/Demo/Classes/ViewController.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// ViewController.swift -// Demo -// -// Created by Phillipp on 07/07/15. -// Copyright © 2015 Advanced Operation. All rights reserved. -// - -import UIKit -import ADVOperation - -class ViewController: UITableViewController { - - let operationQueue = OperationQueue() - - override func viewDidLoad() { - super.viewDidLoad() - // Do any additional setup after loading the view, typically from a nib. - - - - - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - - -} - diff --git a/Demo/Classes/ViewControllers/ViewController.swift b/Demo/Classes/ViewControllers/ViewController.swift new file mode 100644 index 0000000..515b7d0 --- /dev/null +++ b/Demo/Classes/ViewControllers/ViewController.swift @@ -0,0 +1,76 @@ +// +// ViewController.swift +// Demo +// +// Created by Phillipp on 07/07/15. +// Copyright © 2015 Advanced Operation. All rights reserved. +// + +import UIKit +import ADVOperation + +class ViewController: UITableViewController, OperationQueueDelegate { + + let operationQueue = OperationQueue() + + override func viewDidLoad() { + super.viewDidLoad() + // Do any additional setup after loading the view, typically from a nib. + +// self.operationQueue.delegate = self + } + + // MARK: Actions + + @IBAction func startAction(sender: UIBarButtonItem) { + + do { + let cachesFolder = try NSFileManager.defaultManager().URLForDirectory(.CachesDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true) + let cacheFile = cachesFolder.URLByAppendingPathComponent("image.png") + + let networkOperation = HTTPBinNetworkOperation(cacheFile: cacheFile) + networkOperation.userInitiated = true + self.operationQueue.addOperation(networkOperation) + + } catch let error as NSError { + print("error getting caches folder \(error)") + } + + } + + + // MARK: UITableViewDataSource + + override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.operationQueue.operationCount + } + + override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath) + let operation = self.operationQueue.operations[indexPath.item] + let operationName = operation.name ?? "no name" + cell.textLabel?.text = "Name: \(operationName) - State: \(operation.ready)" + + return cell + } + + // MARK: OperationQueue Delegate + + func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) { + print("OperationCount: \(operationQueue.operationCount)") + NSOperationQueue.mainQueue().addOperationWithBlock { + self.tableView.reloadData() + } + } + + func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) { + print("OperationCount: \(operationQueue.operationCount)") + NSOperationQueue.mainQueue().addOperationWithBlock { + self.tableView.reloadData() + } + } + + + +} + diff --git a/Demo/Info.plist b/Demo/Info.plist index 9cced26..7cf0c9d 100644 --- a/Demo/Info.plist +++ b/Demo/Info.plist @@ -22,6 +22,11 @@ DEV LSRequiresIPhoneOS + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/Source/Conditions/OperationCondition.swift b/Source/Conditions/OperationCondition.swift index 270f4ab..19e06b3 100644 --- a/Source/Conditions/OperationCondition.swift +++ b/Source/Conditions/OperationCondition.swift @@ -15,7 +15,7 @@ let OperationConditionKey = "OperationCondition" operation to begin execution. */ -protocol OperationCondition { +public protocol OperationCondition { /** The name of the condition. This is used in userInfo dictionaries of `.ConditionFailed` errors as the value of the `OperationConditionKey` key. @@ -50,17 +50,16 @@ protocol OperationCondition { An enum to indicate whether an `OperationCondition` was satisfied, or if it failed with an error. */ -enum OperationConditionResult { +public enum OperationConditionResult { case Satisfied case Failed(NSError) var error: NSError? { if case .Failed(let error) = self { - return error -} - -return nil -} + return error + } + return nil + } } // MARK: Evaluate Conditions diff --git a/Source/Conditions/ReachabilityCondition.swift b/Source/Conditions/ReachabilityCondition.swift index 0cdf6e5..aec0d5c 100644 --- a/Source/Conditions/ReachabilityCondition.swift +++ b/Source/Conditions/ReachabilityCondition.swift @@ -15,23 +15,23 @@ import SystemConfiguration Reachability is evaluated once when the operation to which this is attached is asked about its readiness. */ -struct ReachabilityCondition: OperationCondition { - static let hostKey = "Host" - static let name = "Reachability" - static let isMutuallyExclusive = false +public struct ReachabilityCondition: OperationCondition { + public static let hostKey = "Host" + public static let name = "Reachability" + public static let isMutuallyExclusive = false let host: NSURL - init(host: NSURL) { + public init(host: NSURL) { self.host = host } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return nil } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { ReachabilityController.requestReachability(host) { reachable in if reachable { diff --git a/Source/Observer/NetworkObserver.swift b/Source/Observer/NetworkObserver.swift index c0cdbb7..78566ef 100644 --- a/Source/Observer/NetworkObserver.swift +++ b/Source/Observer/NetworkObserver.swift @@ -15,7 +15,7 @@ as long as the `Operation` to which it is attached is executing. public struct NetworkObserver: OperationObserver { - init() { + public init() { } public func operationDidStart(operation: Operation) { diff --git a/Source/Operation/AlertOperation.swift b/Source/Operation/AlertOperation.swift index 2660d2a..7d266c2 100644 --- a/Source/Operation/AlertOperation.swift +++ b/Source/Operation/AlertOperation.swift @@ -16,7 +16,7 @@ public class AlertOperation: Operation { private let presentationContext: UIViewController? /// title of alert - var title: String? { + public var title: String? { get { return alertController.title } @@ -28,7 +28,7 @@ public class AlertOperation: Operation { } /// message of alert - var message: String? { + public var message: String? { get { return alertController.message } @@ -40,7 +40,7 @@ public class AlertOperation: Operation { // MARK: Initialization - init(presentationContext: UIViewController? = nil) { + public init(presentationContext: UIViewController? = nil) { self.presentationContext = presentationContext ?? UIApplication.sharedApplication().keyWindow?.rootViewController super.init() @@ -63,7 +63,7 @@ public class AlertOperation: Operation { - parameter style: The Alertstyle. If none if given `.Default` will be used - parameter handler: The handler that will be invoked. */ - func addAction(title: String, style: UIAlertActionStyle = .Default, handler: AlertOperation -> Void = { + public func addAction(title: String, style: UIAlertActionStyle = .Default, handler: AlertOperation -> Void = { _ in }) { let action = UIAlertAction(title: title, style: style) { [weak self] _ in @@ -78,7 +78,7 @@ public class AlertOperation: Operation { } - override func execute() { + override public func execute() { if let presentationContext = presentationContext { dispatch_async(dispatch_get_main_queue()) { if self.alertController.actions.isEmpty { diff --git a/Source/Operation/BlockOperation.swift b/Source/Operation/BlockOperation.swift index b201b19..9ac740c 100644 --- a/Source/Operation/BlockOperation.swift +++ b/Source/Operation/BlockOperation.swift @@ -49,7 +49,7 @@ public class BlockOperation: Operation { } - override func execute() { + override public func execute() { guard let block = block else { finish() return diff --git a/Source/Operation/DelayOperation.swift b/Source/Operation/DelayOperation.swift index 9933821..9e0239e 100644 --- a/Source/Operation/DelayOperation.swift +++ b/Source/Operation/DelayOperation.swift @@ -45,7 +45,7 @@ public class DelayOperation: Operation { super.init() } - override func execute() { + override public func execute() { let interval: NSTimeInterval // Figure out how long we should wait for. diff --git a/Source/Operation/GroupOperation.swift b/Source/Operation/GroupOperation.swift index f797dc6..a5f186e 100644 --- a/Source/Operation/GroupOperation.swift +++ b/Source/Operation/GroupOperation.swift @@ -28,11 +28,11 @@ public class GroupOperation: Operation { private var aggregatedErrors = [NSError]() - convenience init(operations: NSOperation...) { + public convenience init(operations: NSOperation...) { self.init(operations: operations) } - init(operations: [NSOperation]) { + public init(operations: [NSOperation]) { super.init() internalQueue.suspended = true @@ -49,12 +49,12 @@ public class GroupOperation: Operation { super.cancel() } - override func execute() { + override public func execute() { internalQueue.suspended = false internalQueue.addOperation(finishingOperation) } - func addOperation(operation: NSOperation) { + public func addOperation(operation: NSOperation) { internalQueue.addOperation(operation) } @@ -63,17 +63,17 @@ public class GroupOperation: Operation { Errors aggregated through this method will be included in the final array of errors reported to observers and to the `finished(_:)` method. */ - final func aggregateError(error: NSError) { + public final func aggregateError(error: NSError) { aggregatedErrors.append(error) } - func operationDidFinish(operation: NSOperation, withErrors errors: [NSError]) { + public func operationDidFinish(operation: NSOperation, withErrors errors: [NSError]) { // For use by subclassers. } } extension GroupOperation: OperationQueueDelegate { - final func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) { + final public func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) { assert(!finishingOperation.finished && !finishingOperation.executing, "cannot add new operations to a group after the group has completed") /* @@ -86,7 +86,7 @@ extension GroupOperation: OperationQueueDelegate { } } - final func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) { + final public func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) { aggregatedErrors.extend(errors) if operation === finishingOperation { diff --git a/Source/Operation/LocationOperation.swift b/Source/Operation/LocationOperation.swift index d536ada..197b115 100644 --- a/Source/Operation/LocationOperation.swift +++ b/Source/Operation/LocationOperation.swift @@ -33,7 +33,7 @@ public class LocationOperation: Operation, CLLocationManagerDelegate { addCondition(MutuallyExclusive()) } - override func execute() { + override public func execute() { dispatch_async(dispatch_get_main_queue()) { /* `CLLocationManager` needs to be created on a thread with an active diff --git a/Source/Operation/Operation.swift b/Source/Operation/Operation.swift index bc4825e..10f5e32 100644 --- a/Source/Operation/Operation.swift +++ b/Source/Operation/Operation.swift @@ -121,7 +121,7 @@ public class Operation: NSOperation { } } - var userInitiated: Bool { + public var userInitiated: Bool { get { return qualityOfService == .UserInitiated } @@ -166,7 +166,7 @@ public class Operation: NSOperation { private(set) var conditions = [OperationCondition]() - func addCondition(condition: OperationCondition) { + public func addCondition(condition: OperationCondition) { assert(state < .EvaluatingConditions, "Cannot modify conditions after execution has begun.") conditions.append(condition) @@ -174,7 +174,7 @@ public class Operation: NSOperation { private(set) var observers = [OperationObserver]() - func addObserver(observer: OperationObserver) { + public func addObserver(observer: OperationObserver) { assert(state < .Executing, "Cannot modify observers after execution has begun.") observers.append(observer) @@ -210,7 +210,7 @@ public class Operation: NSOperation { finished its execution, and that operations dependent on yours can re-evaluate their readiness state. */ - func execute() { + public func execute() { print("\(self.dynamicType) must override `execute()`.") finish() @@ -221,7 +221,7 @@ public class Operation: NSOperation { cancelWithError() } - func cancelWithError(error: NSError? = nil) { + public func cancelWithError(error: NSError? = nil) { if let error = error { _internalErrors.append(error) } @@ -229,7 +229,7 @@ public class Operation: NSOperation { state = .Cancelled } - final func produceOperation(operation: NSOperation) { + public final func produceOperation(operation: NSOperation) { for observer in observers { observer.operation(self, didProduceOperation: operation) } @@ -245,7 +245,7 @@ public class Operation: NSOperation { for how an error from an `NSURLSession` is passed along via the `finishWithError()` method. */ - final func finishWithError(error: NSError?) { + public final func finishWithError(error: NSError?) { if let error = error { finish([error]) } else { @@ -280,7 +280,7 @@ public class Operation: NSOperation { this method to potentially inform the user about an error when trying to bring up the Core Data stack. */ - func finished(errors: [NSError]) { + public func finished(errors: [NSError]) { // No op. } diff --git a/Source/Operation/URLSessionTaskOperation.swift b/Source/Operation/URLSessionTaskOperation.swift index 915a1b8..d88324d 100644 --- a/Source/Operation/URLSessionTaskOperation.swift +++ b/Source/Operation/URLSessionTaskOperation.swift @@ -23,15 +23,16 @@ private var URLSessionTaksOperationKVOContext = 0 */ public class URLSessionTaskOperation: Operation { - let task: NSURLSessionTask + + public let task: NSURLSessionTask - init(task: NSURLSessionTask) { + public init(task: NSURLSessionTask) { assert(task.state == .Suspended, "Tasks must be suspended.") self.task = task super.init() } - override func execute() { + override public func execute() { assert(task.state == .Suspended, "Task was resumed by something other than \(self).") task.addObserver(self, forKeyPath: "state", options: [], context: &URLSessionTaksOperationKVOContext) diff --git a/Source/OperationQueue/OperationQueue.swift b/Source/OperationQueue/OperationQueue.swift index f001006..e3a8c15 100644 --- a/Source/OperationQueue/OperationQueue.swift +++ b/Source/OperationQueue/OperationQueue.swift @@ -19,7 +19,7 @@ import Foundation `OperationQueue` and uses it to manage dependencies. */ -@objc protocol OperationQueueDelegate: NSObjectProtocol { +@objc public protocol OperationQueueDelegate: NSObjectProtocol { optional func operationQueue(operationQueue: OperationQueue, willAddOperation operation: NSOperation) optional func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) @@ -35,7 +35,8 @@ import Foundation */ public class OperationQueue: NSOperationQueue { - weak var delegate: OperationQueueDelegate? + + public weak var delegate: OperationQueueDelegate? override public func addOperation(operation: NSOperation) { From 1c2c6645fdd88e0ccf2207106e41e11326d37453 Mon Sep 17 00:00:00 2001 From: PBE Date: Tue, 7 Jul 2015 13:02:55 +0200 Subject: [PATCH 5/6] Bugfix in evaluate for operation --- Source/Conditions/NegatedCondition.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Conditions/NegatedCondition.swift b/Source/Conditions/NegatedCondition.swift index 1eb27d2..e869370 100644 --- a/Source/Conditions/NegatedCondition.swift +++ b/Source/Conditions/NegatedCondition.swift @@ -37,10 +37,11 @@ struct NegatedCondition: OperationCondition { return condition.dependencyForOperation(operation) } + func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { condition.evaluateForOperation(operation) { result in - if result.error == nil { + if result.error != nil { // If the composed condition failed, then this one succeeded. completion(.Satisfied) } else { From 74ebe571cc43e81a9899d33225f62cc4e078f6e1 Mon Sep 17 00:00:00 2001 From: PBE Date: Tue, 7 Jul 2015 13:18:57 +0200 Subject: [PATCH 6/6] adapted access modifier --- Source/Conditions/CalendarCondition.swift | 12 +- Source/Conditions/CloudCondition.swift | 12 +- Source/Conditions/HealthCondition.swift | 106 +++++++++--------- Source/Conditions/LocationCondition.swift | 12 +- Source/Conditions/MutuallyExclusive.swift | 16 +-- Source/Conditions/NegatedCondition.swift | 17 +-- Source/Conditions/PhotosCondition.swift | 12 +- .../RemoteNotificationCondition.swift | 15 +-- Source/Conditions/SilentCondition.swift | 12 +- .../UserNotificationCondition.swift | 22 ++-- .../Extensions/CKContainer+Operations.swift | 2 +- Source/Extensions/Dictionary+Operations.swift | 2 +- .../Extensions/NSOperation+Operations.swift | 2 +- .../UIUserNotifications+Operations.swift | 7 +- Source/Observer/BlockObserver.swift | 2 +- Source/Observer/TimeoutObserver.swift | 2 +- Source/Operation/BlockOperation.swift | 7 +- Source/Operation/DelayOperation.swift | 4 +- Source/Operation/GroupOperation.swift | 1 + Source/Operation/LocationOperation.swift | 2 +- .../ExclusivityController.swift | 2 +- 21 files changed, 139 insertions(+), 130 deletions(-) diff --git a/Source/Conditions/CalendarCondition.swift b/Source/Conditions/CalendarCondition.swift index 02cda70..c21b022 100644 --- a/Source/Conditions/CalendarCondition.swift +++ b/Source/Conditions/CalendarCondition.swift @@ -10,23 +10,23 @@ import EventKit /// A condition for verifying access to the user's calendar. -struct CalendarCondition: OperationCondition { +public struct CalendarCondition: OperationCondition { - static let name = "Calendar" + public static let name = "Calendar" + public static let isMutuallyExclusive = false static let entityTypeKey = "EKEntityType" - static let isMutuallyExclusive = false let entityType: EKEntityType - init(entityType: EKEntityType) { + public init(entityType: EKEntityType) { self.entityType = entityType } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return CalendarPermissionOperation(entityType: entityType) } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { switch EKEventStore.authorizationStatusForEntityType(entityType) { case .Authorized: completion(.Satisfied) diff --git a/Source/Conditions/CloudCondition.swift b/Source/Conditions/CloudCondition.swift index be64f29..b3d6929 100644 --- a/Source/Conditions/CloudCondition.swift +++ b/Source/Conditions/CloudCondition.swift @@ -10,9 +10,9 @@ import CloudKit /// A condition describing that the operation requires access to a specific CloudKit container. -struct CloudContainerCondition: OperationCondition { +public struct CloudContainerCondition: OperationCondition { - static let name = "CloudContainer" + public static let name = "CloudContainer" static let containerKey = "CKContainer" /* @@ -20,7 +20,7 @@ struct CloudContainerCondition: OperationCondition { so we will allow operations that use CloudKit to be concurrent with each other. */ - static let isMutuallyExclusive = false + public static let isMutuallyExclusive = false let container: CKContainer // this is the container to which you need access. @@ -33,16 +33,16 @@ struct CloudContainerCondition: OperationCondition { container. This parameter has a default value of `[]`, which would get you anonymized read/write access. */ - init(container: CKContainer, permission: CKApplicationPermissions = []) { + public init(container: CKContainer, permission: CKApplicationPermissions = []) { self.container = container self.permission = permission } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return CloudKitPermissionOperation(container: container, permission: permission) } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { container.verifyPermission(permission, requestingIfNecessary: false) { error in if let error = error { diff --git a/Source/Conditions/HealthCondition.swift b/Source/Conditions/HealthCondition.swift index a4e5318..2352974 100644 --- a/Source/Conditions/HealthCondition.swift +++ b/Source/Conditions/HealthCondition.swift @@ -16,11 +16,14 @@ import UIKit data. */ -struct HealthCondition: OperationCondition { - static let name = "Health" +public struct HealthCondition: OperationCondition { + + public static let name = "Health" + public static let isMutuallyExclusive = false + static let healthDataAvailable = "HealthDataAvailable" static let unauthorizedShareTypesKey = "UnauthorizedShareTypes" - static let isMutuallyExclusive = false + let shareTypes: Set let readTypes: Set @@ -34,66 +37,63 @@ struct HealthCondition: OperationCondition { - parameter typesToRead: An array of `HKSampleType` objects, indicating the kinds of data you wish to read from HealthKit. */ - init(typesToWrite: Set, typesToRead: Set) { + public init(typesToWrite: Set, typesToRead: Set) { shareTypes = typesToWrite readTypes = typesToRead } - func dependencyForOperation(operation: Operation) -> NSOperation? { - guard HKHealthStore.isHealthDataAvailable() - -else { - return nil -} - -guard !shareTypes.isEmpty || !readTypes.isEmpty else { - return nil -} - -return HealthPermissionOperation(shareTypes: shareTypes, readTypes: readTypes) -} + public func dependencyForOperation(operation: Operation) -> NSOperation? { + guard HKHealthStore.isHealthDataAvailable() else { + return nil + } + + guard !shareTypes.isEmpty || !readTypes.isEmpty else { + return nil + } -func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { - guard HKHealthStore.isHealthDataAvailable() + return HealthPermissionOperation(shareTypes: shareTypes, readTypes: readTypes) + } -else { - failed(shareTypes, completion: completion) - return -} + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + guard HKHealthStore.isHealthDataAvailable() else { + failed(shareTypes, completion: completion) + return + } -let store = HKHealthStore() -/* - Note that we cannot check to see if access to the "typesToRead" - has been granted or not, as that is sensitive data. For example, - a person with diabetes may choose to not allow access to Blood Glucose - data, and the fact that this request has denied is itself an indicator - that the user may have diabetes. - - Thus, we can only check to see if we've been given permission to - write data to HealthKit. -*/ -let unauthorizedShareTypes = shareTypes.filter { - shareType in - return store.authorizationStatusForType(shareType) != .SharingAuthorized -} + let store = HKHealthStore() + + /* + Note that we cannot check to see if access to the "typesToRead" + has been granted or not, as that is sensitive data. For example, + a person with diabetes may choose to not allow access to Blood Glucose + data, and the fact that this request has denied is itself an indicator + that the user may have diabetes. + + Thus, we can only check to see if we've been given permission to + write data to HealthKit. + */ + let unauthorizedShareTypes = shareTypes.filter { + shareType in + return store.authorizationStatusForType(shareType) != .SharingAuthorized + } -if !unauthorizedShareTypes.isEmpty { - failed(Set(unauthorizedShareTypes), completion: completion) -} else { - completion(.Satisfied) -} -} + if !unauthorizedShareTypes.isEmpty { + failed(Set(unauthorizedShareTypes), completion: completion) + } else { + completion(.Satisfied) + } + } -// Break this out in to its own method so we don't clutter up the evaluate... method. -private func failed(unauthorizedShareTypes: Set, completion: OperationConditionResult -> Void) { - let error = NSError(code: .ConditionFailed, userInfo: [ - OperationConditionKey: self.dynamicType.name, - self.dynamicType.healthDataAvailable: HKHealthStore.isHealthDataAvailable(), - self.dynamicType.unauthorizedShareTypesKey: unauthorizedShareTypes - ]) + // Break this out in to its own method so we don't clutter up the evaluate... method. + private func failed(unauthorizedShareTypes: Set, completion: OperationConditionResult -> Void) { + let error = NSError(code: .ConditionFailed, userInfo: [ + OperationConditionKey: self.dynamicType.name, + self.dynamicType.healthDataAvailable: HKHealthStore.isHealthDataAvailable(), + self.dynamicType.unauthorizedShareTypesKey: unauthorizedShareTypes + ]) - completion(.Failed(error)) -} + completion(.Failed(error)) + } } diff --git a/Source/Conditions/LocationCondition.swift b/Source/Conditions/LocationCondition.swift index e29b7c0..096c7fd 100644 --- a/Source/Conditions/LocationCondition.swift +++ b/Source/Conditions/LocationCondition.swift @@ -10,7 +10,7 @@ import CoreLocation /// A condition for verifying access to the user's location. -struct LocationCondition: OperationCondition { +public struct LocationCondition: OperationCondition { /** Declare a new enum instead of using `CLAuthorizationStatus`, because that enum has more case values than are necessary for our purposes. @@ -21,10 +21,12 @@ struct LocationCondition: OperationCondition { case Always } - static let name = "Location" + public static let name = "Location" + public static let isMutuallyExclusive = false + static let locationServicesEnabledKey = "CLLocationServicesEnabled" static let authorizationStatusKey = "CLAuthorizationStatus" - static let isMutuallyExclusive = false + let usage: Usage @@ -32,11 +34,11 @@ struct LocationCondition: OperationCondition { self.usage = usage } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return LocationPermissionOperation(usage: usage) } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { let enabled = CLLocationManager.locationServicesEnabled() let actual = CLLocationManager.authorizationStatus() diff --git a/Source/Conditions/MutuallyExclusive.swift b/Source/Conditions/MutuallyExclusive.swift index 5654eec..53d1e37 100644 --- a/Source/Conditions/MutuallyExclusive.swift +++ b/Source/Conditions/MutuallyExclusive.swift @@ -10,23 +10,23 @@ import Foundation /// A generic condition for describing kinds of operations that may not execute concurrently. -struct MutuallyExclusive: OperationCondition { - static var name: String { +public struct MutuallyExclusive: OperationCondition { + public static var name: String { return "MutuallyExclusive<\(T.self)>" } - static var isMutuallyExclusive: Bool { + public static var isMutuallyExclusive: Bool { return true } - init() { + public init() { } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return nil } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { completion(.Satisfied) } } @@ -36,8 +36,8 @@ struct MutuallyExclusive: OperationCondition { type to be used with `MutuallyExclusive`. */ -enum Alert { +public enum Alert { } /// A condition describing that the targeted operation may present an alert. -typealias AlertPresentation = MutuallyExclusive +public typealias AlertPresentation = MutuallyExclusive diff --git a/Source/Conditions/NegatedCondition.swift b/Source/Conditions/NegatedCondition.swift index e869370..10d378b 100644 --- a/Source/Conditions/NegatedCondition.swift +++ b/Source/Conditions/NegatedCondition.swift @@ -14,31 +14,32 @@ import Foundation network is NOT reachable. */ -struct NegatedCondition: OperationCondition { - static var name: String { +public struct NegatedCondition: OperationCondition { + + public static var name: String { return "Not<\(T.name)>" } - static var negatedConditionKey: String { + public static var negatedConditionKey: String { return "NegatedCondition" } - static var isMutuallyExclusive: Bool { + public static var isMutuallyExclusive: Bool { return T.isMutuallyExclusive } - let condition: T + public let condition: T - init(condition: T) { + public init(condition: T) { self.condition = condition } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return condition.dependencyForOperation(operation) } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { condition.evaluateForOperation(operation) { result in if result.error != nil { diff --git a/Source/Conditions/PhotosCondition.swift b/Source/Conditions/PhotosCondition.swift index d606996..92f0509 100644 --- a/Source/Conditions/PhotosCondition.swift +++ b/Source/Conditions/PhotosCondition.swift @@ -11,18 +11,18 @@ This file shows an example of implementing the OperationCondition protocol. import Photos /// A condition for verifying access to the user's Photos library. -struct PhotosCondition: OperationCondition { +public struct PhotosCondition: OperationCondition { - static let name = "Photos" - static let isMutuallyExclusive = false + public static let name = "Photos" + public static let isMutuallyExclusive = false - init() { } + public init() { } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return PhotosPermissionOperation() } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { switch PHPhotoLibrary.authorizationStatus() { case .Authorized: completion(.Satisfied) diff --git a/Source/Conditions/RemoteNotificationCondition.swift b/Source/Conditions/RemoteNotificationCondition.swift index 7321f9d..88faefb 100644 --- a/Source/Conditions/RemoteNotificationCondition.swift +++ b/Source/Conditions/RemoteNotificationCondition.swift @@ -20,9 +20,10 @@ private enum RemoteRegistrationResult { /// A condition for verifying that the app has the ability to receive push notifications. -struct RemoteNotificationCondition: OperationCondition { - static let name = "RemoteNotification" - static let isMutuallyExclusive = false +public struct RemoteNotificationCondition: OperationCondition { + + public static let name = "RemoteNotification" + public static let isMutuallyExclusive = false static func didReceiveNotificationToken(token: NSData) { NSNotificationCenter.defaultCenter().postNotificationName(RemoteNotificationName, object: nil, userInfo: [ @@ -36,17 +37,17 @@ struct RemoteNotificationCondition: OperationCondition { ]) } - let application: UIApplication + public let application: UIApplication - init(application: UIApplication) { + public init(application: UIApplication) { self.application = application } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return RemoteNotificationPermissionOperation(application: application, handler: { _ in }) } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { /* Since evaluation requires executing an operation, use a private operation queue. diff --git a/Source/Conditions/SilentCondition.swift b/Source/Conditions/SilentCondition.swift index 3014493..595fcb4 100644 --- a/Source/Conditions/SilentCondition.swift +++ b/Source/Conditions/SilentCondition.swift @@ -15,27 +15,27 @@ import Foundation do not already have it. */ -struct SilentCondition: OperationCondition { +public struct SilentCondition: OperationCondition { let condition: T - static var name: String { + public static var name: String { return "Silent<\(T.name)>" } - static var isMutuallyExclusive: Bool { + public static var isMutuallyExclusive: Bool { return T.isMutuallyExclusive } - init(condition: T) { + public init(condition: T) { self.condition = condition } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { // Returning nil means we will never a dependency to be generated. return nil } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { condition.evaluateForOperation(operation, completion: completion) } } diff --git a/Source/Conditions/UserNotificationCondition.swift b/Source/Conditions/UserNotificationCondition.swift index fe71ad3..2357f04 100644 --- a/Source/Conditions/UserNotificationCondition.swift +++ b/Source/Conditions/UserNotificationCondition.swift @@ -15,9 +15,9 @@ import UIKit `UILocalNotification` and/or remote notifications. */ -struct UserNotificationCondition: OperationCondition { +public struct UserNotificationCondition: OperationCondition { - enum Behavior { + public enum Behavior { /// Merge the new `UIUserNotificationSettings` with the `currentUserNotificationSettings`. case Merge @@ -25,14 +25,16 @@ struct UserNotificationCondition: OperationCondition { case Replace } - static let name = "UserNotification" + public static let name = "UserNotification" + public static let isMutuallyExclusive = false + static let currentSettings = "CurrentUserNotificationSettings" static let desiredSettings = "DesiredUserNotificationSettigns" - static let isMutuallyExclusive = false - let settings: UIUserNotificationSettings - let application: UIApplication - let behavior: Behavior + + public let settings: UIUserNotificationSettings + public let application: UIApplication + public let behavior: Behavior /** The designated initializer. @@ -49,17 +51,17 @@ struct UserNotificationCondition: OperationCondition { `application`. You may also specify `.Replace`, which means the `settings` will overwrite the exisiting settings. */ - init(settings: UIUserNotificationSettings, application: UIApplication, behavior: Behavior = .Merge) { + public init(settings: UIUserNotificationSettings, application: UIApplication, behavior: Behavior = .Merge) { self.settings = settings self.application = application self.behavior = behavior } - func dependencyForOperation(operation: Operation) -> NSOperation? { + public func dependencyForOperation(operation: Operation) -> NSOperation? { return UserNotificationPermissionOperation(settings: settings, application: application, behavior: behavior) } - func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { + public func evaluateForOperation(operation: Operation, completion: OperationConditionResult -> Void) { let result: OperationConditionResult let current = application.currentUserNotificationSettings() diff --git a/Source/Extensions/CKContainer+Operations.swift b/Source/Extensions/CKContainer+Operations.swift index 7ec4afe..e3d331f 100644 --- a/Source/Extensions/CKContainer+Operations.swift +++ b/Source/Extensions/CKContainer+Operations.swift @@ -8,7 +8,7 @@ A convenient extension to CloudKit.CKContainer. import CloudKit -extension CKContainer { +internal extension CKContainer { /** Verify that the current user has certain permissions for the `CKContainer`, and potentially requesting the permission if necessary. diff --git a/Source/Extensions/Dictionary+Operations.swift b/Source/Extensions/Dictionary+Operations.swift index 48dda6a..5d682ec 100644 --- a/Source/Extensions/Dictionary+Operations.swift +++ b/Source/Extensions/Dictionary+Operations.swift @@ -6,7 +6,7 @@ Abstract: A convenient extension to Swift.Dictionary. */ -extension Dictionary { +internal extension Dictionary { /** It's not uncommon to want to turn a sequence of values into a dictionary, where each value is keyed by some unique identifier. This initializer will diff --git a/Source/Extensions/NSOperation+Operations.swift b/Source/Extensions/NSOperation+Operations.swift index 2c4f961..4db65be 100644 --- a/Source/Extensions/NSOperation+Operations.swift +++ b/Source/Extensions/NSOperation+Operations.swift @@ -8,7 +8,7 @@ A convenient extension to Foundation.NSOperation. import Foundation -extension NSOperation { +public extension NSOperation { /** Add a completion block to be executed after the `NSOperation` enters the "finished" state. diff --git a/Source/Extensions/UIUserNotifications+Operations.swift b/Source/Extensions/UIUserNotifications+Operations.swift index 69b6e0d..53afa37 100644 --- a/Source/Extensions/UIUserNotifications+Operations.swift +++ b/Source/Extensions/UIUserNotifications+Operations.swift @@ -10,9 +10,10 @@ A convenient extension to UIKit.UIUserNotificationSettings. import UIKit -extension UIUserNotificationSettings { +public extension UIUserNotificationSettings { + /// Check to see if one Settings object is a superset of another Settings object. - func contains(settings: UIUserNotificationSettings) -> Bool { + public func contains(settings: UIUserNotificationSettings) -> Bool { // our types must contain all of the other types if !types.contains(settings.types) { return false @@ -28,7 +29,7 @@ extension UIUserNotificationSettings { Merge two Settings objects together. `UIUserNotificationCategories` with the same identifier are considered equal. */ - func settingsByMerging(settings: UIUserNotificationSettings) -> UIUserNotificationSettings { + public func settingsByMerging(settings: UIUserNotificationSettings) -> UIUserNotificationSettings { let mergedTypes = types.union(settings.types) let myCategories = categories ?? [] diff --git a/Source/Observer/BlockObserver.swift b/Source/Observer/BlockObserver.swift index 6ecbbc3..8d759bf 100644 --- a/Source/Observer/BlockObserver.swift +++ b/Source/Observer/BlockObserver.swift @@ -20,7 +20,7 @@ public struct BlockObserver: OperationObserver { private let produceHandler: ((Operation, NSOperation) -> Void)? private let finishHandler: ((Operation, [NSError]) -> Void)? - init(startHandler: (Operation -> Void)? = nil, produceHandler: ((Operation, NSOperation) -> Void)? = nil, finishHandler: ((Operation, [NSError]) -> Void)? = nil) { + public init(startHandler: (Operation -> Void)? = nil, produceHandler: ((Operation, NSOperation) -> Void)? = nil, finishHandler: ((Operation, [NSError]) -> Void)? = nil) { self.startHandler = startHandler self.produceHandler = produceHandler self.finishHandler = finishHandler diff --git a/Source/Observer/TimeoutObserver.swift b/Source/Observer/TimeoutObserver.swift index b203a90..f588562 100644 --- a/Source/Observer/TimeoutObserver.swift +++ b/Source/Observer/TimeoutObserver.swift @@ -22,7 +22,7 @@ public struct TimeoutObserver: OperationObserver { // MARK: Initialization - init(timeout: NSTimeInterval) { + public init(timeout: NSTimeInterval) { self.timeout = timeout } diff --git a/Source/Operation/BlockOperation.swift b/Source/Operation/BlockOperation.swift index 9ac740c..16e280a 100644 --- a/Source/Operation/BlockOperation.swift +++ b/Source/Operation/BlockOperation.swift @@ -9,11 +9,12 @@ This code shows how to create a simple subclass of Operation. import Foundation /// A closure type that takes a closure as its parameter. -typealias OperationBlock = (Void -> Void) -> Void +public typealias OperationBlock = (Void -> Void) -> Void /// A sublcass of `Operation` to execute a closure. public class BlockOperation: Operation { + private let block: OperationBlock? /** @@ -25,7 +26,7 @@ public class BlockOperation: Operation { will never finish executing. If this parameter is `nil`, the operation will immediately finish. */ - init(block: OperationBlock? = nil) { + public init(block: OperationBlock? = nil) { self.block = block super.init() } @@ -38,7 +39,7 @@ public class BlockOperation: Operation { the designated initializer). The operation will be automatically ended after the `mainQueueBlock` is executed. */ - convenience init(mainQueueBlock: dispatch_block_t) { + public convenience init(mainQueueBlock: dispatch_block_t) { self.init(block: { continuation in dispatch_async(dispatch_get_main_queue()) { diff --git a/Source/Operation/DelayOperation.swift b/Source/Operation/DelayOperation.swift index 9e0239e..f11529b 100644 --- a/Source/Operation/DelayOperation.swift +++ b/Source/Operation/DelayOperation.swift @@ -35,12 +35,12 @@ public class DelayOperation: Operation { // MARK: Initialization - init(interval: NSTimeInterval) { + public init(interval: NSTimeInterval) { delay = .Interval(interval) super.init() } - init(until date: NSDate) { + public init(until date: NSDate) { delay = .Date(date) super.init() } diff --git a/Source/Operation/GroupOperation.swift b/Source/Operation/GroupOperation.swift index a5f186e..602d327 100644 --- a/Source/Operation/GroupOperation.swift +++ b/Source/Operation/GroupOperation.swift @@ -23,6 +23,7 @@ import Foundation */ public class GroupOperation: Operation { + private let internalQueue = OperationQueue() private let finishingOperation = NSBlockOperation(block: {}) diff --git a/Source/Operation/LocationOperation.swift b/Source/Operation/LocationOperation.swift index 197b115..493d7c5 100644 --- a/Source/Operation/LocationOperation.swift +++ b/Source/Operation/LocationOperation.swift @@ -25,7 +25,7 @@ public class LocationOperation: Operation, CLLocationManagerDelegate { // MARK: Initialization - init(accuracy: CLLocationAccuracy, locationHandler: CLLocation -> Void) { + public init(accuracy: CLLocationAccuracy, locationHandler: CLLocation -> Void) { self.accuracy = accuracy self.handler = locationHandler super.init() diff --git a/Source/OperationQueue/ExclusivityController.swift b/Source/OperationQueue/ExclusivityController.swift index 6be2142..0e0b267 100644 --- a/Source/OperationQueue/ExclusivityController.swift +++ b/Source/OperationQueue/ExclusivityController.swift @@ -15,7 +15,7 @@ import Foundation app, regardless of the `OperationQueue` on which an `Operation` was executed. */ -public class ExclusivityController { +internal class ExclusivityController { static let sharedExclusivityController = ExclusivityController() private let serialQueue = dispatch_queue_create("Operations.ExclusivityController", DISPATCH_QUEUE_SERIAL)