From 836b80ae6ff8c1d0543dce2c01228524609d13c7 Mon Sep 17 00:00:00 2001 From: Cameron McOnie Date: Mon, 29 Oct 2018 12:57:12 +0200 Subject: [PATCH 1/6] remove --- .../Operations/AsynchronousOperation.swift | 119 ------------------ .../Operations/NetworkDataOperation.swift | 75 ----------- .../Utility/Operations/NetworkOperation.swift | 75 ----------- 3 files changed, 269 deletions(-) delete mode 100644 Example/BlockV/Utility/Operations/AsynchronousOperation.swift delete mode 100644 Example/BlockV/Utility/Operations/NetworkDataOperation.swift delete mode 100644 Example/BlockV/Utility/Operations/NetworkOperation.swift diff --git a/Example/BlockV/Utility/Operations/AsynchronousOperation.swift b/Example/BlockV/Utility/Operations/AsynchronousOperation.swift deleted file mode 100644 index 3e7557a9..00000000 --- a/Example/BlockV/Utility/Operations/AsynchronousOperation.swift +++ /dev/null @@ -1,119 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 BlockV AG -// -// 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. -// - -// -// Adapted from Robert Ryan (https://github.com/robertmryan) -// - -import Foundation - -/// Asynchronous Operation base class -/// -/// This class performs all of the necessary KVN of `isFinished` and -/// `isExecuting` for a concurrent `NSOperation` subclass. So, to developer -/// a concurrent NSOperation subclass, you instead subclass this class which: -/// -/// - must override `main()` with the tasks that initiate the asynchronous task; -/// -/// - must call `completeOperation()` function when the asynchronous task is done; -/// -/// - optionally, periodically check `self.cancelled` status, performing any clean-up -/// necessary and then ensuring that `completeOperation()` is called; or -/// override `cancel` method, calling `super.cancel()` and then cleaning-up -/// and ensuring `completeOperation()` is called. -public class AsynchronousOperation : Operation { - - override public var isAsynchronous: Bool { return true } - - private let stateLock = NSLock() - - private var _executing: Bool = false - override private(set) public var isExecuting: Bool { - get { - return stateLock.withCriticalScope { _executing } - } - set { - willChangeValue(forKey: "isExecuting") - stateLock.withCriticalScope { _executing = newValue } - didChangeValue(forKey: "isExecuting") - } - } - - private var _finished: Bool = false - override private(set) public var isFinished: Bool { - get { - return stateLock.withCriticalScope { _finished } - } - set { - willChangeValue(forKey: "isFinished") - stateLock.withCriticalScope { _finished = newValue } - didChangeValue(forKey: "isFinished") - } - } - - /// Complete the operation - /// - /// This will result in the appropriate KVN of isFinished and isExecuting - - public func completeOperation() { - if isExecuting { - isExecuting = false - } - - if !isFinished { - isFinished = true - } - } - - override public func start() { - if isCancelled { - isFinished = true - return - } - - isExecuting = true - - main() - } - - override public func main() { - fatalError("subclasses must override `main`") - } -} - -extension NSLock { - - /// Perform closure within lock. - /// - /// An extension to `NSLock` to simplify executing critical code. - /// - /// - parameter block: The closure to be performed. - - func withCriticalScope( block: () -> T) -> T { - lock() - let value = block() - unlock() - return value - } -} - diff --git a/Example/BlockV/Utility/Operations/NetworkDataOperation.swift b/Example/BlockV/Utility/Operations/NetworkDataOperation.swift deleted file mode 100644 index 8366c167..00000000 --- a/Example/BlockV/Utility/Operations/NetworkDataOperation.swift +++ /dev/null @@ -1,75 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 BlockV AG -// -// 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. -// - -// -// Adapted from Robert Ryan (https://github.com/robertmryan) -// - -import Foundation -import Alamofire - -class NetworkDataOperation: AsynchronousOperation { - - // define properties to hold everything that you'll supply when you instantiate - // this object and will be used when the request finally starts - // - // in this example, I'll keep track of (a) URL; and (b) closure to call when request is done - - private let urlString: String - private var networkOperationCompletionHandler: ((_ responseData: Data?, _ error: Error?) -> Void)? - - // we'll also keep track of the resulting request operation in case we need to cancel it later - - weak var request: Alamofire.Request? - - // define init method that captures all of the properties to be used when issuing the request - - init(urlString: String, networkOperationCompletionHandler: ((_ responseData: Data?, _ error: Error?) -> Void)? = nil) { - self.urlString = urlString - self.networkOperationCompletionHandler = networkOperationCompletionHandler - super.init() - } - - // when the operation actually starts, this is the method that will be called - - override func main() { - request = Alamofire.request(urlString, method: .get) - .responseData { dataResponse in - - // call the completion handler - self.networkOperationCompletionHandler?(dataResponse.data, dataResponse.result.error) - self.networkOperationCompletionHandler = nil - - // now that I'm done, complete this operation - - self.completeOperation() - } - } - - // we'll also support canceling the request, in case we need it - - override func cancel() { - request?.cancel() - super.cancel() - } -} diff --git a/Example/BlockV/Utility/Operations/NetworkOperation.swift b/Example/BlockV/Utility/Operations/NetworkOperation.swift deleted file mode 100644 index 4cd68daa..00000000 --- a/Example/BlockV/Utility/Operations/NetworkOperation.swift +++ /dev/null @@ -1,75 +0,0 @@ -// MIT License -// -// Copyright (c) 2018 BlockV AG -// -// 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. -// - -// -// Adapted from Robert Ryan (https://github.com/robertmryan) -// - -import Foundation -import Alamofire - -class NetworkOperation: AsynchronousOperation { - - // define properties to hold everything that you'll supply when you instantiate - // this object and will be used when the request finally starts - // - // in this example, I'll keep track of (a) URL; and (b) closure to call when request is done - - private let urlString: String - private var networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)? - - // we'll also keep track of the resulting request operation in case we need to cancel it later - - weak var request: Alamofire.Request? - - // define init method that captures all of the properties to be used when issuing the request - - init(urlString: String, networkOperationCompletionHandler: ((_ responseObject: Any?, _ error: Error?) -> Void)? = nil) { - self.urlString = urlString - self.networkOperationCompletionHandler = networkOperationCompletionHandler - super.init() - } - - // when the operation actually starts, this is the method that will be called - - override func main() { - request = Alamofire.request(urlString, method: .get) - .responseJSON { response in - // do whatever you want here; personally, I'll just all the completion handler that was passed to me in `init` - - self.networkOperationCompletionHandler?(response.result.value, response.result.error) - self.networkOperationCompletionHandler = nil - - // now that I'm done, complete this operation - - self.completeOperation() - } - } - - // we'll also support canceling the request, in case we need it - - override func cancel() { - request?.cancel() - super.cancel() - } -} From 18dcd52a311027e6d839dabca8f854be20738e12 Mon Sep 17 00:00:00 2001 From: Cameron McOnie Date: Mon, 29 Oct 2018 12:57:24 +0200 Subject: [PATCH 2/6] remove operation files --- Example/BlockV.xcodeproj/project.pbxproj | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/Example/BlockV.xcodeproj/project.pbxproj b/Example/BlockV.xcodeproj/project.pbxproj index bd861bab..9027102e 100644 --- a/Example/BlockV.xcodeproj/project.pbxproj +++ b/Example/BlockV.xcodeproj/project.pbxproj @@ -24,15 +24,12 @@ AD920A5C215FB47200CEFC1D /* PasswordTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD920A59215FB47200CEFC1D /* PasswordTableViewController.swift */; }; AD920A5D215FB47200CEFC1D /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD920A5A215FB47200CEFC1D /* ProfileViewController.swift */; }; CDC268C7232CDB5AF1386116 /* Pods_BlockV_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DE93FD598C0B61200B996A4 /* Pods_BlockV_Example.framework */; }; - D519A845205E6EE3006B0D19 /* NetworkOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D519A844205E6EE3006B0D19 /* NetworkOperation.swift */; }; - D519A848205E76EB006B0D19 /* NetworkDataOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D519A847205E76EB006B0D19 /* NetworkDataOperation.swift */; }; D519A84B205E83AB006B0D19 /* VatomDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D519A84A205E83AB006B0D19 /* VatomDetailTableViewController.swift */; }; D530759C21119E2A00DE7FD0 /* MockModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5307588211199C200DE7FD0 /* MockModels.swift */; }; D530759F2111CC5200DE7FD0 /* Test+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D530759E2111CC5200DE7FD0 /* Test+Utils.swift */; }; D53075A42112E9E000DE7FD0 /* FaceModel_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D53075A32112E9E000DE7FD0 /* FaceModel_Tests.swift */; }; D5475764205D124100E6FE90 /* UIView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5475763205D124100E6FE90 /* UIView+Ext.swift */; }; D547576B205E67AD00E6FE90 /* VatomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D547576A205E67AD00E6FE90 /* VatomCell.swift */; }; - D547576D205E6C6300E6FE90 /* AsynchronousOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D547576C205E6C6300E6FE90 /* AsynchronousOperation.swift */; }; D55B21E02052772400B6D5C2 /* VerifyTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B21DF2052772400B6D5C2 /* VerifyTableViewController.swift */; }; D55B21E520527EB400B6D5C2 /* TokenCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B21E420527EB400B6D5C2 /* TokenCell.swift */; }; D55B21EA2052E38000B6D5C2 /* RoundedImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D55B21E92052E38000B6D5C2 /* RoundedImageView.swift */; }; @@ -98,8 +95,6 @@ BC27A08D03F2D2401C48F394 /* Pods-BlockV_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BlockV_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-BlockV_Example/Pods-BlockV_Example.release.xcconfig"; sourceTree = ""; }; C55D0BD7FD8ECD417C235202 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; CB05032DBDAACFFC17BA9C27 /* Pods-BlockV_Tests.dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BlockV_Tests.dev.xcconfig"; path = "Pods/Target Support Files/Pods-BlockV_Tests/Pods-BlockV_Tests.dev.xcconfig"; sourceTree = ""; }; - D519A844205E6EE3006B0D19 /* NetworkOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkOperation.swift; sourceTree = ""; }; - D519A847205E76EB006B0D19 /* NetworkDataOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkDataOperation.swift; sourceTree = ""; }; D519A84A205E83AB006B0D19 /* VatomDetailTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VatomDetailTableViewController.swift; sourceTree = ""; }; D5307588211199C200DE7FD0 /* MockModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockModels.swift; sourceTree = ""; }; D530759121119E0E00DE7FD0 /* BLOCKv_Unit_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BLOCKv_Unit_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -108,7 +103,6 @@ D53075A32112E9E000DE7FD0 /* FaceModel_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaceModel_Tests.swift; sourceTree = ""; }; D5475763205D124100E6FE90 /* UIView+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Ext.swift"; sourceTree = ""; }; D547576A205E67AD00E6FE90 /* VatomCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VatomCell.swift; sourceTree = ""; }; - D547576C205E6C6300E6FE90 /* AsynchronousOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsynchronousOperation.swift; sourceTree = ""; }; D55B21DF2052772400B6D5C2 /* VerifyTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifyTableViewController.swift; sourceTree = ""; }; D55B21E420527EB400B6D5C2 /* TokenCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TokenCell.swift; sourceTree = ""; }; D55B21E92052E38000B6D5C2 /* RoundedImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedImageView.swift; sourceTree = ""; }; @@ -262,16 +256,6 @@ path = "User Profile"; sourceTree = ""; }; - D519A846205E76C9006B0D19 /* Operations */ = { - isa = PBXGroup; - children = ( - D547576C205E6C6300E6FE90 /* AsynchronousOperation.swift */, - D519A844205E6EE3006B0D19 /* NetworkOperation.swift */, - D519A847205E76EB006B0D19 /* NetworkDataOperation.swift */, - ); - path = Operations; - sourceTree = ""; - }; D519A849205E8360006B0D19 /* Inventory */ = { isa = PBXGroup; children = ( @@ -357,7 +341,6 @@ isa = PBXGroup; children = ( D55B21E92052E38000B6D5C2 /* RoundedImageView.swift */, - D519A846205E76C9006B0D19 /* Operations */, ); path = Utility; sourceTree = ""; @@ -566,7 +549,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - D519A848205E76EB006B0D19 /* NetworkDataOperation.swift in Sources */, AD0BCC98217A0106001836DE /* LiveVatomView.swift in Sources */, D5475764205D124100E6FE90 /* UIView+Ext.swift in Sources */, AD920A5C215FB47200CEFC1D /* PasswordTableViewController.swift in Sources */, @@ -587,8 +569,6 @@ 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, D55B21EA2052E38000B6D5C2 /* RoundedImageView.swift in Sources */, D5728D5E206155600041F4F7 /* ActionListTableViewController.swift in Sources */, - D519A845205E6EE3006B0D19 /* NetworkOperation.swift in Sources */, - D547576D205E6C6300E6FE90 /* AsynchronousOperation.swift in Sources */, AD36A59F215D1CE4009EFD55 /* CustomLoaderView.swift in Sources */, D5728D5C206131980041F4F7 /* TransferActionViewController.swift in Sources */, ); From 3c027f261f736207d72034765cee11b76c04f35f Mon Sep 17 00:00:00 2001 From: Cameron McOnie Date: Mon, 29 Oct 2018 12:57:41 +0200 Subject: [PATCH 3/6] update pod to use 2.2.0 --- Example/Podfile | 1 + Example/Podfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Example/Podfile b/Example/Podfile index c77bae08..75f3ab75 100644 --- a/Example/Podfile +++ b/Example/Podfile @@ -1,4 +1,5 @@ use_frameworks! +platform :ios, '10.0' target 'BlockV_Example' do diff --git a/Example/Podfile.lock b/Example/Podfile.lock index eb573141..70ead67d 100644 --- a/Example/Podfile.lock +++ b/Example/Podfile.lock @@ -1,14 +1,14 @@ PODS: - Alamofire (4.7.3) - - BLOCKv (2.1.0): - - BLOCKv/Face (= 2.1.0) - - BLOCKv/Core (2.1.0): + - BLOCKv (2.2.0): + - BLOCKv/Face (= 2.2.0) + - BLOCKv/Core (2.2.0): - Alamofire (~> 4.7) - JWTDecode (~> 2.1) - Signals (~> 5.0) - Starscream (~> 3.0) - SwiftLint (~> 0.26) - - BLOCKv/Face (2.1.0): + - BLOCKv/Face (2.2.0): - BLOCKv/Core - FLAnimatedImage (~> 1.0) - Nuke (~> 7.0) @@ -38,7 +38,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Alamofire: c7287b6e5d7da964a70935e5db17046b7fde6568 - BLOCKv: 0d8fb7f96628f812dcb05779f9034484ebabe637 + BLOCKv: efebdfa36a1106e23d677298b6766734b621702e FLAnimatedImage: 4a0b56255d9b05f18b6dd7ee06871be5d3b89e31 JWTDecode: 83e8b381aafe27158d9f692cbdca2b12d61d460c Nuke: 799c0430f87b11c4dacfa80b13e1c4f0a1464b90 From 60b1baa1c2fbf2dd673215bbc2123953e652fd01 Mon Sep 17 00:00:00 2001 From: Cameron McOnie Date: Mon, 29 Oct 2018 12:57:59 +0200 Subject: [PATCH 4/6] rename to ImageProgressFaceView --- BlockV/Face/Face Views/FaceViewRoster.swift | 2 +- .../ImageProgressFaceView.swift | 281 ++++++++++++++++++ 2 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 BlockV/Face/Face Views/Image Progress/ImageProgressFaceView.swift diff --git a/BlockV/Face/Face Views/FaceViewRoster.swift b/BlockV/Face/Face Views/FaceViewRoster.swift index 4f9cb81d..e5456b6a 100644 --- a/BlockV/Face/Face Views/FaceViewRoster.swift +++ b/BlockV/Face/Face Views/FaceViewRoster.swift @@ -49,7 +49,7 @@ public class FaceViewRoster { // embedded face views roster.register(ImageFaceView.self) roster.register(ImagePolicyFaceView.self) - roster.register(ProgressImageFaceView.self) + roster.register(ImageProgressFaceView.self) return roster }() diff --git a/BlockV/Face/Face Views/Image Progress/ImageProgressFaceView.swift b/BlockV/Face/Face Views/Image Progress/ImageProgressFaceView.swift new file mode 100644 index 00000000..67fde648 --- /dev/null +++ b/BlockV/Face/Face Views/Image Progress/ImageProgressFaceView.swift @@ -0,0 +1,281 @@ +// +// BlockV AG. Copyright (c) 2018, all rights reserved. +// +// Licensed under the BlockV SDK License (the "License"); you may not use this file or +// the BlockV SDK except in compliance with the License accompanying it. Unless +// required by applicable law or agreed to in writing, the BlockV SDK distributed under +// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +// ANY KIND, either express or implied. See the License for the specific language +// governing permissions and limitations under the License. +// + +import UIKit +import Nuke + +/// Native progress image face view +/// +/// Assumption: +/// Both the empty and full images have the same size. +class ImageProgressFaceView: FaceView { + + class var displayURL: String { return "native://progress-image-overlay" } + + // MARK: - Properties + + public private(set) var isLoaded: Bool = false + + private lazy var progressLabel: UILabel = { + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + label.text = "0%" + label.textAlignment = .right + label.textColor = UIColor.lightGray + label.font = UIFont.systemFont(ofSize: 13, weight: .medium) + return label + }() + + private lazy var emptyImageView: UIImageView = { + let imageView = UIImageView() + imageView.frame = self.bounds + imageView.autoresizingMask = [ .flexibleWidth, .flexibleHeight ] + imageView.contentMode = .scaleAspectFit + imageView.clipsToBounds = true + return imageView + }() + + private lazy var fullImageView: UIImageView = { + let imageView = UIImageView() + imageView.frame = self.bounds + imageView.autoresizingMask = [ .flexibleWidth, .flexibleHeight ] + imageView.contentMode = .scaleAspectFit + imageView.clipsToBounds = true + return imageView + }() + + // MARK: - Dynamic Vatom Private Properties + + private var progress: CGFloat { + return CGFloat(min(1, max(0, vatom.props.cloningScore))) + } + + // MARK: - Config + + /// Face model face configuration specification. + /// + /// Face config is immutable. + private struct Config { + + // defaults + var emptyImageName: String = "BaseImage" + var fullImageName: String = "ActivatedImage" + var direction: String = "up" + var paddingEnd: Double = 0 + var paddingStart: Double = 0 + var showPercentage: Bool = true + + /// Initialize using default values. + init() {} + + /// Initialize using face configuration. + init(_ faceConfig: JSON) { + + // assign iff not nil + self.emptyImageName ?= faceConfig["empty_image"]?.stringValue + self.fullImageName ?= faceConfig["full_image"]?.stringValue + self.direction ?= faceConfig["direction"]?.stringValue + self.showPercentage ?= faceConfig["show_percentage"]?.boolValue + + if let paddingEnd = faceConfig["padding_end"]?.floatValue { + self.paddingEnd ?= Double(paddingEnd) + } + if let paddingStart = faceConfig["padding_start"]?.floatValue { + self.paddingStart = Double(paddingStart) + } + } + + } + + /// Face configuration (immutable). + /// + /// On the server, Faces and Actions are mutable (irrespective of the published state of the template). Face Views + /// however treat face config as immutable. If the face config changes (typically by the publisher deleting and + /// re-adding the face) the Face View should be torn down and recreated. + /// + /// Dynamically responding to face and action changes is not a function of Face Views. For this reason, the config + /// struct immutalbe and is ONLY populated on init. + private let config: Config + + // MARK: - Initialization + + required init(vatom: VatomModel, faceModel: FaceModel) { + + // init face config (or legacy private section) fallback on default values + + if let config = faceModel.properties.config { + self.config = Config(config) // face config + } else if let config = vatom.private { + self.config = Config(config) // private section + } else { + self.config = Config() // default values + } + + super.init(vatom: vatom, faceModel: faceModel) + + self.addSubview(emptyImageView) + self.addSubview(fullImageView) + + // progress label + self.addSubview(progressLabel) + progressLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true + progressLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10).isActive = true + progressLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true + + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) should not be called on Face Views. Please use VatomView.") + } + + // MARK: - FaceView Lifecycle + + func load(completion: ((Error?) -> Void)?) { + + self.updateResources { (error) in + self.setNeedsLayout() + self.updateUI() + completion?(error) + } + + } + + func vatomChanged(_ vatom: VatomModel) { + + // update vatom + self.vatom = vatom + updateUI() + } + + func unload() { } + + // MARK: - View Lifecycle + + /// Updates the UI using local data. + private func updateUI() { + self.progressLabel.isHidden = !self.config.showPercentage + self.progressLabel.text = "\(Int(progress * 100))%" + + // request layout + self.setNeedsLayout() + self.layoutIfNeeded() + } + + override func layoutSubviews() { + super.layoutSubviews() + + // image size + guard let image = fullImageView.image else { return } + + // - Pixels + // size of image in pixels + let imagePixelSize = CGSize(width: (image.size.width * image.scale), height: (image.size.height * image.scale)) + + // rect of the image inside the image view + let contentClippingRect = self.fullImageView.contentClippingRect + + let paddingStart = contentClippingRect.height * CGFloat(self.config.paddingStart) / imagePixelSize.height + let paddingEnd = contentClippingRect.width * CGFloat(self.config.paddingEnd) / imagePixelSize.width + + let innerY = contentClippingRect.height - paddingStart - paddingEnd + let innerX = contentClippingRect.width - paddingStart - paddingEnd + + let innerProgressY = (contentClippingRect.height - paddingStart - paddingEnd) * progress + let innerProgressX = (contentClippingRect.width - paddingStart - paddingEnd) * progress + + // assuming up + + var maskRect: CGRect! + + switch self.config.direction.lowercased() { + + case "left": + let offset = paddingStart + innerProgressX + maskRect = CGRect(x: contentClippingRect.maxX - offset, + y: 0, + width: offset, + height: self.bounds.height) + case "right": + maskRect = CGRect(x: contentClippingRect.minX, + y: 0, + width: paddingStart + innerProgressX, + height: self.bounds.height) + case "down": + maskRect = CGRect(x: self.bounds.minX, + y: contentClippingRect.minY, + width: self.bounds.width, + height: paddingStart + innerProgressY) + default: // "up" + let topOffset: CGFloat = paddingEnd + (innerY - innerProgressY) + maskRect = CGRect(x: self.bounds.minX, + y: contentClippingRect.minY + topOffset, + width: self.bounds.width, + height: contentClippingRect.height - topOffset) + + } + + // mask in the full portion (as a function of the direction and progress) + let maskPath = CGPath.init(rect: maskRect, transform: nil) + maskLayer.path = maskPath + self.fullImageView.layer.mask = maskLayer + + } + + private var maskLayer = CAShapeLayer() + + // MARK: - Resource Management + + // group async events + private let dispatchGroup = DispatchGroup() + + /// Fetches required resources and populates the relevant `ImageView`s. The completion handler is called once all + /// images are downloaded (or an error is encountered). + private func updateResources(completion: ((Error?) -> Void)?) { + + // ensure required resources are present + guard + let emptyImageResource = vatom.props.resources.first(where: { $0.name == self.config.emptyImageName }), + let fullImageResource = vatom.props.resources.first(where: { $0.name == self.config.fullImageName }) + else { + printBV(error: "\(#file) - failed to extract resources.") + return + } + + // ensure encoding passes + guard + let emptyURL = try? BLOCKv.encodeURL(emptyImageResource.url), + let fullURL = try? BLOCKv.encodeURL(fullImageResource.url) + else { + printBV(error: "\(#file) - failed to encode resources.") + return + } + + dispatchGroup.enter() + dispatchGroup.enter() + + // load image (automatically handles reuse) + Nuke.loadImage(with: emptyURL, into: self.emptyImageView) { (_, _) in + self.dispatchGroup.leave() + } + + // load image (automatically handles reuse) + Nuke.loadImage(with: fullURL, into: self.fullImageView) { (_, _) in + self.dispatchGroup.leave() + } + + dispatchGroup.notify(queue: .main) { + self.isLoaded = true + completion?(nil) + } + + } + +} From f53d74ee7ad8f6bcc06daa0576bd0f5b795c7850 Mon Sep 17 00:00:00 2001 From: Cameron McOnie Date: Mon, 29 Oct 2018 13:49:15 +0200 Subject: [PATCH 5/6] remove --- .../ProgressImageFaceView.swift | 281 ------------------ 1 file changed, 281 deletions(-) delete mode 100644 BlockV/Face/Face Views/Progress Image/ProgressImageFaceView.swift diff --git a/BlockV/Face/Face Views/Progress Image/ProgressImageFaceView.swift b/BlockV/Face/Face Views/Progress Image/ProgressImageFaceView.swift deleted file mode 100644 index 335f8803..00000000 --- a/BlockV/Face/Face Views/Progress Image/ProgressImageFaceView.swift +++ /dev/null @@ -1,281 +0,0 @@ -// -// BlockV AG. Copyright (c) 2018, all rights reserved. -// -// Licensed under the BlockV SDK License (the "License"); you may not use this file or -// the BlockV SDK except in compliance with the License accompanying it. Unless -// required by applicable law or agreed to in writing, the BlockV SDK distributed under -// the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -// ANY KIND, either express or implied. See the License for the specific language -// governing permissions and limitations under the License. -// - -import UIKit -import Nuke - -/// Native progress image face view -/// -/// Assumption: -/// Both the empty and full images have the same size. -class ProgressImageFaceView: FaceView { - - class var displayURL: String { return "native://progress-image-overlay" } - - // MARK: - Properties - - public private(set) var isLoaded: Bool = false - - private lazy var progressLabel: UILabel = { - let label = UILabel() - label.translatesAutoresizingMaskIntoConstraints = false - label.text = "0%" - label.textAlignment = .right - label.textColor = UIColor.lightGray - label.font = UIFont.systemFont(ofSize: 13, weight: .medium) - return label - }() - - private lazy var emptyImageView: UIImageView = { - let imageView = UIImageView() - imageView.frame = self.bounds - imageView.autoresizingMask = [ .flexibleWidth, .flexibleHeight ] - imageView.contentMode = .scaleAspectFit - imageView.clipsToBounds = true - return imageView - }() - - private lazy var fullImageView: UIImageView = { - let imageView = UIImageView() - imageView.frame = self.bounds - imageView.autoresizingMask = [ .flexibleWidth, .flexibleHeight ] - imageView.contentMode = .scaleAspectFit - imageView.clipsToBounds = true - return imageView - }() - - // MARK: - Dynamic Vatom Private Properties - - private var progress: CGFloat { - return CGFloat(min(1, max(0, vatom.props.cloningScore))) - } - - // MARK: - Config - - /// Face model face configuration specification. - /// - /// Face config is immutable. - private struct Config { - - // defaults - var emptyImageName: String = "BaseImage" - var fullImageName: String = "ActivatedImage" - var direction: String = "up" - var paddingEnd: Double = 0 - var paddingStart: Double = 0 - var showPercentage: Bool = true - - /// Initialize using default values. - init() {} - - /// Initialize using face configuration. - init(_ faceConfig: JSON) { - - // assign iff not nil - self.emptyImageName ?= faceConfig["empty_image"]?.stringValue - self.fullImageName ?= faceConfig["full_image"]?.stringValue - self.direction ?= faceConfig["direction"]?.stringValue - self.showPercentage ?= faceConfig["show_percentage"]?.boolValue - - if let paddingEnd = faceConfig["padding_end"]?.floatValue { - self.paddingEnd ?= Double(paddingEnd) - } - if let paddingStart = faceConfig["padding_start"]?.floatValue { - self.paddingStart = Double(paddingStart) - } - } - - } - - /// Face configuration (immutable). - /// - /// On the server, Faces and Actions are mutable (irrespective of the published state of the template). Face Views - /// however treat face config as immutable. If the face config changes (typically by the publisher deleting and - /// re-adding the face) the Face View should be torn down and recreated. - /// - /// Dynamically responding to face and action changes is not a function of Face Views. For this reason, the config - /// struct immutalbe and is ONLY populated on init. - private let config: Config - - // MARK: - Initialization - - required init(vatom: VatomModel, faceModel: FaceModel) { - - // init face config (or legacy private section) fallback on default values - - if let config = faceModel.properties.config { - self.config = Config(config) // face config - } else if let config = vatom.private { - self.config = Config(config) // private section - } else { - self.config = Config() // default values - } - - super.init(vatom: vatom, faceModel: faceModel) - - self.addSubview(emptyImageView) - self.addSubview(fullImageView) - - // progress label - self.addSubview(progressLabel) - progressLabel.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true - progressLabel.rightAnchor.constraint(equalTo: self.rightAnchor, constant: -10).isActive = true - progressLabel.heightAnchor.constraint(equalToConstant: 20).isActive = true - - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) should not be called on Face Views. Please use VatomView.") - } - - // MARK: - FaceView Lifecycle - - func load(completion: ((Error?) -> Void)?) { - - self.updateResources { (error) in - self.setNeedsLayout() - self.updateUI() - completion?(error) - } - - } - - func vatomChanged(_ vatom: VatomModel) { - - // update vatom - self.vatom = vatom - updateUI() - } - - func unload() { } - - // MARK: - View Lifecycle - - /// Updates the UI using local data. - private func updateUI() { - self.progressLabel.isHidden = !self.config.showPercentage - self.progressLabel.text = "\(Int(progress * 100))%" - - // request layout - self.setNeedsLayout() - self.layoutIfNeeded() - } - - override func layoutSubviews() { - super.layoutSubviews() - - // image size - guard let image = fullImageView.image else { return } - - // - Pixels - // size of image in pixels - let imagePixelSize = CGSize(width: (image.size.width * image.scale), height: (image.size.height * image.scale)) - - // rect of the image inside the image view - let contentClippingRect = self.fullImageView.contentClippingRect - - let paddingStart = contentClippingRect.height * CGFloat(self.config.paddingStart) / imagePixelSize.height - let paddingEnd = contentClippingRect.width * CGFloat(self.config.paddingEnd) / imagePixelSize.width - - let innerY = contentClippingRect.height - paddingStart - paddingEnd - let innerX = contentClippingRect.width - paddingStart - paddingEnd - - let innerProgressY = (contentClippingRect.height - paddingStart - paddingEnd) * progress - let innerProgressX = (contentClippingRect.width - paddingStart - paddingEnd) * progress - - // assuming up - - var maskRect: CGRect! - - switch self.config.direction.lowercased() { - - case "left": - let offset = paddingStart + innerProgressX - maskRect = CGRect(x: contentClippingRect.maxX - offset, - y: 0, - width: offset, - height: self.bounds.height) - case "right": - maskRect = CGRect(x: contentClippingRect.minX, - y: 0, - width: paddingStart + innerProgressX, - height: self.bounds.height) - case "down": - maskRect = CGRect(x: self.bounds.minX, - y: contentClippingRect.minY, - width: self.bounds.width, - height: paddingStart + innerProgressY) - default: // "up" - let topOffset: CGFloat = paddingEnd + (innerY - innerProgressY) - maskRect = CGRect(x: self.bounds.minX, - y: contentClippingRect.minY + topOffset, - width: self.bounds.width, - height: contentClippingRect.height - topOffset) - - } - - // mask in the full portion (as a function of the direction and progress) - let maskPath = CGPath.init(rect: maskRect, transform: nil) - maskLayer.path = maskPath - self.fullImageView.layer.mask = maskLayer - - } - - private var maskLayer = CAShapeLayer() - - // MARK: - Resource Management - - // group async events - private let dispatchGroup = DispatchGroup() - - /// Fetches required resources and populates the relevant `ImageView`s. The completion handler is called once all - /// images are downloaded (or an error is encountered). - private func updateResources(completion: ((Error?) -> Void)?) { - - // ensure required resources are present - guard - let emptyImageResource = vatom.props.resources.first(where: { $0.name == self.config.emptyImageName }), - let fullImageResource = vatom.props.resources.first(where: { $0.name == self.config.fullImageName }) - else { - printBV(error: "\(#file) - failed to extract resources.") - return - } - - // ensure encoding passes - guard - let emptyURL = try? BLOCKv.encodeURL(emptyImageResource.url), - let fullURL = try? BLOCKv.encodeURL(fullImageResource.url) - else { - printBV(error: "\(#file) - failed to encode resources.") - return - } - - dispatchGroup.enter() - dispatchGroup.enter() - - // load image (automatically handles reuse) - Nuke.loadImage(with: emptyURL, into: self.emptyImageView) { (_, _) in - self.dispatchGroup.leave() - } - - // load image (automatically handles reuse) - Nuke.loadImage(with: fullURL, into: self.fullImageView) { (_, _) in - self.dispatchGroup.leave() - } - - dispatchGroup.notify(queue: .main) { - self.isLoaded = true - completion?(nil) - } - - } - -} From a84da963e975805f3e43dec7c26ab05734cfabd7 Mon Sep 17 00:00:00 2001 From: Cameron McOnie Date: Mon, 29 Oct 2018 14:27:47 +0200 Subject: [PATCH 6/6] fix release configuration explicitly set swift language to 4 --- Example/BlockV.xcodeproj/project.pbxproj | 7 +++++-- .../xcshareddata/xcschemes/BlockV-Example.xcscheme | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Example/BlockV.xcodeproj/project.pbxproj b/Example/BlockV.xcodeproj/project.pbxproj index 9027102e..23c0c458 100644 --- a/Example/BlockV.xcodeproj/project.pbxproj +++ b/Example/BlockV.xcodeproj/project.pbxproj @@ -23,7 +23,7 @@ AD920A5B215FB47200CEFC1D /* UserInfoTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD920A58215FB47100CEFC1D /* UserInfoTableViewController.swift */; }; AD920A5C215FB47200CEFC1D /* PasswordTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD920A59215FB47200CEFC1D /* PasswordTableViewController.swift */; }; AD920A5D215FB47200CEFC1D /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD920A5A215FB47200CEFC1D /* ProfileViewController.swift */; }; - CDC268C7232CDB5AF1386116 /* Pods_BlockV_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DE93FD598C0B61200B996A4 /* Pods_BlockV_Example.framework */; }; + CDC268C7232CDB5AF1386116 /* Pods_BlockV_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DE93FD598C0B61200B996A4 /* Pods_BlockV_Example.framework */; settings = {ATTRIBUTES = (Required, ); }; }; D519A84B205E83AB006B0D19 /* VatomDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D519A84A205E83AB006B0D19 /* VatomDetailTableViewController.swift */; }; D530759C21119E2A00DE7FD0 /* MockModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5307588211199C200DE7FD0 /* MockModels.swift */; }; D530759F2111CC5200DE7FD0 /* Test+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = D530759E2111CC5200DE7FD0 /* Test+Utils.swift */; }; @@ -540,7 +540,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/SwiftLint/swiftlint"; + shellScript = "${PODS_ROOT}/SwiftLint/swiftlint\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -671,6 +671,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; }; name = Debug; }; @@ -718,6 +719,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; VALIDATE_PRODUCT = YES; }; name = Dev; @@ -968,6 +970,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; VALIDATE_PRODUCT = YES; }; name = Prod; diff --git a/Example/BlockV.xcodeproj/xcshareddata/xcschemes/BlockV-Example.xcscheme b/Example/BlockV.xcodeproj/xcshareddata/xcschemes/BlockV-Example.xcscheme index 06429f09..5feb9978 100644 --- a/Example/BlockV.xcodeproj/xcshareddata/xcschemes/BlockV-Example.xcscheme +++ b/Example/BlockV.xcodeproj/xcshareddata/xcschemes/BlockV-Example.xcscheme @@ -119,7 +119,7 @@ buildConfiguration = "Debug">