Getting a base value into ValueTransformer - swift

I'm trying to output a value relative to a base value that is calculated elsewhere. I was hoping that ValueTransformer would do the trick but I have no idea how to get a base value into the ValueTransformer.The initWithBase func shown below does not work. Any advice would be welcome. Thanks.
import Cocoa
class PercentTransformer: ValueTransformer {
var baseValue: Double = 1
override class func transformedValueClass() -> Swift.AnyClass {
return NSString.self
}
override class func allowsReverseTransformation() -> Bool {
return false
}
func initWithBase(base: Double) {
baseValue = base
return
}
override func transformedValue(_ value: Any?) -> Any? {
let inputValue = value as! Double
if inputValue == 0.0 {
return "--------------"
}
// calculate output value
let percent = inputValue/baseValue * 100
let output = String(format: "%.2e", inputValue) + " " + String(format: "%.1f", percent) + "%"
return output as NSString
}
}

Related

Swift ValueTransformer old "good" code now complains with contemporary swift

I have an old simple ValueTransformer. Written just following the conventional pattern to support a GUI interface to edit a specific encoded file format. I recently has cause to regenerate with current compiler and it is complaining bitterly about converting Bool.self to AnyClass. Yeah, OK, I understand that Bool is no longer a Class (if it ever was) it is a frozen Struct. So the question is that of, is there a Swift way of continuing to use this ValueTransformer for a struct rather than a class?
I can see an ugly solution of wrapping a Bool in a Class and using that but is poor design at best, but if needs must....
Am I missing something obvious in the ever changing world ?
The complaint from the the compiler is on the single line in transformedValueClass
return Bool.self as! AnyClass
Cast from 'Bool.Type' to unrelated type 'AnyClass' (aka 'AnyObject.Type') always fails
class StringToBoolTransformer : ValueTransformer
{
var boolValue : Bool?
override func transformedValue(_ value: Any?) -> Any? {
if let stringRep = value as? String
{
if stringRep.count == 1 {
boolValue = (stringRep == "1" ? true :(stringRep == "0" ? false: nil))
}
}
return boolValue
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
var boolAsString : String?
if let boolValue = value as? Bool {
boolAsString = boolValue ? "1" : "0"
}
return boolAsString
}
override class func transformedValueClass() -> AnyClass
{
return Bool.self as! AnyClass
}
override class func allowsReverseTransformation() -> Bool {
return true
}
}
(NS)ValueTransformer relies on Objective-C and the corresponding class of Bool is NSNumber.
I made the code a bit more contemporary 😉
class StringToBoolTransformer : ValueTransformer
{
override func transformedValue(_ value: Any?) -> Any? {
guard let stringRep = value as? String,
stringRep.count == 1,
["0","1"].contains(stringRep) else { return nil }
let boolValue = stringRep == "1"
return NSNumber(booleanLiteral: boolValue)
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let number = value as? NSNumber else { return nil }
return number.boolValue ? "1" : "0"
}
override class func transformedValueClass() -> AnyClass
{
return NSNumber.self
}
override class func allowsReverseTransformation() -> Bool {
return true
}
}
In Swift Bool is a struct, NOT a class. So you can never cast it to AnyClass.
What you can try as a workaround is use NSNumber class as a storage for bool via NSNumber.init(value: Bool) and then return NSNumber.self from your implementation.

how verify if a method was called in a concrete class at unit test in swift

