Check for value or reference type in Swift - swift

How can we check if a parameter passed in a function is a value or a reference type? For example
func isReferenceType(toTest: Any) {
return true // or false
}
As we see here, we are not able to do this leveraging generics.

AnyObject is a protocol that any class type automatically conforms to, so you can write:
func isReferenceType(toTest: Any) -> Bool {
return toTest.dynamicType is AnyObject
}
class Foo { }
struct Bar { }
isReferenceType(Foo()) // true
isReferenceType(Bar()) // false
isReferenceType("foo") // false
isReferenceType(123) // false
isReferenceType([1,2,3]) // false

Swift 5
func isReferenceType(_ toTest: Any) -> Bool {
return type(of: toTest) is AnyClass
}

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.

Swift protocol with generic method: invalid redeclaration of 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.

Optional closure with Bool return type

I have a method with an optional closure argument like this:
func when(_ name:String, state:State = .normal, closure:(() -> Bool)? = nil)
{
...
}
I call them like this:
when("I do something")
{
if !self.doSomething() { return false }
if !self.doSomethingElse() { return false }
return true
}
This gives me a compile-time error:
Error: cannot convert value of type '()' to expected argument type
'Bool'
What is going on here? Why does Swift sees a type () where Bool is expected?
Your function self.doSomething() and self.doSomethingElse() do not return a bool value
Instead of
func doSomething() {
return true
}
It should be like this
func doSomething() -> Bool {
return true
}

Using as a concrete type conforming to protocol AnyObject is not supported

