Code:
class User
{
class var BoolProperty: Bool
{
get {
var anyObject: AnyObject? = getValue("BoolProperty")
if let value = anyObject as? Bool {
return value
}
else {
return false
}
}
set(value) {
setValue("BoolProperty", value: value)
}
}
private class func getValue(key: String) -> AnyObject?
{
var store = NSUserDefaults.standardUserDefaults();
return store.objectForKey(key) as AnyObject?
}
}
passes the test:
class UserTests: XCTestCase
{
func testFields()
{
User.BoolProperty = true
var result = User.BoolProperty
XCTAssertEqual(true, result)
}
}
but the following code doesn't pass the same test, which uses T instead of Bool for casting:
class User
{
class var BoolProperty: Bool
{
get {
return get("BoolProperty", defaultValue: false)
}
set(value) {
setValue("BoolProperty", value: value)
}
}
private class func getValue(key: String) -> AnyObject?
{
var store = NSUserDefaults.standardUserDefaults();
return store.objectForKey(key) as AnyObject?
}
private class func get<T>(key: String, defaultValue: T) -> T
{
var anyObject: AnyObject? = getValue(key)
if let value = anyObject as? T {
return value
}
else {
return defaultValue
}
}
}
it seems, that for some reason if let value = anyObject as? T always returns false when casting to T.
In C# this is a classic example of working with untyped collections and I was wondering what's the right approach to achieve the same in Swift.
The problem is that an NSNumber is not a Bool. It looks like a Bool, and it can be converted to a Bool, but it's really a different type. Your anyObject is really an NSNumber, and asking for it to be as? T where T is Bool is too far. That's still likely a compiler bug, because it should be the same as a generic as without, but it's where the problem is happening.
You need to specialize the generic function to NSNumber:
get {
return Bool(get("BoolProperty", defaultValue: NSNumber(bool: false)))
}
It's still probably worth opening a radar. It shouldn't behave differently with and without the generic.
That said, part of the trouble is the use of AnyObject. A Bool is not an AnyObject. It's an Any. When you try to assign it to an AnyObject, it gets converted into an NSNumber. I don't know of any documentation of this; I've worked it out empirically in playgrounds. Compare the results of let a:AnyObject = false and let a:Any = false.
In theory, this should fix it all:
var anyObject: Any? = getValue(key)
But it currently crashes the compiler.
Related
Wondering if there's a good way to do this or not:
I have a #propertyWrapper named "Enhanced" that I use. I use the wrappedValue.set to do some actions, and I would also like to do some further actions if the property is Equatable.
Currently, the code looks like this:
#propertyWrapper
class Enhanced<T: Equatable>: Codable
{
private var value: T
var projectedValue: Enhanced<T> { self }
var wrappedValue: T
{
get { value }
set { set(newValue, notify: nil) }
}
func set(_ proposedValue: T, notify: Bool?)
{
let oldValue = value
let newValue = proposedValue
let changed = newValue != oldValue
if changed { /* more magic here */ }
value = newValue
}
}
Now I would like to remove the Equatable conformance over the generic T, but still be able to compare the old and new values IF the generic T conforms to Equatable.
I've tried a handful of techniques, all of which dead end somewhere. My latest was this:
let changed: Bool
switch T.self
{
case let equatableType as any Equatable.Type:
if
let oldEquatableValue = oldValue as? any Equatable,
let newEquatableValue = newValue as? any Equatable
{
changed = newEquatableValue != oldEquatableValue
}
default:
changed = true
}
...but the error is an understandable Binary operator '!=' cannot be applied to two 'any Equatable' operands.
I tried different patterns to cast the generic type T into an Equatable and silently fail if the generic does not conform, but even if they do, the resulting "cast" types I get back aren't equatable themselves.
Any revelations to the proper pattern would be great!
After some deep web-sleuthing, I came across a snippet of code that does the magic I need:
private extension Equatable
{
func isEqualTo(_ rhs: Any) -> Bool
{
if let castRHS = rhs as? Self
{
return self == castRHS
}
else
{
return false
}
}
}
(HT to neonichu/equalizer.swift on GitHub)
With this bit of pseudo type-erasure, I can make this work:
let changed: Bool
if let oldEquatableValue = oldValue as? any Equatable,
let newEquatableValue = newValue as? any Equatable
{
changed = oldEquatableValue.isEqualTo(newEquatableValue) == false
}
else
{
changed = true
}
By using an extension on Equatable that does further casting of the values, this allows for these two values to be compared (and fail if they are not the same).
Hope this helps someone else!
I was trying to make an extension for safe unwrapping, and I was working in 2 version of it, one long code form, second short code! But unexpectedly they do not work! So far as I can see to my code, I just made all things correct! What I am missing to fix both version?
struct ContentView: View {
let test: String? = "Hello, World!"
var body: some View {
Text(test.safeUnwrapV1(defaultValue: "Empty!"))
Text(test.safeUnwrapV2(defaultValue: "Empty!"))
}
}
extension Optional {
func safeUnwrapV1<T>(defaultValue: T) -> T {
let wrappedValue: T? = (self as? T?) ?? nil
if let unwrappedValue: T = wrappedValue { return unwrappedValue }
else { return defaultValue }
}
func safeUnwrapV2<T>(defaultValue: T) -> T {
return (self as? T) ?? defaultValue
}
}
There's no need to define your own generic type parameter. Optional is already generic and its generic type parameter is called Wrapped. So you simply need to declare the type of the default value to be Wrapped.
extension Optional {
func defaultValue(_ value: Wrapped) -> Wrapped {
self ?? value
}
}
struct ContentView: View {
let test: String? = "Hello, World!"
var body: some View {
Text(test.defaultValue("Empty!"))
}
}
extension Optional {
func safeUnwrap(_ defaultValue: Wrapped) -> Wrapped {
switch self {
case let value?: return value
case nil: return defaultValue
}
}
}
Or even
extension Optional {
func safeUnwrap(_ defaultValue: Wrapped) -> Wrapped {
self ?? defaultValue
}
}
But as was pointed out, this is more wordy and less idiomatic than just using the ?? operator.
Make your extension like this.
extension Optional {
func safeUnwrapV1<T>(defaultValue: T) -> Wrapped {
guard let value = self else {
return defaultValue as! Wrapped
}
return value
}
}
EDIT: As #Rudedog suggest no need to define generic here
extension Optional {
func safeUnwrapV1(defaultValue: Wrapped) -> Wrapped {
guard let value = self else {
return defaultValue
}
return value
}
}
I would constrain Optional generic Wrapped type to LosslessStringConvertible:
extension LosslessStringConvertible {
var string: String { .init(self) }
}
extension Optional where Wrapped: LosslessStringConvertible {
var string: String { self?.string ?? "" }
func string(default value: Wrapped) -> String {
self?.string ?? value.string
}
}
Usage:
var double1 = Double("2.7")
var double2 = Double("a")
print(double1.string(default: 0))
print(double2.string(default: 0))
This would print:
2.7
0
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.
I'm playing with some protocol/generic stuff in Swift and I'm curious why following code is refusing to be compiled:
protocol MyProtocol {
func value<T>() -> T
}
class StringImpl: MyProtocol {
var string: String
init() {
self.string = "..."
}
init(string: String) {
self.string = string
}
func value<String>() -> String {
return self.string as! String
}
}
class BoolImpl: MyProtocol {
var value: Bool
init() {
self.value = false
}
init(value: Bool) {
self.value = value
}
func value<Bool>() -> Bool {
return self.value as! Bool
}
}
with particular error
error: invalid redeclaration of 'value()'
func value<Bool>() -> Bool {
It may imply that I cannot have different implementations of protocol generic method, but I don't see clear reason why.
(I'm not speaking of force type cast as generic type shadows existing one)
P.S. For those who are curious I may tell that I'm trying to make protocol that is not messed with associatedtype and still is generic to some degree.
You have no problem the mistake is var value, and redeclaring a function with name of func value<Bool>, i just changed the variable name and it worked, the error clearly says
error: invalid redeclaration of 'value()'
class BoolImpl: MyProtocol {
var bool: Bool
init() {
self.bool = false
}
init(value: Bool) {
self.bool = value
}
func value<Bool>() -> Bool {
return self.bool as! Bool
}
}
error: invalid redeclaration of 'value()'
The thing is that. Your method has same name as your variable and it also returns the same type. So, compiler tells you that this isn't legal.
Anyway, you should think if you really need this method. You can add this value property to protocol and it also looks like you need associatedtype for your protocol:
protocol MyProtocol {
associatedtype T
var value: T { get set }
}
class StringImpl: MyProtocol {
typealias T = String
var value: T = "..."
init(string: T) {
value = string
}
}
class BoolImpl: MyProtocol {
typealias T = Bool
var value: T = false
init(value: T) {
self.value = value
}
}
then, if you need just value of your object, you can just get value property
someStringImpl.value
someBoolImpl.value
The problem is that 'value' variable name is the same as 'value' function name
class BoolImpl: MyProtocol {
var storedValue: Bool
init() {
self.storedValue = false
}
init(value: Bool) {
self.storedValue = value
}
func value<Bool>() -> Bool {
return self.storedValue as! Bool
}
}
Everyone seems to be missing the point here.
You seem to think that your adopters of
protocol MyProtocol {
func value<T>() -> T
}
...resolve the generic. They do not. They merely repeat the generic.
For example, think about your
func value<String>() -> String {
return self.string as! String
}
self.string is a string. So ask yourself why you have to say as! String. It’s because you are misusing String as a placeholder name just like T. You would get the same result using a nonsense word:
func value<Stringgg>() -> Stringgg {
return self.string as! Stringgg
}
That compiles too. You still haven’t resolved the generic, you merely changed its name. Your attempt to avoid an associated type has failed. Your code compiles but it can never run.
Coming from Objective-C you can call function objc_setAssociatedObject between 2 objects to have them maintain a reference, which can be handy if at runtime you don't want an object to be destroyed until its reference is removed also. Does Swift have anything similar to this?
Here is a simple but complete example derived from jckarter's answer.
It shows how to add a new property to an existing class. It does it by defining a computed property in an extension block. The computed property is stored as an associated object:
import ObjectiveC
// Declare a global var to produce a unique address as the assoc object handle
private var AssociatedObjectHandle: UInt8 = 0
extension MyClass {
var stringProperty:String {
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! String
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
EDIT:
If you need to support getting the value of an uninitialized property and to avoid getting the error unexpectedly found nil while unwrapping an Optional value, you can modify the getter like this:
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as? String ?? ""
}
The solution supports all the value types as well, and not only those that are automagically bridged, such as String, Int, Double, etc.
Wrappers
import ObjectiveC
final class Lifted<T> {
let value: T
init(_ x: T) {
value = x
}
}
private func lift<T>(x: T) -> Lifted<T> {
return Lifted(x)
}
func setAssociatedObject<T>(object: AnyObject, value: T, associativeKey: UnsafePointer<Void>, policy: objc_AssociationPolicy) {
if let v: AnyObject = value as? AnyObject {
objc_setAssociatedObject(object, associativeKey, v, policy)
}
else {
objc_setAssociatedObject(object, associativeKey, lift(value), policy)
}
}
func getAssociatedObject<T>(object: AnyObject, associativeKey: UnsafePointer<Void>) -> T? {
if let v = objc_getAssociatedObject(object, associativeKey) as? T {
return v
}
else if let v = objc_getAssociatedObject(object, associativeKey) as? Lifted<T> {
return v.value
}
else {
return nil
}
}
A possible
Class extension (Example of usage)
extension UIView {
private struct AssociatedKey {
static var viewExtension = "viewExtension"
}
var referenceTransform: CGAffineTransform? {
get {
return getAssociatedObject(self, associativeKey: &AssociatedKey.viewExtension)
}
set {
if let value = newValue {
setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.viewExtension, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
I wrote a modern wrapper available at https://github.com/b9swift/AssociatedObject
You may be surprised that it even supports Swift structures for free.
Obviously, this only works with Objective-C objects. After fiddling around with this a bit, here's how to make the calls in Swift:
import ObjectiveC
// Define a variable whose address we'll use as key.
// "let" doesn't work here.
var kSomeKey = "s"
…
func someFunc() {
objc_setAssociatedObject(target, &kSomeKey, value, UInt(OBJC_ASSOCIATION_RETAIN))
let value : AnyObject! = objc_getAssociatedObject(target, &kSomeKey)
}
Update in Swift 3.0
For example this is a UITextField
import Foundation
import UIKit
import ObjectiveC
// Declare a global var to produce a unique address as the assoc object handle
var AssociatedObjectHandle: UInt8 = 0
extension UITextField
{
var nextTextField:UITextField {
get {
return objc_getAssociatedObject(self, &AssociatedObjectHandle) as! UITextField
}
set {
objc_setAssociatedObject(self, &AssociatedObjectHandle, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
Klaas answer just for Swift 2.1:
import ObjectiveC
let value = NSUUID().UUIDString
var associationKey: UInt8 = 0
objc_setAssociatedObject(parentObject, &associationKey, value, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
let fetchedValue = objc_getAssociatedObject(parentObject, &associationKey) as! String
Just add #import <objc/runtime.h> on your brindging header file to access objc_setAssociatedObject under swift code
The above friend has answered your question, but if it is related to closure properties, please note:
```
import UIKit
public extension UICollectionView {
typealias XYRearrangeNewDataBlock = (_ newData: [Any]) -> Void
typealias XYRearrangeOriginaDataBlock = () -> [Any]
// MARK:- associat key
private struct xy_associatedKeys {
static var originalDataBlockKey = "xy_originalDataBlockKey"
static var newDataBlockKey = "xy_newDataBlockKey"
}
private class BlockContainer {
var rearrangeNewDataBlock: XYRearrangeNewDataBlock?
var rearrangeOriginaDataBlock: XYRearrangeOriginaDataBlock?
}
private var newDataBlock: BlockContainer? {
get {
if let newDataBlock = objc_getAssociatedObject(self, &xy_associatedKeys.newDataBlockKey) as? BlockContainer {
return newDataBlock
}
return nil
}
set(newValue) {
objc_setAssociatedObject(self, xy_associatedKeys.newDataBlockKey, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
}
convenience init(collectionVewFlowLayout : UICollectionViewFlowLayout, originalDataBlock: #escaping XYRearrangeOriginaDataBlock, newDataBlock: #escaping XYRearrangeNewDataBlock) {
self.init()
let blockContainer: BlockContainer = BlockContainer()
blockContainer.rearrangeNewDataBlock = newDataBlock
blockContainer.rearrangeOriginaDataBlock = originalDataBlock
self.newDataBlock = blockContainer
}
```
For 2022, now very simple:
// Utils-tags.swift
// Just a "dumb Swift trick" to add a string tag to a view controller.
// For example, with UIDocumentPickerViewController you need to know
// "which button was clicked to launch a picker"
import UIKit
private var _docPicAssociationKey: UInt8 = 0
extension UIDocumentPickerViewController {
public var tag: String {
get {
return objc_getAssociatedObject(self, &_docPicAssociationKey)
as? String ?? ""
}
set(newValue) {
objc_setAssociatedObject(self, &_docPicAssociationKey,
newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}