I have this scenario:
class X {
init(y: ProtocolA)
func foo(){
if(y.isSomething()){
methodA()
} else {
methodB()
}
}
func methodA(){
// any
}
func methodB(){
// any
}
}
class Y : ProtocolA {
func isSomething(): Bool { return true OR false }
}
i wanna test class X,
i will mock ProtocolA to return in isSomething() method true or false in two different test to know if methodA or methodB was called.
What the best strategy to solve this?
ps: with mockito, using a Spy with verify this is very easy, but with Swift this is very painful
edit:
well, i do this:
first, the parts:
class X { init(y: Y) }
protocol Y { func isSomething() -> Bool }
now, the struct for test: mock and spy object
typealias VerifyMethodAssert = (count: Int, parameters: [Any]?, returnn: Any?)
Configurable Mock for Dependency
class YMock : Y {
init(configure: Bool)
func isSomething{ return configure }
}
Spy for concrete class
class XSpy : X {
private let y: Y
var verify: [String: VerifyMethodAssert] = [
"methodA()": (count: 0, parameters: nil, returnn: nil)
"methodB()": (count: 0, parameters: nil, returnn: nil)
]
var nothing: [String: Bool] = [
"methodA()": false
"methodB()": false
]
init(y: Y, verify: [String: VerifyMethodAssert]?, nothing: [String: Bool]?)
func methodA(){
verify["\(#function)"] = (count: verify["\(#function)"]!.count + 1, parameters: nil,
returnn: nothing["\(#function)"]! ? nil : super.methodA())
}
func methodB(doNothing: Bool = false){
verify["\(#function)"] = (count: verify["\(#function)"]!.count + 1, parameters: nil,
returnn: nothing["\(#function)"]! ? nil : super.methodB())
}
}
and test:
class XTest : QuickSpec {
override func spec(){
describe("a position view model"){
it("test 1"){
let y = Y(configure: true)
let x = XSpy(y: y)
x.foo()
expect(1).to(x.verify["methodA()"].count)
expect(0).to(x.verify["methodB()"].count)
}
it("test 2"){
let y = Y(configure: true)
let x = XSpy(y: y)
x.foo()
expect(0).to(x.verify["methodA()"].count)
expect(1).to(x.verify["methodB()"].count)
}
}
}
}
There is no out of the box way as far as I know.
One way to do it is to check a counter:
class X {
var countA: Int = 0
var countB: Int = 0
init(y: ProtocolA)
func foo(){
if(y.isSomething()){
methodA()
} else {
methodB()
}
}
func methodA(){
countA += 1
// any
}
func methodB(){
countB += 1
// any
}
}
This approach is also suggested here.
In this specific case you can subclass X and use an associated object to hold invocation counts, you can generalize this if you see yourself using it again and again:
final class TestX: X {
private struct AssociatedKeys {
static var invocations = "\(String(describing: type(of: TestX.self)))-invocations"
}
func invocations(for method: String) -> Int {
return invocations[method] ?? 0
}
private var invocations: [String: Int] {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.invocations) as? [String: Int] ?? [:]
}
set {
objc_setAssociatedObject( self, &AssociatedKeys.invocations, newValue as NSDictionary, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
override func methodA(){
super.methodA()
invocations["methodA"] = (invocations["methodA"] ?? 0) + 1
}
override func methodB(){
super.methodB()
invocations["methodB"] = (invocations["methodB"] ?? 0) + 1
}
}
let x = TestX(y: Y())
x.invocations(for: "methodA") //0
x.foo()
x.invocations(for: "methodA") //1

How can I get a number input from a TextField?

I will get right to the question.
var a = 0
var b = 20
I want a user to input number into TextField and I could save that number into variable A. Then I want to do an if statement where
if a == b {
//code
}
What I am having trouble is getting that number input from the textfield.
You can check the input in the textfield for get the number
Swift4
let text = textField.text ?? ""
guard let number = Int(text) else {
print("Must be input the number")
return
}
// Do your task with number
a = number
Try this class Functions
class SourceVC: UIViewController
{
#IBOutlet weak var sampleTF: UITextField!{
didSet{
sampleTF.delegate = self
}
}
/// TF value
var a : Int = 0
var b : Int = 20
override func viewDidLoad() {
super.viewDidLoad()
}
/// Assuming this button action to get Value from TF
#IBAction func naviagteToDestination(_ sender: Any)
{
a = Int((sampleTF.text?.trimmingCharacters(in: .whitespaces))!)!
if a == b {
print("Execute")
}
else{
print("Dont Execute")
}
}
}
extension SourceVC : UITextFieldDelegate
{
/// This will Limit TF to accept only Numbers
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool
{
let allowedCharacters = CharacterSet.decimalDigits
let characterSet = CharacterSet(charactersIn: string)
return allowedCharacters.isSuperset(of: characterSet)
}
}

Swift: Reference to a generic class

