Skip to content
This repository has been archived by the owner on Sep 21, 2021. It is now read-only.

Commit

Permalink
Refactore observers
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikita Ermolenko authored and Nikita Ermolenko committed Mar 12, 2017
1 parent 054cff4 commit e0c7a58
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 268 deletions.
8 changes: 4 additions & 4 deletions NxEnabled.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
8422EF2F1E4B692E00B29E9F /* BaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8422EF2E1E4B692E00B29E9F /* BaseTests.swift */; };
846931121E45AED800008405 /* NxEnabled.h in Headers */ = {isa = PBXBuildFile; fileRef = 846931111E45AED800008405 /* NxEnabled.h */; settings = {ATTRIBUTES = (Public, ); }; };
846931191E45B0C700008405 /* Button+Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846931161E45B0C700008405 /* Button+Observer.swift */; };
8469311A1E45B0C700008405 /* Observers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846931171E45B0C700008405 /* Observers.swift */; };
8469311A1E45B0C700008405 /* Observer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846931171E45B0C700008405 /* Observer.swift */; };
8469311B1E45B0C700008405 /* TextInput+Textable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 846931181E45B0C700008405 /* TextInput+Textable.swift */; };
848853151E4B00F700AC207E /* JustReturnedEnabledValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 848853141E4B00F700AC207E /* JustReturnedEnabledValueTests.swift */; };
848853171E4B00F700AC207E /* NxEnabled.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 846931021E45AD5F00008405 /* NxEnabled.framework */; };
Expand All @@ -36,7 +36,7 @@
8469310F1E45ADDD00008405 /* Info-iOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-iOS.plist"; sourceTree = "<group>"; };
846931111E45AED800008405 /* NxEnabled.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NxEnabled.h; sourceTree = "<group>"; };
846931161E45B0C700008405 /* Button+Observer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Button+Observer.swift"; sourceTree = "<group>"; };
846931171E45B0C700008405 /* Observers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observers.swift; sourceTree = "<group>"; };
846931171E45B0C700008405 /* Observer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Observer.swift; sourceTree = "<group>"; };
846931181E45B0C700008405 /* TextInput+Textable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TextInput+Textable.swift"; sourceTree = "<group>"; };
846931201E45B75300008405 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
846931211E45B75300008405 /* NxEnabled.podspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = NxEnabled.podspec; sourceTree = "<group>"; };
Expand Down Expand Up @@ -94,7 +94,7 @@
8469310F1E45ADDD00008405 /* Info-iOS.plist */,
846931111E45AED800008405 /* NxEnabled.h */,
846931161E45B0C700008405 /* Button+Observer.swift */,
846931171E45B0C700008405 /* Observers.swift */,
846931171E45B0C700008405 /* Observer.swift */,
846931181E45B0C700008405 /* TextInput+Textable.swift */,
);
path = Sources;
Expand Down Expand Up @@ -225,7 +225,7 @@
files = (
8469311B1E45B0C700008405 /* TextInput+Textable.swift in Sources */,
846931191E45B0C700008405 /* Button+Observer.swift in Sources */,
8469311A1E45B0C700008405 /* Observers.swift in Sources */,
8469311A1E45B0C700008405 /* Observer.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
69 changes: 42 additions & 27 deletions Sources/Button+Observer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,28 @@ import UIKit.UIButton

private var observerTypeAssociationKey: UInt8 = 0

public typealias ConfigurationHandler1 = (String) -> Bool
public typealias ConfigurationHandler2 = (String, String) -> Bool
public typealias ConfigurationHandler3 = (String, String, String) -> Bool
public typealias ConfigurationHandler4 = (String, String, String, String) -> Bool
public typealias ConfigurationHandler5 = (String, String, String, String, String) -> Bool
public typealias ConfigurationHandler6 = (String, String, String, String, String, String) -> Bool

extension UIButton {

/// Special object which observes text for all textable values

fileprivate var observer: NUIObserver? {
get { return objc_getAssociatedObject(self, &observerTypeAssociationKey) as! NUIObserver? }
get { return objc_getAssociatedObject(self, &observerTypeAssociationKey) as? NUIObserver }
set { objc_setAssociatedObject(self, &observerTypeAssociationKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) }
}

var enabledHandler: (Bool) -> Void {
return { [unowned self] isEnabled in
self.isEnabled = isEnabled
}
}

/// Clears all observers from textable values, which have been configured by `isEnabled` method.
///
/// - note: Don't forget to call this method when you need to remove observers from `textableValue`s. Usually it's a `deinit` method.
Expand All @@ -44,11 +57,13 @@ extension UIButton {
///

public func isEnabled<T: NSObject>(by textableValue: T,
configurationHandler: @escaping ConfigurationHandler1) where T: Textable {
let inputs = [textableValue]
observer = NUIObserver1(textableValues: inputs, configurationHandler: configurationHandler) { [unowned self] isEnabled in
self.isEnabled = isEnabled
}
configurationHandler: @escaping ConfigurationHandler1)
where T: Textable {

let inputs = [textableValue]
observer = NUIObserver(textableValues: inputs, configurationHandler: { texts in
configurationHandler(texts[0])
}, enabledHandler: enabledHandler)
}
}

Expand All @@ -68,14 +83,14 @@ extension UIButton {
///

public func isEnabled<T1: NSObject, T2: NSObject>(by textableValue1: T1,
_ textableValue2: T2,
configurationHandler: @escaping ConfigurationHandler2)
_ textableValue2: T2,
configurationHandler: @escaping ConfigurationHandler2)
where T1: Textable, T2: Textable {

let inputs = [textableValue1, textableValue2]
observer = NUIObserver2(textableValues: inputs, configurationHandler: configurationHandler) { [unowned self] isEnabled in
self.isEnabled = isEnabled
}
observer = NUIObserver(textableValues: inputs, configurationHandler: { texts in
configurationHandler(texts[0], texts[1])
}, enabledHandler: enabledHandler)
}
}

Expand All @@ -100,11 +115,11 @@ extension UIButton {
_ textableValue3: T3,
configurationHandler: @escaping ConfigurationHandler3)
where T1: Textable, T2: Textable, T3: Textable {

let inputs = [textableValue1, textableValue2, textableValue3]
observer = NUIObserver3(textableValues: inputs, configurationHandler: configurationHandler) { [unowned self] isEnabled in
self.isEnabled = isEnabled
}
observer = NUIObserver(textableValues: inputs, configurationHandler: { texts in
configurationHandler(texts[0], texts[1], texts[2])
}, enabledHandler: enabledHandler)
}
}

Expand All @@ -131,11 +146,11 @@ extension UIButton {
_ textableValue4: T4,
configurationHandler: @escaping ConfigurationHandler4)
where T1: Textable, T2: Textable, T3: Textable, T4: Textable {

let inputs = [textableValue1, textableValue2, textableValue3, textableValue4]
observer = NUIObserver4(textableValues: inputs, configurationHandler: configurationHandler) { [unowned self] isEnabled in
self.isEnabled = isEnabled
}
observer = NUIObserver(textableValues: inputs, configurationHandler: { texts in
configurationHandler(texts[0], texts[1], texts[2], texts[3])
}, enabledHandler: enabledHandler)
}
}

Expand Down Expand Up @@ -164,11 +179,11 @@ extension UIButton {
_ textableValue5: T5,
configurationHandler: @escaping ConfigurationHandler5)
where T1: Textable, T2: Textable, T3: Textable, T4: Textable, T5: Textable {

let inputs = [textableValue1, textableValue2, textableValue3, textableValue4, textableValue5]
observer = NUIObserver5(textableValues: inputs, configurationHandler: configurationHandler) { [unowned self] isEnabled in
self.isEnabled = isEnabled
}
observer = NUIObserver(textableValues: inputs, configurationHandler: { texts in
configurationHandler(texts[0], texts[1], texts[2], texts[3], texts[4])
}, enabledHandler: enabledHandler)
}
}

Expand Down Expand Up @@ -199,10 +214,10 @@ extension UIButton {
_ textableValue6: T6,
configurationHandler: @escaping ConfigurationHandler6)
where T1: Textable, T2: Textable, T3: Textable, T4: Textable, T5: Textable, T6: Textable {

let inputs = [textableValue1, textableValue2, textableValue3, textableValue4, textableValue5, textableValue6]
observer = NUIObserver6(textableValues: inputs, configurationHandler: configurationHandler) { [unowned self] isEnabled in
self.isEnabled = isEnabled
}
observer = NUIObserver(textableValues: inputs, configurationHandler: { texts in
configurationHandler(texts[0], texts[1], texts[2], texts[3], texts[4], texts[5])
}, enabledHandler: enabledHandler)
}
}
87 changes: 87 additions & 0 deletions Sources/Observer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// Observer.swift
// NxEnabled
//
// Created by Nikita Ermolenko on 12/01/2017.
// Copyright © 2017 Nikita. All rights reserved.
//

import UIKit.UIControl

typealias EnabledHandler = (Bool) -> Void
typealias ConfigurationHandler = ([String]) -> Bool

final class NUIObserver: NSObject {

private var enabledHandler: EnabledHandler
private var textableValues: NSPointerArray /// Weak array of textable values.
private var configurationHandler: ConfigurationHandler

deinit {
textableValues.allObjects.map { textableValueTuple(by: $0) }.forEach { (value, key) in
value.removeObserver(self, forKeyPath: key)
}
}

init(textableValues: [NSObject], configurationHandler: @escaping ConfigurationHandler, enabledHandler: @escaping EnabledHandler) {
self.configurationHandler = configurationHandler
self.enabledHandler = enabledHandler
self.textableValues = NSPointerArray(options: .weakMemory)

super.init()

textableValues.forEach { value in
self.textableValues.addPointer(Unmanaged.passUnretained(value).toOpaque())
}

let textableValueTuples = textableValues.map { textableValueTuple(by: $0) }
textableValueTuples.forEach { (value, key) in
value.addObserver(self, forKeyPath: key, options: [.new, .initial], context: nil)

switch value {
case let value as UIControl: value.addTarget(self, action:#selector(textableValueChanged), for: .editingChanged)
case let value as UITextView: NotificationCenter.default.addObserver(self,
selector: #selector(textVewChanged),
name: .UITextViewTextDidChange,
object: value)
default: break
}
}
}

// MARK: Text changing events

/// This method is called when the `UITextViewTextDidChange` notification of `UITextView` is triggered.
@objc private func textVewChanged() {
configureEnabling()
}

/// This method is called when the `editingChanged` event of `UIControl` is triggered.
@objc private func textableValueChanged() {
configureEnabling()
}

override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
configureEnabling()
}

// MARK: Helpers

private func configureEnabling() {
let texts = textableValues.allObjects.map { textableValue -> String in
let object = textableValue as! NSObject
let key = (object as! Textable).textKey
return object.value(forKey: key) as? String ?? ""
}
enabledHandler(configurationHandler(texts))
}

private func textableValueTuple(by value: Any) -> (value:NSObject, key:String) {
let textableValue = value as! NSObject
let key = (textableValue as! Textable).textKey
return (textableValue, key)
}
}
Loading

0 comments on commit e0c7a58

Please sign in to comment.