I'm using Swift 2 and using WeakContainer as a way to store a set of weak objects, much like NSHashTable.weakObjectsHashTable()
struct WeakContainer<T: AnyObject> {
weak var value: T?
}
public protocol MyDelegate : AnyObject {
}
Then in my ViewController, I declare
public var delegates = [WeakContainer<MyDelegate>]
But it is error
Using MyDelegate as a concrete type conforming to protocol AnyObject is not supported
I see that the error is that WeakContainer has value member declared as weak, so T is expected to be object. But I also declare MyDelegate as AnyObject, too. How to get around this?
I ran into the same problem when I tried to implement weak containers. As #plivesey points out in a comment above, this seems to be a bug in Swift 2.2 / Xcode 7.3, but it is expected to work.
However, the problem does not occur for some Foundation protocols. For example, this compiles:
let container = WeakContainer<NSCacheDelegate>()
I found out that this works for protocols marked with the #objc attribute. You can use this as a workaround:
Workaround 1
#objc
public protocol MyDelegate : AnyObject { }
let container = WeakContainer<MyDelegate>() // No compiler error
As this can lead to other problems (some types cannot be represented in Objective-C), here is an alternative approach:
Workaround 2
Drop the AnyObject requirement from the container, and cast the value to AnyObject internally.
struct WeakContainer<T> {
private weak var _value:AnyObject?
var value: T? {
get {
return _value as? T
}
set {
_value = newValue as? AnyObject
}
}
}
protocol MyDelegate : AnyObject { }
var container = WeakContainer<MyDelegate>() // No compiler error
Caveat: Setting a value that conforms to T, but is not an AnyObject, fails.
I had the same idea to create weak container with generics.
As result I created wrapper for NSHashTable and did some workaround for your compiler error.
class WeakSet<ObjectType>: SequenceType {
var count: Int {
return weakStorage.count
}
private let weakStorage = NSHashTable.weakObjectsHashTable()
func addObject(object: ObjectType) {
guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
weakStorage.addObject(object as? AnyObject)
}
func removeObject(object: ObjectType) {
guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
weakStorage.removeObject(object as? AnyObject)
}
func removeAllObjects() {
weakStorage.removeAllObjects()
}
func containsObject(object: ObjectType) -> Bool {
guard object is AnyObject else { fatalError("Object (\(object)) should be subclass of AnyObject") }
return weakStorage.containsObject(object as? AnyObject)
}
func generate() -> AnyGenerator<ObjectType> {
let enumerator = weakStorage.objectEnumerator()
return anyGenerator {
return enumerator.nextObject() as! ObjectType?
}
}
}
Usage:
protocol MyDelegate : AnyObject {
func doWork()
}
class MyClass: AnyObject, MyDelegate {
fun doWork() {
// Do delegated work.
}
}
var delegates = WeakSet<MyDelegate>()
delegates.addObject(MyClass())
for delegate in delegates {
delegate.doWork()
}
It's not the best solution, because WeakSet can be initialized with any type, and if this type doesn't conform to AnyObject protocol then app will crash. But I don't see any better solution right now.
Why are you trying to use generics? I would suggest doing the following:
import Foundation
import UIKit
protocol MyDelegate : AnyObject {
}
class WeakContainer : AnyObject {
weak var value: MyDelegate?
}
class ViewController: UIViewController {
var delegates = [WeakContainer]()
}
There is also NSValue's nonretainedObject
If your Protocol can be marked as #obj then you can use code below
protocol Observerable {
associatedtype P : AnyObject
var delegates: NSHashTable<P> { get }
}
#objc protocol MyProtocol {
func someFunc()
}
class SomeClass : Observerable {
var delegates = NSHashTable<MyProtocol>.weakObjects()
}
Your issue is that WeakContainer requires its generic type T to be a subtype of AnyObject - a protocol declaration is not a subtype of AnyObject. You have four options:
Instead of declaring WeakContainer<MyDelegate> replace it with something that actually implements MyDelegate. The Swift-y approach for this is to use the AnyX pattern: struct AnyMyDelegate : MyDelegate { ... }
Define MyDelegate to be 'class bound' as protocol MyDelegate : class { ... }
Annotate MyDelegate with #obj which, essentially, makes it 'class bound'
Reformulate WeakContainer to not require its generic type to inherit from AnyObject. You'll be hard pressed to make this work because you need a property declared as weak var and there are limitation as to what types are accepted by weak var - which are AnyObject essentially.
Here is my implementation of WeakSet in pure Swift (without NSHashTable).
internal struct WeakBox<T: AnyObject> {
internal private(set) weak var value: T?
private var pointer: UnsafePointer<Void>
internal init(_ value: T) {
self.value = value
self.pointer = unsafeAddressOf(value)
}
}
extension WeakBox: Hashable {
var hashValue: Int {
return self.pointer.hashValue
}
}
extension WeakBox: Equatable {}
func ==<T>(lhs: WeakBox<T>, rhs: WeakBox<T>) -> Bool {
return lhs.pointer == rhs.pointer
}
public struct WeakSet<Element>: SequenceType {
private var boxes = Set<WeakBox<AnyObject>>()
public mutating func insert(member: Element) {
guard let object = member as? AnyObject else {
fatalError("WeakSet's member (\(member)) must conform to AnyObject protocol.")
}
self.boxes.insert(WeakBox(object))
}
public mutating func remove(member: Element) {
guard let object = member as? AnyObject else {
fatalError("WeakSet's member (\(member)) must conform to AnyObject protocol.")
}
self.boxes.remove(WeakBox(object))
}
public mutating func removeAll() {
self.boxes.removeAll()
}
public func contains(member: Element) -> Bool {
guard let object = member as? AnyObject else {
fatalError("WeakSet's member (\(member)) must conform to AnyObject protocol.")
}
return self.boxes.contains(WeakBox(object))
}
public func generate() -> AnyGenerator<Element> {
var generator = self.boxes.generate()
return AnyGenerator {
while(true) {
guard let box = generator.next() else {
return nil
}
guard let element = box.value else {
continue
}
return element as? Element
}
}
}
}

Setting up equatable for PFObject

I m getting error when setting up equatable in swift
extension PFObject : Equatable {}
public func ==(lhs:PFObject,rhs:PFObject) -> Bool {
return lhs.objectId == rhs.objectId
}
Below the following error I m getting
Redundant conformance of 'PFObject' to protocol 'Equatable'
Is there any solution for this error?
A change in Swift 2 makes every NSObject conform to Equatable through the isEqual implementation which simply compares pointer values (read more here).
You can use the following extension to override the default implementation of isEqual:
extension PFObject {
public override func isEqual(object: AnyObject?) -> Bool {
if (object as? PFObject)?.objectId == self.objectId {
return true
} else {
return super.isEqual(object)
}
}
}
PFObject inherits from NSObject so it is already conforming to Equatable.
Instead of adding Equatable to a class that already implements it, you should subclass it and override the default implementation.
Here's an example implementation of isEqual that also checks the object is of the same Parse object type.
extension PFObject {
open override func isEqual(_ object: Any?) -> Bool {
if let parseObject = object as? PFObject {
return parseObject.parseClassName == self.parseClassName && parseObject.objectId == self.objectId
} else {
return super.isEqual(object)
}
}
}