Here's the code,
protocol TestType : AnyObject {
}
class Generic<TestType> : NSObject {
private let filterFunction : (TestType,String) -> Bool
init(filter: #escaping (TestType,String) -> Bool) {
filterFunction = filter
}
}
class Parent : UIViewController {
public var generic : Generic<TestType>!
}
class Child : Parent {
override func viewDidLoad() {
// ERROR THIS LINE
generic = Generic<Object>(filter: { (str1, str2) -> Bool in
return true
})
}
}
class Object : TestType {
}
The error is:
Cannot assign value of type 'Generic<Object>' to type 'Generic<TestType>!'
I tried many things, like typealias, but can't compile the code.
The problem is that i don't want a Parent<TestType> or Child<TestType> class, since i want to be able to use it in IB.
How can i store a reference of Generic in the Parent, and initialize it in Child (dynamically, by setting the concrete TestType like Object or another)
I finally succeed doing what i wanted!
Not perfect at all, feel free to comment for architecture improvements (especially on the asBaseProtocol() part...)
Here's my complete code (Swift 3.0)
DataFilter
protocol DataFilterDelegate : class {
func didFilter()
func didUpdateItems()
}
class DataFilter<T> {
public weak var delegate : DataFilterDelegate?
private var items : [SelectableItem<T>]?
private var filteredItems : [SelectableItem<T>]?
var source: [SelectableItem<T>]? {
get {
if filteredItems != nil {
return filteredItems
}
return items
}
}
var filter : (T,String) -> Bool
var populateCell : (T) -> UITableViewCell
init(filter : #escaping (T,String) -> Bool, populateCell: #escaping (T) -> UITableViewCell) {
self.filter = filter
self.populateCell = populateCell
}
func updateItems(_ items: [T]) {
self.items = [SelectableItem<T>]()
for item in items {
self.items?.append(SelectableItem(item))
}
delegate?.didUpdateItems()
}
func filterItems(text : String) {
filteredItems = (text == "") ? nil : items?.filter { item in
filter(item.item, text)
}
delegate?.didFilter()
}
func selectedItems() -> [T]? {
guard let items = items else {
return nil
}
var selectedItems = [T]()
for item in items {
if item.isSelected {
selectedItems.append(item.item)
}
}
return selectedItems
}
}
extension DataFilter where T : FIRDataObject {
func asBaseProtocol() -> DataFilter<FIRDataObject> {
return DataFilter<FIRDataObject>(filter: filterAsBaseProtocol(), populateCell: populateCellAsBaseProtocol())
}
private func filterAsBaseProtocol() -> ((FIRDataObject,String) -> Bool) {
return { (object, text) -> Bool in
self.filter(object as! T, text)
}
}
private func populateCellAsBaseProtocol() -> ((FIRDataObject) -> UITableViewCell) {
return { (object) -> UITableViewCell in
self.populateCell(object as! T)
}
}
}
ParentViewController Class
class ParentViewController : UIViewController {
public var dataFilter : DataFilter<FIRDataObject>? {
didSet {
dataFilter!.delegate = self
}
}
// Some Functions using dataFilter
}
ChildViewController Class
class ChildViewController : Parent {
// Stored as a variable to not have to cast objects to the good type everytime I access dataFilter
private var patientDataFilter = DataFilter<Patient>(filter: { patient, text in
patient.firstname.contains(text) ||
patient.lastname.contains(text)
}
, populateCell: { patient in
let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "Patient")
cell.textLabel?.text = patient.lastname + " " + patient.firstname
cell.detailTextLabel?.text = patient.postalCode + " " + patient.city
return cell
})
override func viewDidLoad() {
super.viewDidLoad()
dataFilter = patientDataFilter.asBaseProtocol()
}
func someFunc() {
let patient1 = patientDataFilter.source[0].item
// OR
let patient2 = dataFilter.source[0].item as! Patient
}
}

Mixpanel does not work with swift

