diff --git a/AppMessage/AppMessage.xcodeproj/project.pbxproj b/AppMessage/AppMessage.xcodeproj/project.pbxproj index 9e71b12..1f6d4f2 100755 --- a/AppMessage/AppMessage.xcodeproj/project.pbxproj +++ b/AppMessage/AppMessage.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 13BCB1C81F7BC817002C7DD4 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13BCB1C71F7BC817002C7DD4 /* Async.swift */; }; 437210433C525AAAFE3EB76C /* Pods_AppMessage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 495AC817462253B57CD42BDB /* Pods_AppMessage.framework */; }; 7F0791301E1CF12B00A015A7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F07912F1E1CF12B00A015A7 /* main.m */; }; 7F0992E51AEA3BAD00522AE6 /* SSASideMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0992E41AEA3BAD00522AE6 /* SSASideMenu.swift */; }; @@ -44,6 +45,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 13BCB1C71F7BC817002C7DD4 /* Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Async.swift; sourceTree = ""; }; 318A421B62B0803009675CC1 /* Pods_OSXTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_OSXTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3F3763EA1FE6EAFCB50FEE13 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 495AC817462253B57CD42BDB /* Pods_AppMessage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppMessage.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -109,9 +111,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 13BCB1C61F7BC7F2002C7DD4 /* AsyncSwift */ = { + isa = PBXGroup; + children = ( + 13BCB1C71F7BC817002C7DD4 /* Async.swift */, + ); + name = AsyncSwift; + sourceTree = ""; + }; 7F0992E21AEA3B6100522AE6 /* Frameworks */ = { isa = PBXGroup; children = ( + 13BCB1C61F7BC7F2002C7DD4 /* AsyncSwift */, 7F0992E31AEA3B8C00522AE6 /* SSASideMenu */, ); name = Frameworks; @@ -275,13 +286,13 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = mirabeau; TargetAttributes = { 7F434A2219628D490081F35C = { CreatedOnToolsVersion = 6.0; DevelopmentTeam = NFYKDD8CR4; - LastSwiftMigration = 0800; + LastSwiftMigration = 0900; SystemCapabilities = { com.apple.BackgroundModes = { enabled = 1; @@ -360,26 +371,26 @@ ); inputPaths = ( "${SRCROOT}/../Pods/Target Support Files/Pods-AppMessage/Pods-AppMessage-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/AsyncSwift/Async.framework", "${BUILT_PRODUCTS_DIR}/CRToast/CRToast.framework", "${BUILT_PRODUCTS_DIR}/EVCloudKitDao-iOS/EVCloudKitDao.framework", "${BUILT_PRODUCTS_DIR}/EVReflection-iOS/EVReflection.framework", "${BUILT_PRODUCTS_DIR}/JSQMessagesViewController/JSQMessagesViewController.framework", "${BUILT_PRODUCTS_DIR}/JSQSystemSoundPlayer/JSQSystemSoundPlayer.framework", "${BUILT_PRODUCTS_DIR}/SwiftLocation/SwiftLocation.framework", + "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", "${BUILT_PRODUCTS_DIR}/UIImage-Resize/UIImage_Resize.framework", "${BUILT_PRODUCTS_DIR}/UzysAssetsPickerController/UzysAssetsPickerController.framework", "${BUILT_PRODUCTS_DIR}/VIPhotoView/VIPhotoView.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Async.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CRToast.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EVCloudKitDao.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/EVReflection.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JSQMessagesViewController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JSQSystemSoundPlayer.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftLocation.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UIImage_Resize.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/UzysAssetsPickerController.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/VIPhotoView.framework", @@ -422,6 +433,7 @@ 7FC46F8619813D77000A875F /* RootViewController.swift in Sources */, 7FF447441A16964C0003AE91 /* ChatViewController.swift in Sources */, 7FFD4C0D1A222E9D00E03FAB /* Helper.swift in Sources */, + 13BCB1C81F7BC817002C7DD4 /* Async.swift in Sources */, 7F76554C197FF0FE008AD0D5 /* Asset.swift in Sources */, 7FC8B3B119E0600900C146AE /* MenuViewController.swift in Sources */, 7F0992E51AEA3BAD00522AE6 /* SSASideMenu.swift in Sources */, @@ -447,14 +459,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -495,14 +513,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -547,7 +571,8 @@ PROVISIONING_PROFILE = ""; SWIFT_OBJC_BRIDGING_HEADER = "AppMessage/AppMessage-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -570,7 +595,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE = ""; SWIFT_OBJC_BRIDGING_HEADER = "AppMessage/AppMessage-Bridging-Header.h"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Release; diff --git a/AppMessage/AppMessage.xcodeproj/xcshareddata/xcschemes/AppMessage.xcscheme b/AppMessage/AppMessage.xcodeproj/xcshareddata/xcschemes/AppMessage.xcscheme index ee0416a..c9178f8 100755 --- a/AppMessage/AppMessage.xcodeproj/xcshareddata/xcschemes/AppMessage.xcscheme +++ b/AppMessage/AppMessage.xcodeproj/xcshareddata/xcschemes/AppMessage.xcscheme @@ -1,6 +1,6 @@ @@ -45,6 +46,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/AppMessage/AppMessage/Async.swift b/AppMessage/AppMessage/Async.swift new file mode 100644 index 0000000..3f0a8c0 --- /dev/null +++ b/AppMessage/AppMessage/Async.swift @@ -0,0 +1,768 @@ +// +// Async.swift +// +// Created by Tobias DM on 15/07/14. +// +// OS X 10.10+ and iOS 8.0+ +// Only use with ARC +// +// The MIT License (MIT) +// Copyright (c) 2014 Tobias Due Munk +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +import Foundation + +// MARK: - DSL for GCD queues + +/** + `GCD` is a convenience enum with cases to get `DispatchQueue` of different quality of service classes, as provided by `DispatchQueue.global` or `DispatchQueue` for main thread or a specific custom queue. + + let mainQueue = GCD.main + let utilityQueue = GCD.utility + let customQueue = GCD.custom(queue: aDispatchQueue) + + - SeeAlso: Grand Central Dispatch + */ +private enum GCD { + case main, userInteractive, userInitiated, utility, background, custom(queue: DispatchQueue) + + var queue: DispatchQueue { + switch self { + case .main: return .main + case .userInteractive: return .global(qos: .userInteractive) + case .userInitiated: return .global(qos: .userInitiated) + case .utility: return .global(qos: .utility) + case .background: return .global(qos: .background) + case .custom(let queue): return queue + } + } +} + + + +// MARK: - Async – Struct + +/** + The **Async** struct is the main part of the Async.framework. Handles an internally `@convention(block) () -> Swift.Void`. + + Chainable dispatch blocks with GCD: + + Async.background { + // Run on background queue + }.main { + // Run on main queue, after the previous block + } + + All moderns queue classes: + + Async.main {} + Async.userInteractive {} + Async.userInitiated {} + Async.utility {} + Async.background {} + + Custom queues: + + let customQueue = dispatch_queue_create("Label", DISPATCH_QUEUE_CONCURRENT) + Async.customQueue(customQueue) {} + + Dispatch block after delay: + + let seconds = 0.5 + Async.main(after: seconds) {} + + Cancel blocks not yet dispatched + + let block1 = Async.background { + // Some work + } + let block2 = block1.background { + // Some other work + } + Async.main { + // Cancel async to allow block1 to begin + block1.cancel() // First block is NOT cancelled + block2.cancel() // Second block IS cancelled + } + + Wait for block to finish: + + let block = Async.background { + // Do stuff + } + // Do other stuff + // Wait for "Do stuff" to finish + block.wait() + // Do rest of stuff + + - SeeAlso: Grand Central Dispatch + */ + +private class Reference { + var value: T? +} + +public typealias Async = AsyncBlock + +public struct AsyncBlock { + + + // MARK: - Private properties and init + + /** + Private property to hold internally on to a `@convention(block) () -> Swift.Void` + */ + private let block: DispatchWorkItem + + private let input: Reference? + private let output_: Reference + public var output: Out? { + return output_.value + } + + /** + Private init that takes a `@convention(block) () -> Swift.Void` + */ + private init(_ block: DispatchWorkItem, input: Reference? = nil, output: Reference = Reference()) { + self.block = block + self.input = input + self.output_ = output + } + + + // MARK: - Static methods + + /** + Sends the a block to be run asynchronously on the main thread. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the main queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with non-static method + */ + @discardableResult + public static func main(after seconds: Double? = nil, _ block: @escaping () -> O) -> AsyncBlock { + return AsyncBlock.async(after: seconds, block: block, queue: .main) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_USER_INTERACTIVE. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with non-static method + */ + @discardableResult + public static func userInteractive(after seconds: Double? = nil, _ block: @escaping () -> O) -> AsyncBlock { + return AsyncBlock.async(after: seconds, block: block, queue: .userInteractive) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_USER_INITIATED. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with non-static method + */ + @discardableResult + public static func userInitiated(after seconds: Double? = nil, _ block: @escaping () -> O) -> AsyncBlock { + return Async.async(after: seconds, block: block, queue: .userInitiated) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_UTILITY. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with non-static method + */ + @discardableResult + public static func utility(after seconds: Double? = nil, _ block: @escaping () -> O) -> AsyncBlock { + return Async.async(after: seconds, block: block, queue: .utility) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_BACKGROUND. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with non-static method + */ + @discardableResult + public static func background(after seconds: Double? = nil, _ block: @escaping () -> O) -> AsyncBlock { + return Async.async(after: seconds, block: block, queue: .background) + } + + /** + Sends the a block to be run asynchronously on a custom queue. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with non-static method + */ + @discardableResult + public static func custom(queue: DispatchQueue, after seconds: Double? = nil, _ block: @escaping () -> O) -> AsyncBlock { + return Async.async(after: seconds, block: block, queue: .custom(queue: queue)) + } + + + // MARK: - Private static methods + + /** + Convenience for dispatch_async(). Encapsulates the block in a "true" GCD block using DISPATCH_BLOCK_INHERIT_QOS_CLASS. + + - parameters: + - block: The block that is to be passed to be run on the `queue` + - queue: The queue on which the `block` is run. + + - returns: An `Async` struct which encapsulates the `@convention(block) () -> Swift.Void` + */ + + private static func async(after seconds: Double? = nil, block: @escaping () -> O, queue: GCD) -> AsyncBlock { + let reference = Reference() + let block = DispatchWorkItem(block: { + reference.value = block() + }) + + if let seconds = seconds { + let time = DispatchTime.now() + seconds + queue.queue.asyncAfter(deadline: time, execute: block) + } else { + queue.queue.async(execute: block) + } + + // Wrap block in a struct since @convention(block) () -> Swift.Void can't be extended + return AsyncBlock(block, output: reference) + } + + + // MARK: - Instance methods (matches static ones) + + /** + Sends the a block to be run asynchronously on the main thread, after the current block has finished. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the main queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with static method + */ + @discardableResult + public func main(after seconds: Double? = nil, _ chainingBlock: @escaping (Out) -> O) -> AsyncBlock { + return chain(after: seconds, block: chainingBlock, queue: .main) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_USER_INTERACTIVE, after the current block has finished. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with static method + */ + @discardableResult + public func userInteractive(after seconds: Double? = nil, _ chainingBlock: @escaping (Out) -> O) -> AsyncBlock { + return chain(after: seconds, block: chainingBlock, queue: .userInteractive) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_USER_INITIATED, after the current block has finished. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with static method + */ + @discardableResult + public func userInitiated(after seconds: Double? = nil, _ chainingBlock: @escaping (Out) -> O) -> AsyncBlock { + return chain(after: seconds, block: chainingBlock, queue: .userInitiated) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_UTILITY, after the current block has finished. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with static method + */ + @discardableResult + public func utility(after seconds: Double? = nil, _ chainingBlock: @escaping (Out) -> O) -> AsyncBlock { + return chain(after: seconds, block: chainingBlock, queue: .utility) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_BACKGROUND, after the current block has finished. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with static method + */ + @discardableResult + public func background(after seconds: Double? = nil, _ chainingBlock: @escaping (Out) -> O) -> AsyncBlock { + return chain(after: seconds, block: chainingBlock, queue: .background) + } + + /** + Sends the a block to be run asynchronously on a custom queue, after the current block has finished. + + - parameters: + - after: After how many seconds the block should be run. + - block: The block that is to be passed to be run on the queue + + - returns: An `Async` struct + + - SeeAlso: Has parity with static method + */ + @discardableResult + public func custom(queue: DispatchQueue, after seconds: Double? = nil, _ chainingBlock: @escaping (Out) -> O) -> AsyncBlock { + return chain(after: seconds, block: chainingBlock, queue: .custom(queue: queue)) + } + + // MARK: - Instance methods + + /** + Convenience function to call `dispatch_block_cancel()` on the encapsulated block. + Cancels the current block, if it hasn't already begun running to GCD. + + Usage: + + let block1 = Async.background { + // Some work + } + let block2 = block1.background { + // Some other work + } + Async.main { + // Cancel async to allow block1 to begin + block1.cancel() // First block is NOT cancelled + block2.cancel() // Second block IS cancelled + } + + */ + public func cancel() { + block.cancel() + } + + + /** + Convenience function to call `dispatch_block_wait()` on the encapsulated block. + Waits for the current block to finish, on any given thread. + + - parameters: + - seconds: Max seconds to wait for block to finish. If value is 0.0, it uses DISPATCH_TIME_FOREVER. Default value is 0. + + - SeeAlso: dispatch_block_wait, DISPATCH_TIME_FOREVER + */ + @discardableResult + public func wait(seconds: Double? = nil) -> DispatchTimeoutResult { + let timeout = seconds + .flatMap { DispatchTime.now() + $0 } + ?? .distantFuture + return block.wait(timeout: timeout) + } + + + // MARK: Private instance methods + + /** + Convenience for `dispatch_block_notify()` to + + - parameters: + - block: The block that is to be passed to be run on the `queue` + - queue: The queue on which the `block` is run. + + - returns: An `Async` struct which encapsulates the `@convention(block) () -> Swift.Void`, which is called when the current block has finished. + + - SeeAlso: dispatch_block_notify, dispatch_block_create + */ + + private func chain(after seconds: Double? = nil, block chainingBlock: @escaping (Out) -> O, queue: GCD) -> AsyncBlock { + let reference = Reference() + let dispatchWorkItem = DispatchWorkItem(block: { + reference.value = chainingBlock(self.output_.value!) + }) + + let queue = queue.queue + if let seconds = seconds { + block.notify(queue: queue) { + let time = DispatchTime.now() + seconds + queue.asyncAfter(deadline: time, execute: dispatchWorkItem) + } + } else { + block.notify(queue: queue, execute: dispatchWorkItem) + } + + // See Async.async() for comments + return AsyncBlock(dispatchWorkItem, input: self.output_, output: reference) + } +} + + +// MARK: - Apply - DSL for `dispatch_apply` + +/** + `Apply` is an empty struct with convenience static functions to parallelize a for-loop, as provided by `dispatch_apply`. + + Apply.background(100) { i in + // Calls blocks in parallel + } + + `Apply` runs a block multiple times, before returning. If you want run the block asynchronously from the current thread, wrap it in an `Async` block: + + Async.background { + Apply.background(100) { i in + // Calls blocks in parallel asynchronously + } + } + + - SeeAlso: Grand Central Dispatch, dispatch_apply + */ +public struct Apply { + + /** + Block is run any given amount of times on a queue with a quality of service of QOS_CLASS_USER_INTERACTIVE. The block is being passed an index parameter. + + - parameters: + - iterations: How many times the block should be run. Index provided to block goes from `0.. ()) { + GCD.userInteractive.queue.async { + DispatchQueue.concurrentPerform(iterations: iterations, execute: block) + } + } + + /** + Block is run any given amount of times on a queue with a quality of service of QOS_CLASS_USER_INITIATED. The block is being passed an index parameter. + + - parameters: + - iterations: How many times the block should be run. Index provided to block goes from `0.. ()) { + GCD.userInitiated.queue.async { + DispatchQueue.concurrentPerform(iterations: iterations, execute: block) + } + } + + /** + Block is run any given amount of times on a queue with a quality of service of QOS_CLASS_UTILITY. The block is being passed an index parameter. + + - parameters: + - iterations: How many times the block should be run. Index provided to block goes from `0.. ()) { + GCD.utility.queue.async { + DispatchQueue.concurrentPerform(iterations: iterations, execute: block) + } + } + + /** + Block is run any given amount of times on a queue with a quality of service of QOS_CLASS_BACKGROUND. The block is being passed an index parameter. + + - parameters: + - iterations: How many times the block should be run. Index provided to block goes from `0.. ()) { + GCD.background.queue.async { + DispatchQueue.concurrentPerform(iterations: iterations, execute: block) + } + } + + /** + Block is run any given amount of times on a custom queue. The block is being passed an index parameter. + + - parameters: + - iterations: How many times the block should be run. Index provided to block goes from `0.. ()) { + queue.async { + DispatchQueue.concurrentPerform(iterations: iterations, execute: block) + } + } +} + + +// MARK: - AsyncGroup – Struct + +/** + The **AsyncGroup** struct facilitates working with groups of asynchronous blocks. Handles a internally `dispatch_group_t`. + + Multiple dispatch blocks with GCD: + + let group = AsyncGroup() + group.background { + // Run on background queue + } + group.utility { + // Run on untility queue, after the previous block + } + group.wait() + + All moderns queue classes: + + group.main {} + group.userInteractive {} + group.userInitiated {} + group.utility {} + group.background {} + + Custom queues: + + let customQueue = dispatch_queue_create("Label", DISPATCH_QUEUE_CONCURRENT) + group.customQueue(customQueue) {} + + Wait for group to finish: + + let group = AsyncGroup() + group.background { + // Do stuff + } + group.background { + // Do other stuff in parallel + } + // Wait for both to finish + group.wait() + // Do rest of stuff + + - SeeAlso: Grand Central Dispatch + */ +public struct AsyncGroup { + + // MARK: - Private properties and init + + /** + Private property to internally on to a `dispatch_group_t` + */ + private var group: DispatchGroup + + /** + Private init that takes a `dispatch_group_t` + */ + public init() { + group = DispatchGroup() + } + + + /** + Convenience for `dispatch_group_async()` + + - parameters: + - block: The block that is to be passed to be run on the `queue` + - queue: The queue on which the `block` is run. + + - SeeAlso: dispatch_group_async, dispatch_group_create + */ + private func async(block: @escaping @convention(block) () -> Swift.Void, queue: GCD) { + queue.queue.async(group: group, execute: block) + } + + /** + Convenience for `dispatch_group_enter()`. Used to add custom blocks to the current group. + + - SeeAlso: dispatch_group_enter, dispatch_group_leave + */ + public func enter() { + group.enter() + } + + /** + Convenience for `dispatch_group_leave()`. Used to flag a custom added block is complete. + + - SeeAlso: dispatch_group_enter, dispatch_group_leave + */ + public func leave() { + group.leave() + } + + + // MARK: - Instance methods + + /** + Sends the a block to be run asynchronously on the main thread, in the current group. + + - parameters: + - block: The block that is to be passed to be run on the main queue + */ + public func main(_ block: @escaping @convention(block) () -> Swift.Void) { + async(block: block, queue: .main) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_USER_INTERACTIVE, in the current group. + + - parameters: + - block: The block that is to be passed to be run on the queue + */ + public func userInteractive(_ block: @escaping @convention(block) () -> Swift.Void) { + async(block: block, queue: .userInteractive) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_USER_INITIATED, in the current group. + + - parameters: + - block: The block that is to be passed to be run on the queue + */ + public func userInitiated(_ block: @escaping @convention(block) () -> Swift.Void) { + async(block: block, queue: .userInitiated) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of + QOS_CLASS_UTILITY, in the current block. + + - parameters: + - block: The block that is to be passed to be run on the queue + */ + public func utility(_ block: @escaping @convention(block) () -> Swift.Void) { + async(block: block, queue: .utility) + } + + /** + Sends the a block to be run asynchronously on a queue with a quality of service of QOS_CLASS_BACKGROUND, in the current block. + + - parameters: + - block: The block that is to be passed to be run on the queue + */ + public func background(_ block: @escaping @convention(block) () -> Swift.Void) { + async(block: block, queue: .background) + } + + /** + Sends the a block to be run asynchronously on a custom queue, in the current group. + + - parameters: + - queue: Custom queue where the block will be run. + - block: The block that is to be passed to be run on the queue + */ + public func custom(queue: DispatchQueue, block: @escaping @convention(block) () -> Swift.Void) { + async(block: block, queue: .custom(queue: queue)) + } + + /** + Convenience function to call `dispatch_group_wait()` on the encapsulated block. + Waits for the current group to finish, on any given thread. + + - parameters: + - seconds: Max seconds to wait for block to finish. If value is nil, it uses DISPATCH_TIME_FOREVER. Default value is nil. + + - SeeAlso: dispatch_group_wait, DISPATCH_TIME_FOREVER + */ + @discardableResult + public func wait(seconds: Double? = nil) -> DispatchTimeoutResult { + let timeout = seconds + .flatMap { DispatchTime.now() + $0 } + ?? .distantFuture + return group.wait(timeout: timeout) + } +} + + +// MARK: - Extension for `qos_class_t` + +/** + Extension to add description string for each quality of service class. + */ +public extension qos_class_t { + + /** + Description of the `qos_class_t`. E.g. "Main", "User Interactive", etc. for the given Quality of Service class. + */ + var description: String { + get { + switch self { + case qos_class_main(): return "Main" + case DispatchQoS.QoSClass.userInteractive.rawValue: return "User Interactive" + case DispatchQoS.QoSClass.userInitiated.rawValue: return "User Initiated" + case DispatchQoS.QoSClass.default.rawValue: return "Default" + case DispatchQoS.QoSClass.utility.rawValue: return "Utility" + case DispatchQoS.QoSClass.background.rawValue: return "Background" + case DispatchQoS.QoSClass.unspecified.rawValue: return "Unspecified" + default: return "Unknown" + } + } + } +} + + +// MARK: - Extension for `DispatchQueue.GlobalAttributes` + +/** + Extension to add description string for each quality of service class. + */ +public extension DispatchQoS.QoSClass { + + var description: String { + get { + switch self { + case DispatchQoS.QoSClass(rawValue: qos_class_main())!: return "Main" + case .userInteractive: return "User Interactive" + case .userInitiated: return "User Initiated" + case .default: return "Default" + case .utility: return "Utility" + case .background: return "Background" + case .unspecified: return "Unspecified" + } + } + } +} diff --git a/AppMessage/AppMessage/Controlers/ChatViewController.swift b/AppMessage/AppMessage/Controlers/ChatViewController.swift index 52fb4f5..3c2821d 100755 --- a/AppMessage/AppMessage/Controlers/ChatViewController.swift +++ b/AppMessage/AppMessage/Controlers/ChatViewController.swift @@ -13,7 +13,6 @@ import SwiftLocation import VIPhotoView import MapKit import UIImage_Resize -import Async import EVCloudKitDao import EVReflection diff --git a/AppMessage/AppMessage/Controlers/LeftMenuViewController.swift b/AppMessage/AppMessage/Controlers/LeftMenuViewController.swift index 41d1669..182ac62 100755 --- a/AppMessage/AppMessage/Controlers/LeftMenuViewController.swift +++ b/AppMessage/AppMessage/Controlers/LeftMenuViewController.swift @@ -7,7 +7,6 @@ import UIKit import CloudKit -import Async import EVCloudKitDao class LeftMenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { @@ -144,9 +143,8 @@ class LeftMenuViewController: UIViewController, UITableViewDataSource, UITableVi // ------------------------------------------------------------------------ func connectToNews(_ retryCount: Double = 1) { - let loc: CLLocation! = nil EVCloudData.publicDB.connect( - News(), predicate: NSPredicate(value: true), orderBy: Distance(field: "", relativeLocation: loc).Ascending("Subject").Descending("creationDate"), filterId: "News_All", configureNotificationInfo: { notificationInfo in + News(), predicate: NSPredicate(value: true), orderBy: Ascending(field: "Subject").Descending("creationDate"), filterId: "News_All", configureNotificationInfo: { notificationInfo in //notificationInfo.alertBody = "News update" notificationInfo.shouldSendContentAvailable = true // is already the default notificationInfo.alertLocalizationKey = "News: %1$@" diff --git a/AppMessage/AppMessage/Controlers/RightMenuViewController.swift b/AppMessage/AppMessage/Controlers/RightMenuViewController.swift index f99e5f5..89e656f 100755 --- a/AppMessage/AppMessage/Controlers/RightMenuViewController.swift +++ b/AppMessage/AppMessage/Controlers/RightMenuViewController.swift @@ -7,7 +7,6 @@ import UIKit import CloudKit -import Async import EVCloudKitDao class RightMenuViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { diff --git a/AppMessage/AppMessage/Controlers/RootViewController.swift b/AppMessage/AppMessage/Controlers/RootViewController.swift index 5160130..4484d00 100755 --- a/AppMessage/AppMessage/Controlers/RootViewController.swift +++ b/AppMessage/AppMessage/Controlers/RootViewController.swift @@ -6,7 +6,6 @@ // import UIKit -import Async import EVCloudKitDao class RootViewController: UIViewController { diff --git a/AppMessage/AppMessage/Controlers/SearchViewController.swift b/AppMessage/AppMessage/Controlers/SearchViewController.swift index 0536779..c03a1f0 100755 --- a/AppMessage/AppMessage/Controlers/SearchViewController.swift +++ b/AppMessage/AppMessage/Controlers/SearchViewController.swift @@ -6,7 +6,6 @@ // import UIKit -import Async import EVCloudKitDao class SearchViewController: UITableViewController, UISearchBarDelegate { diff --git a/AppMessage/AppMessage/Controlers/TestsViewController.swift b/AppMessage/AppMessage/Controlers/TestsViewController.swift index cf07226..377cca3 100755 --- a/AppMessage/AppMessage/Controlers/TestsViewController.swift +++ b/AppMessage/AppMessage/Controlers/TestsViewController.swift @@ -20,6 +20,9 @@ class TestsViewController: UIViewController { var createdId = ""; @IBAction func runTest(_ sender: AnyObject) { + + conflictTest() + getUserInfoTest() // will set the self.userId ingnoreFieldTest() @@ -44,6 +47,28 @@ class TestsViewController: UIViewController { alternateContainerTest() } + + func conflictTest() { + let message = Message() + message.recordID = CKRecordID(recordName: "We use this twice") + message.Text = "This is the message text" + + let message2 = Message() + message2.recordID = CKRecordID(recordName: "We use this twice") + message2.Text = "This is an other message text" + + self.dao.saveItem(message, completionHandler: {record in + EVLog("saveItem Message: \(record.recordID.recordName)"); + }, errorHandler: {error in + EVLog("<--- ERROR saveItem message \(error)"); + }) + + self.dao.saveItem(message, completionHandler: {record in + EVLog("saveItem Message: \(record.recordID.recordName)"); + }, errorHandler: {error in + EVLog("<--- ERROR saveItem message \(error)"); + }) + } func getUserInfoTest() { // retrieve our CloudKit user id. (made syncronous for this demo) diff --git a/AppMessage/AppMessage/Helper.swift b/AppMessage/AppMessage/Helper.swift index b6098d6..064e541 100755 --- a/AppMessage/AppMessage/Helper.swift +++ b/AppMessage/AppMessage/Helper.swift @@ -8,7 +8,6 @@ import Foundation import CRToast -import Async import EVCloudKitDao class Helper { diff --git a/Podfile b/Podfile index ccadb0b..8072247 100755 --- a/Podfile +++ b/Podfile @@ -4,13 +4,14 @@ workspace 'EVCloudKitDao' def libraries pod 'SwiftLocation' - pod 'AsyncSwift' + #pod 'AsyncSwift' --> for now included pod 'JSQMessagesViewController' pod 'CRToast' pod 'UIImage-Resize' pod 'UzysAssetsPickerController' pod 'VIPhotoView' #pod 'SSASideMenu' --> for now included + pod 'EVReflection/CloudKit', :git => 'https://github.com/evermeer/EVReflection.git' end target 'AppMessage' do diff --git a/Podfile.lock b/Podfile.lock index 9ce7412..708653b 100755 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,23 +1,24 @@ PODS: - - AsyncSwift (2.0.1) - CRToast (0.0.9) - - EVCloudKitDao (3.3.3): + - EVCloudKitDao (3.5.0): - EVReflection/CloudKit - - EVReflection/CloudKit (4.19.0): + - EVReflection/CloudKit (5.4.0): - EVReflection/Core - - EVReflection/Core (4.19.0) + - EVReflection/Core (5.4.0) - JSQMessagesViewController (7.3.5): - JSQSystemSoundPlayer (~> 2.0.1) - JSQSystemSoundPlayer (2.0.1) - - SwiftLocation (2.0.7) + - SwiftLocation (3.1.0): + - SwiftyJSON (~> 4.0.0-alpha.1) + - SwiftyJSON (4.0.0) - UIImage-Resize (1.0.1) - UzysAssetsPickerController (0.9.9) - VIPhotoView (0.1) DEPENDENCIES: - - AsyncSwift - CRToast - EVCloudKitDao (from `./`) + - EVReflection/CloudKit (from `https://github.com/evermeer/EVReflection.git`) - JSQMessagesViewController - SwiftLocation - UIImage-Resize @@ -27,19 +28,26 @@ DEPENDENCIES: EXTERNAL SOURCES: EVCloudKitDao: :path: ./ + EVReflection: + :git: https://github.com/evermeer/EVReflection.git + +CHECKOUT OPTIONS: + EVReflection: + :commit: 09e6acdf49e8be89f402d3e6feee3413ce5c53dd + :git: https://github.com/evermeer/EVReflection.git SPEC CHECKSUMS: - AsyncSwift: 632138e42ead868b53c745fcde9bf733ce1527ad CRToast: 5a78c22b921c5ed3487488af605bc403a9c92fed - EVCloudKitDao: c5581a82ab0304be33e3a80bfe49953508b52d7d - EVReflection: d6f7b1cc8b5cb2f4fb2dea6cbd5ac2a0503ad9a2 + EVCloudKitDao: b79984e657dc7add73be3f1e90eb4e33509ea3b3 + EVReflection: fcea2958ae3bf621293afb800b708c1c9daaf89e JSQMessagesViewController: 6587f56aedbcb150a8ba1d968e1ef58b1bc758b7 JSQSystemSoundPlayer: c5850e77a4363ffd374cd851154b9af93264ed8d - SwiftLocation: 1fe2e15a5812e961496e82eeaca71d03e829e47a + SwiftLocation: 8f5ef4f510a45e43239de57a3145b3669301e898 + SwiftyJSON: 070dabdcb1beb81b247c65ffa3a79dbbfb3b48aa UIImage-Resize: fa776860f10e6900bbaf53a73e494bdceaaccc77 UzysAssetsPickerController: 47c27bd96955467925f3d4d80b1e2e8aaeff766d VIPhotoView: bdc0cf8ddadda754b451869bd3a919e8eb5c11ab -PODFILE CHECKSUM: 4fef42d1ae6c4f432dcaa117df3e1accfa55a1a6 +PODFILE CHECKSUM: 5d40e568a3c21f9601112d4ce2cc9f5ceebd9d7d -COCOAPODS: 1.3.0 +COCOAPODS: 1.3.1 diff --git a/Source/EVCloudKitDao.swift b/Source/EVCloudKitDao.swift index 85e53ab..c19ca7b 100755 --- a/Source/EVCloudKitDao.swift +++ b/Source/EVCloudKitDao.swift @@ -455,7 +455,6 @@ open class EVCloudKitDao { }) } - // discoverAllContactUserInfosWithCompletionHandler not available on tvOS #if os(tvOS) public func allContactsUserInfo(completionHandler: (_ users: [CKDiscoveredUserInfo]?) -> Void, errorHandler:((_ error: Error) -> Void)? = nil) { diff --git a/UnitTests/UnitTests.xcodeproj/project.pbxproj b/UnitTests/UnitTests.xcodeproj/project.pbxproj index b0ff8e3..a9e1b14 100755 --- a/UnitTests/UnitTests.xcodeproj/project.pbxproj +++ b/UnitTests/UnitTests.xcodeproj/project.pbxproj @@ -148,7 +148,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0710; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 0900; ORGANIZATIONNAME = mirabeau; TargetAttributes = { 7F29764D1BB93D6A0074C85A = { @@ -377,14 +377,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -412,7 +418,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; }; name = Debug; @@ -425,14 +431,20 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -452,7 +464,7 @@ METAL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; diff --git a/UnitTests/UnitTests.xcodeproj/xcshareddata/xcschemes/OSXTest.xcscheme b/UnitTests/UnitTests.xcodeproj/xcshareddata/xcschemes/OSXTest.xcscheme index b77a5e8..ca533ea 100755 --- a/UnitTests/UnitTests.xcodeproj/xcshareddata/xcschemes/OSXTest.xcscheme +++ b/UnitTests/UnitTests.xcodeproj/xcshareddata/xcschemes/OSXTest.xcscheme @@ -1,6 +1,6 @@