Cocoapods is not ready for Swift yet. When I implemented Mixpanel into my iOS project that uses Swift, I got several errors.
How to fix this issue and make mixpanel to work?
Add this line of code to every file that is giving an error
#import <UIKit/UIKit.h>
Also need to add these in your linked frameworks and libraries:
libicucore.dylib
CFNetwork.framework
Security.framework
Then add this to your Bridging-Header.h file:
#import "Mixpanel.h"
Then add this piece of code in your appdelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
//other stuff here
Mixpanel.sharedInstanceWithToken("yourtokennumbergoeshere")
var mixPanel = Mixpanel.sharedInstance()
mixPanel.track("Finished Launching", properties: ["name1":"property1","name2":"property2"])
return true
}
Change your #import "mixpanel.h" to #import "Mixpanel/Mixpanel.h"
Use this unofficial Swift client! Probably not as good as the real thing, but it will do for basic tracking. https://github.com/soffes/Mixpanel
Getting A/B Testing to work in Swift is not trivial as the macro is not supported. Feel free to use this gist :
https://gist.github.com/ohad7/8ce12a432773fe3b1bf3
import Foundation
import Mixpanel
public class Tweaks : NSObject, MPTweakObserver {
public static let sharedInstance = Tweaks(tweaks: [ ExecuteDiskCleanup])
public static let ExecuteDiskCleanup = Tweak.Booln(name: "Cleanup-Disk", defaultValue:false)
private var values = [String:AnyObject]()
private let tweaks: [Tweak]
public init(tweaks: [Tweak]) {
self.tweaks = tweaks
let defaults = NSUserDefaults.standardUserDefaults()
for tweak in tweaks {
if let value: AnyObject = defaults.objectForKey(Tweaks.generateKey(tweak.name)) {
self.values[tweak.name] = value
println("Initialized Tweak \(tweak.name) with value: \(value)")
}
}
}
public func setup() {
tweaks.foreach(){ tweak in
let theTweak = MPTweak(name: tweak.name, andEncoding: "UTF-8")
theTweak.defaultValue = tweak.defaultValue
theTweak.addObserver(self)
MPTweakStore.sharedInstance().addTweak(theTweak)
}
Mixpanel.sharedInstance().checkForVariantsOnActive = true
Mixpanel.sharedInstance().joinExperiments()
}
public func get(tweaks: [Tweak]) -> [String:AnyObject] {
var result = [String:AnyObject]()
synced(values) {
tweaks.foreach(){ tweak in
result[tweak.name] = self.values[tweak.name]
}
}
return result
}
public func tweakDidChange(tweak: MPTweak) {
println("tweakDidChange Tweak \(tweak.name) current value :\(tweak.currentValue)")
synced(self.values) {
let localTweak = self.tweaks.filter{ $0.name == tweak.name}.first
if let localTweak = localTweak{
switch (localTweak) {
case .Booln:
self.values[tweak.name] = Bool(tweak.currentValue as? Int == 1)
default:
self.values[tweak.name] = tweak.currentValue
}
println("Tweak \(localTweak.name) -> \(self.values[localTweak.name])")
NSUserDefaults.standardUserDefaults().setObject(self.values[localTweak.name], forKey: Tweaks.generateKey(localTweak.name))
NSUserDefaults.standardUserDefaults().synchronize()
}
}
}
private class func generateKey(key: String) -> String {
return "mp_tweak_\(key)"
}
}
public enum Tweak {
case Str(name: String, defaultValue: String)
case Booln(name: String, defaultValue: Bool)
case Integer(name: String, defaultValue: Int)
case Flt(name: String, defaultValue: Float)
var name : String {
switch (self) {
case let .Str(n, d): return n
case let .Booln(n, d): return n
case let .Integer(n, d): return n
case let .Flt(n, d): return n
}
}
var defaultValue : AnyObject {
switch (self) {
case let .Str(n, d): return d
case let .Booln(n, d): return d
case let .Integer(n, d): return d
case let .Flt(n, d): return d
}
}
public func get<T>() -> T? {
return get(self.defaultValue as? T)
}
public func get<T>(defaultValue: T?) -> T? {
var value = synced(Tweaks.sharedInstance.values){
return Tweaks.sharedInstance.values[self.name] as? T
}
return value != nil ? value : defaultValue
}
}
// Examples :
// public static let SomeIntegerZero = Tweak.Integer(name: "SomeIntegerZero", defaultValue:0)
// public static let SomeIntegerNonZero = Tweak.Integer(name: "SomeIntegerNonZero", defaultValue:6666)
// public static let SomeBooleanFalse = Tweak.Booln(name: "SomeBooleanFalse", defaultValue:false)
// public static let SomeBooleanTrue = Tweak.Booln(name: "SomeBooleanTrue", defaultValue:true)
// public static let SomeStringEmpty = Tweak.Str(name: "SomeEmptyString", defaultValue:"")
// public static let SomeStringFull = Tweak.Str(name: "SomeFullString", defaultValue:"full")
/*** Utilities - sync methods ***/
func synced<T>(lock: AnyObject, closure: () -> T) -> T {
objc_sync_enter(lock)
let result = closure()
objc_sync_exit(lock)
return result
}
func synced(lock: AnyObject, closure: () -> ()) {
objc_sync_enter(lock)
closure()
objc_sync_exit(lock)
}
Looks like Mixpanel has posted some official documentation on this:
https://mixpanel.com/blog/2015/08/17/community-tip-implement-mixpanel-in-swift-apps
I had trouble with podfiles and bridging-header imports, but the use_frameworks! suggested in the above link works perfectly for me.