Get class name of object as string in Swift - swift

Getting the classname of an object as String using:
object_getClassName(myViewController)
returns something like this:
_TtC5AppName22CalendarViewController
I am looking for the pure version: "CalendarViewController". How do I get a cleaned up string of the class name instead?
I found some attempts of questions about this but not an actual answer. Is it not possible at all?

String from an instance:
String(describing: self)
String from a type:
String(describing: YourType.self)
Example:
struct Foo {
// Instance Level
var typeName: String {
return String(describing: Foo.self)
}
// Instance Level - Alternative Way
var otherTypeName: String {
let thisType = type(of: self)
return String(describing: thisType)
}
// Type Level
static var typeName: String {
return String(describing: self)
}
}
Foo().typeName // = "Foo"
Foo().otherTypeName // = "Foo"
Foo.typeName // = "Foo"
Tested with class, struct and enum.

UPDATED TO SWIFT 5
We can get pretty descriptions of type names using the instance variable through the String initializer and create new objects of a certain class
Like, for example print(String(describing: type(of: object))). Where object can be an instance variable like array, a dictionary, an Int, a NSDate, etc.
Because NSObject is the root class of most Objective-C class hierarchies, you could try to make an extension for NSObject to get the class name of every subclass of NSObject. Like this:
extension NSObject {
var theClassName: String {
return NSStringFromClass(type(of: self))
}
}
Or you could make a static funcion whose parameter is of type Any (The protocol to which all types implicitly conform) and returns the class name as String. Like this:
class Utility{
class func classNameAsString(_ obj: Any) -> String {
//prints more readable results for dictionaries, arrays, Int, etc
return String(describing: type(of: obj))
}
}
Now you can do something like this:
class ClassOne : UIViewController{ /* some code here */ }
class ClassTwo : ClassOne{ /* some code here */ }
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Get the class name as String
let dictionary: [String: CGFloat] = [:]
let array: [Int] = []
let int = 9
let numFloat: CGFloat = 3.0
let numDouble: Double = 1.0
let classOne = ClassOne()
let classTwo: ClassTwo? = ClassTwo()
let now = NSDate()
let lbl = UILabel()
print("dictionary: [String: CGFloat] = [:] -> \(Utility.classNameAsString(dictionary))")
print("array: [Int] = [] -> \(Utility.classNameAsString(array))")
print("int = 9 -> \(Utility.classNameAsString(int))")
print("numFloat: CGFloat = 3.0 -> \(Utility.classNameAsString(numFloat))")
print("numDouble: Double = 1.0 -> \(Utility.classNameAsString(numDouble))")
print("classOne = ClassOne() -> \((ClassOne).self)") //we use the Extension
if classTwo != nil {
print("classTwo: ClassTwo? = ClassTwo() -> \(Utility.classNameAsString(classTwo!))") //now we can use a Forced-Value Expression and unwrap the value
}
print("now = Date() -> \(Utility.classNameAsString(now))")
print("lbl = UILabel() -> \(String(describing: type(of: lbl)))") // we use the String initializer directly
}
}
Also, once we can get the class name as String, we can instantiate new objects of that class:
// Instantiate a class from a String
print("\nInstantiate a class from a String")
let aClassName = classOne.theClassName
let aClassType = NSClassFromString(aClassName) as! NSObject.Type
let instance = aClassType.init() // we create a new object
print(String(cString: class_getName(type(of: instance))))
print(instance.self is ClassOne)
Maybe this helps someone out there!.

Swift 5
Here is the extension to get the typeName as a variable (work with both value type or reference type).
protocol NameDescribable {
var typeName: String { get }
static var typeName: String { get }
}
extension NameDescribable {
var typeName: String {
return String(describing: type(of: self))
}
static var typeName: String {
return String(describing: self)
}
}
How to use:
// Extend with class/struct/enum...
extension NSObject: NameDescribable {}
extension Array: NameDescribable {}
extension UIBarStyle: NameDescribable { }
print(UITabBarController().typeName)
print(UINavigationController.typeName)
print([Int]().typeName)
print(UIBarStyle.typeName)
// Out put:
UITabBarController
UINavigationController
Array<Int>
UIBarStyle

Swift 5.2:
String(describing: type(of: self))

Swift 3.0
String(describing: MyViewController.self)

I suggest such an approach (very Swifty):
// Swift 3
func typeName(_ some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}
// Swift 2
func typeName(some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(some.dynamicType)"
}
It doesn't use neither introspection nor manual demangling (no magic!).
Here is a demo:
// Swift 3
import class Foundation.NSObject
func typeName(_ some: Any) -> String {
return (some is Any.Type) ? "\(some)" : "\(type(of: some))"
}
class GenericClass<T> {
var x: T? = nil
}
protocol Proto1 {
func f(x: Int) -> Int
}
#objc(ObjCClass1)
class Class1: NSObject, Proto1 {
func f(x: Int) -> Int {
return x
}
}
struct Struct1 {
var x: Int
}
enum Enum1 {
case X
}
print(typeName(GenericClass<Int>.self)) // GenericClass<Int>
print(typeName(GenericClass<Int>())) // GenericClass<Int>
print(typeName(Proto1.self)) // Proto1
print(typeName(Class1.self)) // Class1
print(typeName(Class1())) // Class1
print(typeName(Class1().f)) // (Int) -> Int
print(typeName(Struct1.self)) // Struct1
print(typeName(Struct1(x: 1))) // Struct1
print(typeName(Enum1.self)) // Enum1
print(typeName(Enum1.X)) // Enum1

If you have type Foo, the following code will give you "Foo" in Swift 3 and Swift 4:
let className = String(describing: Foo.self) // Gives you "Foo"
The problem with most of the answers on here are that they give you "Foo.Type" as the resulting string when you don't have any instance of the type, when what you really want is just "Foo". The following gives you "Foo.Type", as mentioned in a bunch of the other answers.
let className = String(describing: type(of: Foo.self)) // Gives you "Foo.Type"
The type(of:) part is unnecessary if you just want "Foo".

In Swift 4.1 and now Swift 4.2 :
import Foundation
class SomeClass {
class InnerClass {
let foo: Int
init(foo: Int) {
self.foo = foo
}
}
let foo: Int
init(foo: Int) {
self.foo = foo
}
}
class AnotherClass : NSObject {
let foo: Int
init(foo: Int) {
self.foo = foo
super.init()
}
}
struct SomeStruct {
let bar: Int
init(bar: Int) {
self.bar = bar
}
}
let c = SomeClass(foo: 42)
let s = SomeStruct(bar: 1337)
let i = SomeClass.InnerClass(foo: 2018)
let a = AnotherClass(foo: 1<<8)
If you don't have an instance around:
String(describing: SomeClass.self) // Result: SomeClass
String(describing: SomeStruct.self) // Result: SomeStruct
String(describing: SomeClass.InnerClass.self) // Result: InnerClass
String(describing: AnotherClass.self) // Result: AnotherClass
If you do have an instance around:
String(describing: type(of: c)) // Result: SomeClass
String(describing: type(of: s)) // Result: SomeStruct
String(describing: type(of: i)) // Result: InnerClass
String(describing: type(of: a)) // Result: AnotherClass

Swift 5.1
You can get class, struct, enum, protocol and NSObject names though Self.self.
print("\(Self.self)")

To get name of a Swift class from an object, e.g. for var object: SomeClass(), use
String(describing: type(of: object))
To get name of a Swift class from a class type, e.g. SomeClass, use:
String(describing: SomeClass.self)
Output:
"SomeClass"

You can try this way:
self.classForCoder.description()

To get the type name as a string in Swift 4 (I haven't checked the earlier versions), just use string interpolation:
"\(type(of: myViewController))"
You can use .self on a type itself, and the type(of:_) function on an instance:
// Both constants will have "UIViewController" as their value
let stringFromType = "\(UIViewController.self)"
let stringFromInstance = "\(type(of: UIViewController()))"

You can use the Swift standard library function called _stdlib_getDemangledTypeName like this:
let name = _stdlib_getDemangledTypeName(myViewController)

Swift 5:
Way 1:
print("Class: \(String(describing: self)), Function: \(#function), line: \(#line)")
Output:
Class: <Test.ViewController: 0x7ffaabc0a3d0>, Function: viewDidLoad(), line: 15
Way 2:
print("Class: \(String(describing: type(of: self))), Function: \(#function), line: \(#line)")
Output:
Class: ViewController, Function: viewDidLoad(), line: 16

One can also use mirrors:
let vc = UIViewController()
String(Mirror(reflecting: vc).subjectType)
NB: This method can also be used for Structs and Enums. There is a displayStyle that gives an indication of what type of the structure:
Mirror(reflecting: vc).displayStyle
The return is an enum so you can:
Mirror(reflecting: vc).displayStyle == .Class

Swift 3.0:
You can create an extension like this one.. It gives back the class name without the project name
extension NSObject {
var className: String {
return NSStringFromClass(self as! AnyClass).components(separatedBy: ".").last ?? ""
}
public class var className: String {
return NSStringFromClass(self).components(separatedBy: ".").last ?? ""
}
}

You can extend NSObjectProtocol in Swift 4 like this :
import Foundation
extension NSObjectProtocol {
var className: String {
return String(describing: Self.self)
}
}
This will make calculated variable className available to ALL classes. Using this inside a print() in CalendarViewController will print "CalendarViewController" in console.

You can get the name of the class doing something like:
class Person {}
String(describing: Person.self)

To get class name as String declare your class as following
#objc(YourClassName) class YourClassName{}
And get class name using following syntax
NSStringFromClass(YourClassName)

I've been looking for this answer off and on for a while. I use GKStateMachine and like to observe state changes and wanted an easy way to see just the class name. I'm not sure if it's just iOS 10 or Swift 2.3, but in that environment, the following does exactly what I want:
let state:GKState?
print("Class Name: \(String(state.classForCoder)")
// Output:
// Class Name: GKState

Try reflect().summary on Class self or instance dynamicType. Unwrap optionals before getting dynamicType otherwise the dynamicType is the Optional wrapper.
class SampleClass { class InnerClass{} }
let sampleClassName = reflect(SampleClass.self).summary;
let instance = SampleClass();
let instanceClassName = reflect(instance.dynamicType).summary;
let innerInstance = SampleClass.InnerClass();
let InnerInstanceClassName = reflect(innerInstance.dynamicType).summary.pathExtension;
let tupleArray = [(Int,[String:Int])]();
let tupleArrayTypeName = reflect(tupleArray.dynamicType).summary;
The summary is a class path with generic types described. To get a simple class name from the summary try this method.
func simpleClassName( complexClassName:String ) -> String {
var result = complexClassName;
var range = result.rangeOfString( "<" );
if ( nil != range ) { result = result.substringToIndex( range!.startIndex ); }
range = result.rangeOfString( "." );
if ( nil != range ) { result = result.pathExtension; }
return result;
}

The above solutions didn't work for me. The produced mostly the issues mention in several comments:
MyAppName.ClassName
or
MyFrameWorkName.ClassName
This solutions worked on XCode 9, Swift 3.0:
I named it classNameCleaned so it is easier to access and doesn't conflict with future className() changes:
extension NSObject {
static var classNameCleaned : String {
let className = self.className()
if className.contains(".") {
let namesArray = className.components(separatedBy: ".")
return namesArray.last ?? className
} else {
return self.className()
}
}
}
Usage:
NSViewController.classNameCleaned
MyCustomClass.classNameCleaned

Swift 5
NSStringFromClass(CustomClass.self)

This kind of example for class var. Don't include the name of bundle.
extension NSObject {
class var className: String {
return "\(self)"
}
}

Swift 3.0 (macOS 10.10 and later), you can get it from className
self.className.components(separatedBy: ".").last!

I tried type(of:...) in Playground with Swift 3. This is my result.
This is the code format version.
print(String(describing: type(of: UIButton.self)))
print(String(describing: type(of: UIButton())))
UIButton.Type
UIButton

Swift 5.1 :-
You can also use generic function for get class name of object as string
struct GenericFunctions {
static func className<T>(_ name: T) -> String {
return "\(name)"
}
}
Call this function by using following:-
let name = GenericFunctions.className(ViewController.self)
Happy Coding :)

This solution will work for all the classes
Swift 5 solution:
extension NSObject {
var className: String {
return String(describing: type(of: self))
}
class var className: String {
return String(describing: self)
}
}
USAGE:
class TextFieldCell: UITableVIewCell {
}
class LoginViewController: UIViewController {
let cellClassName = TextFieldCell.className
}

If you don't like the mangled name, you can dictate your own name:
#objc(CalendarViewController) class CalendarViewController : UIViewController {
// ...
}
However, it would be better in the long run to learn to parse the mangled name. The format is standard and meaningful and won't change.

Sometimes the other solutions will give a non useful name depending on what object you are trying to look at. In that case you can get the class name as a string using the following.
String(cString: object_getClassName(Any!))
⌘ click the function in xcode to see some related methods that are fairly useful. or check here https://developer.apple.com/reference/objectivec/objective_c_functions

Related

Why is the subclass type not available when an instance property is initialized by a static member?

When the following code is run, the self inside of defaultModuleName is ReactViewController when one would expect it to be FooViewController. Why?
class ReactViewController: UIViewController {
var moduleName: String = defaultModuleName
static var defaultModuleName: String {
let t = String(reflecting: self) // Also tried NSStringFromClass
guard let s = t.split(separator: ".").last else { return "" }
guard let r = s.range(of: "ViewController") else { return "" }
return String(s.prefix(upTo: r.lowerBound))
}
}
class FooViewController: ReactViewController {
override func viewDidLoad() {
super.viewDidLoad();
print(moduleName); // Prints "React"
}
}
This is pretty interesting; it appears that the self available in a property initialiser is merely the type that the property is defined in, rather than the dynamic type of the instance being constructed.
A more minimal example would be:
class C {
static var foo: String { return "\(self)" }
let bar = foo // the implicit 'self' in the call to 'foo' is always C.
}
class D : C {}
print(D().bar) // C
In the property initialiser for bar, the implicit self is C.self, not D.self; despite the fact that we're constructing a D instance. So that's what the call to foo sees as self.
This also prevents class member overrides from being called from property initialisers:
class C {
class var foo: String { return "C" }
let bar = foo
}
class D : C {
override class var foo: String { return "D" }
}
print(D().bar) // C
Therefore I regard this as a bug, and have filed a report here.
Until fixed, a simple solution is to use a lazy property instead, as now self is the actual instance (upon the property being accessed for the first time), which we get can get the dynamic type of with type(of: self).
For example:
class C {
static var foo: String { return "\(self)" }
// private(set) as the property was a 'let' in the previous example.
lazy private(set) var bar = type(of: self).foo
}
class D : C {}
print(D().bar) // D
Applied to your example:
class ReactViewController : UIViewController {
lazy var moduleName = type(of: self).defaultModuleName
static var defaultModuleName: String {
let t = String(reflecting: self) // Also tried NSStringFromClass
guard let s = t.split(separator: ".").last else { return "" }
guard let r = s.range(of: "ViewController") else { return "" }
return String(s.prefix(upTo: r.lowerBound))
}
}
class FooViewController : ReactViewController {
override func viewDidLoad() {
super.viewDidLoad()
print(moduleName) // Prints "Foo"
}
}
You just need to pass self instead of type(of: self), and use the String(describing:) initializer.
class ClassA {
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB.className) // prints "ClassB"
EDIT: clarification on the var moduleName: String = defaultModuleName update. Suppose I add this line to the above example (same idea):
class ClassA {
// This is a property of ClassA -> it gets implicitly initialized
// when ClassA does -> it uses ClassA.className for its value
var instanceClassName = className
static var className: String {
return String(describing: self)
}
}
class ClassB: ClassA { }
print(ClassB().instanceClassName) // prints "ClassA"
This new instanceClassName is not static, so it is an instance property on ClassA. It is therefore initialized when ClassA is initialized (not when ClassB is initialized). Ergo, a property being set within ClassA, using a reference to className, will print out ClassA.

Generic Return Type Based on Class

I'm trying to create factory method on a class that automatically casts to the class it's on.
extension NSObject {
// how can I get the return type to be the current NSObject subclass
// instead of NSObject?
class func create() -> NSObject {
return self.init()
}
// example: create(type: NSArray.self)
class func create<T:NSObject>(type:T.Type) -> T {
return T()
}
}
Example two works, but gets NO advantage from being a class method:
let result = NSArray.create(type: NSArray.self)
But I'd love to be able to just call:
let result = NSArray.create()
without having to cast afterwards. Is there a way to do this in Swift?
You can use the class-level Self for this:
extension NSObject {
class func create() -> Self {
return self.init()
}
}
let array = NSArray.create()
But I don't really see why you would, since you might as well just add an initializer.
The accepted answer does the trick, thanks!
However, I needed this for a case where I wasn't calling the init directly. Instead, I had an object that was of type NSObject and needed a forced downcast
As #Hamish pointed out from this other SO answer, you can use the generic inference on a class method if you're another layer deep (a method called by a class method).
class func create() -> Self {
return createInner()
}
class func createInner<T>() -> T {
// upcasting to NSObject to show that we can downcast
let b = self.init() as NSObject
return b as! T
}
let array = NSArray.create() // gives me an NSArray
An Example with CoreData
I still can't figure out how to get the fetch part to compile, so I'm using an external function still.
import CoreData
// callers use
// try fetch(type: SomeMO.self, moc: moc)
func fetch<T:NSManagedObject>(type:T.Type, moc:NSManagedObjectContext) throws -> [T] {
return try T.fetch(moc: moc) as! [T]
}
extension NSManagedObject {
class func makeOne(moc:NSManagedObjectContext) -> Self {
return makeOneInner(moc: moc)
}
private class func makeOneInner<T>(moc:NSManagedObjectContext) -> T {
let name = "\(self)"
let retVal = NSEntityDescription.insertNewObject(forEntityName: name, into: moc)
return retVal as! T
}
class func fetch(moc:NSManagedObjectContext) throws -> [NSManagedObject] {
let fetchReq:NSFetchRequest<NSManagedObject> = self.fetchRequest() as! NSFetchRequest<NSManagedObject>
let retVal = try moc.fetch(fetchReq) as [NSManagedObject]
return retVal
}
}

Opposite of __conversion in Swift to assign to a value of a different type

Swift provides a special method called __conversion that allows you to implicitly convert your type to another type. I would like to be able to define a method that allows you to go the other way: to be able to assign a custom type to another type and have it implicitly converted to allow the assignment to work.
Forward usage that is valid:
class MyClass<T> {
var myValue : T
func __conversion() -> T? {
return myValue
}
init(value: T) {
self.myValue = value
}
}
func takesString(aString: String?) {
}
var myInstance = MyClass(value: "Hello")
takesString(myInstance)
What I would like to do:
class MyClass<T> {
func __conversion(aValue: T) -> MyClass<T> {
return MyClass(value: T)
}
}
var myInstance : MyClass<String> = "Hello World" // compiler error
Is there any way to do this?
It turns out that __conversion is a private method and will be removed by the end of the beta so this will definitely not be possible once Swift is released.
Have you tried adding an extension to String?
extension String {
func __conversion() -> MyClass {
var myInstance = MyClass()
myInstance.myString = self
return myInstance
}
}
It worked in an iOS playground in Xcode 6 Beta 4.
let anInstance: MyClass = "test"
To deal with generics:
class MyClass<T> {
var myString = ""
var myT : T?
}
extension String {
func __conversion<T>() -> MyClass<T> {
var myInstance = MyClass<T>()
myInstance.myString = self
return myInstance
}
}
let anInstance: MyClass<Int> = "test"
BTW: I think the Apple approved answer is an init method.
let anInstance = MyClass(myString: "test")

Implementing copy() in Swift

I want to be able to copy a custom class in Swift. So far, so good. In Objective-C I just had to implement the NSCopying protocol, which means implementing copyWithZone.
As an example, I have a basic class called Value which stores a NSDecimalNumber.
func copyWithZone(zone: NSZone) -> AnyObject! {
return Value(value: value.copy() as NSDecimalNumber)
}
In Objective-C I, could easily just call copy to copy my object. In Swift, there seems to be no way to call copy. Do I really need to call copyWithZone even if no zone is needed? And which zone do I need to pass as a parameter?
The copy method is defined in NSObject. If your custom class does not inherit from NSObject, copy won't be available.
You can define copy for any object in the following way:
class MyRootClass {
//create a copy if the object implements NSCopying, crash otherwise
func copy() -> Any {
guard let asCopying = ((self as AnyObject) as? NSCopying) else {
fatalError("This class doesn't implement NSCopying")
}
return asCopying.copy(with: nil)
}
}
class A : MyRootClass {
}
class B : MyRootClass, NSCopying {
func copy(with zone: NSZone? = nil) -> Any {
return B()
}
}
var b = B()
var a = A()
b.copy() //will create a copy
a.copy() //will fail
I guess that copy isn't really a pure Swift way of copying objects. In Swift it is probably a more common way to create a copy constructor (an initializer that takes an object of the same type).
Well, there is a really easy solution for this and you do not have to create root class.
protocol Copyable {
init(instance: Self)
}
extension Copyable {
func copy() -> Self {
return Self.init(instance: self)
}
}
Now, if you want to make your custom class be able to copy, you have to conform it to Copyable protocol and provide init(instance: Self) implementation.
class A: Copyable {
var field = 0
init() {
}
required init(instance: A) {
self.field = instance.field
}
}
Finally, you can use func copy() -> Self on any instance of A class to create a copy of it.
let a = A()
a.field = 1
let b = a.copy()
You can just write your own copy method
class MyRootClass {
var someVariable:Int
init() {
someVariable = 2
}
init(otherObject:MyRootClass) {
someVariable = otherObject.someVariable
}
func copy() -> MyRootClass {
return MyRootClass(self)
}
}
The benefit of this is when you are using subclasses around your project, you can call the 'copy' command and it will copy the subclass. If you just init a new one to copy, you will also have to rewrite that class for each object...
var object:Object
....
//This code will only work for specific class
var objectCopy = Object()
//vs
//This code will work regardless of whether you are using subClass or superClass
var objectCopy = object.copy()
In my case the object chain was large and nested so was looking for simpler solutions.
The core concept being simple enough... duplicate the data by new initialization, I used Encode and Decode to deep-copy the entire object since my objects were already conforming to Codable,
Simple Example:
class MyCodableObject: Codable, CustomStringConvertible {
var name: String
var description: String { name }
init(name: String) {
self.name = name
}
}
let originalArr = [MyCodableObject(name: "a"),
MyCodableObject(name: "b")]
do {
let data = try JSONEncoder().encode(originalArr)
let copyArr = try JSONDecoder().decode([MyCodableObject].self, from: data)
//modify if required
copyArr.forEach { obj in
obj.name = "\(obj.name) modified"
}
print(originalArr, copyArr) //-> [a, b] [a modified, b modified]
} catch {
fatalError(error.localizedDescription)
}
Refactor (Generic Solution):
To simplify future cases we can create a protocol that will provide a copy function.
For Non-Codable objects, you will have to implement your own copy function.
For Codable objects, we can provide a default implementation so it's ready-to-use. Like so:
protocol Copyable {
func copy() -> Self
}
extension Copyable where Self: Codable {
func copy() -> Self {
do {
let encoded = try JSONEncoder().encode(self)
let decoded = try JSONDecoder().decode(Self.self, from: encoded)
return decoded
} catch {
fatalError(error.localizedDescription)
}
}
}
We can now conform a Codable object to our Copyable protocol and start using it immediately.
extension MyCodableObject: Copyable {}
Example:
let a = MyCodableObject(name: "A")
let b = a.copy()
b.name = "B"
print(a.name, b.name) //-> "A B"
We can also conform an Array of Codable objects to Copyable and access the copy function instantly:
extension Array: Copyable where Element: Codable {}
Example:
let originalArr = [MyCodableObject(name: "a"),
MyCodableObject(name: "b")]
let copyArr = originalArr.copy()
copyArr.forEach { (obj) in
obj.name = "\(obj.name) modified"
}
print(originalArr, copyArr) //-> [a, b] [a modified, b modified]
IMO, the simplest way to achieve this is :
protocol Copyable
{
init(other: Self)
}
extension Copyable
{
func copy() -> Self
{
return Self.init(other: self)
}
}
Implemented in a struct as :
struct Struct : Copyable
{
var value: String
init(value: String)
{
self.value = value
}
init(other: Struct)
{
value = other.value
}
}
And, in a class, as :
class Shape : Copyable
{
var color: NSColor
init(color: NSColor)
{
self.color = color
}
required init(other: Shape)
{
color = other.color
}
}
And in subclasses of such a base class as :
class Circle : Shape
{
var radius: Double = 0.0
init(color: NSColor, radius: Double)
{
super.init(color: color)
self.radius = radius
}
required init(other: Shape)
{
super.init(other: other)
if let other = other as? Circle
{
radius = other.radius
}
}
}
class Square : Shape
{
var side: Double = 0.0
init(color: NSColor, side: Double)
{
super.init(color: color)
self.side = side
}
required init(other: Shape)
{
super.init(other: other)
if let other = other as? Square
{
side = other.side
}
}
}
If you want to be able to copy an array of Copyable types :
extension Array where Element : Copyable
{
func copy() -> Array<Element>
{
return self.map { $0.copy() }
}
}
Which then allows you to do simple code like :
{
let shapes = [Circle(color: .red, radius: 5.0), Square(color: .blue, side: 5.0)]
let copies = shapes.copy()
}
In my opinion, more Swifty way is to use associated type in Copyable protocol which allows define return type for method copy. Other ways don't allow to copy an object tree like this:
protocol Copyable {
associatedtype V
func copy() -> V
func setup(v: V) -> V
}
class One: Copyable {
typealias T = One
var name: String?
func copy() -> V {
let instance = One()
return setup(instance)
}
func setup(v: V) -> V {
v.name = self.name
return v
}
}
class Two: One {
var id: Int?
override func copy() -> Two {
let instance = Two()
return setup(instance)
}
func setup(v: Two) -> Two {
super.setup(v)
v.id = self.id
return v
}
}
extension Array where Element: Copyable {
func clone() -> [Element.V] {
var copiedArray: [Element.V] = []
for element in self {
copiedArray.append(element.copy())
}
return copiedArray
}
}
let array = [One(), Two()]
let copied = array.clone()
print("\(array)")
print("\(copied)")
Copyable instances in swift
NOTE:
The great thing about this approach to copying Class instances is that it doesn't rely on NSObject or objc code, and most importantly it doesn't clutter up the "Data-Style-Class". Instead it extends the protocol that extends the "Data-Style-Class". This way you can compartmentalize better by having the copy code in another place than the data it self. The inheritance between classes is also taken care of as long as you model the protocols after the classes. Here is an example of this approach:
protocol IA{var text:String {get set}}
class A:IA{
var text:String
init(_ text:String){
self.text = text
}
}
extension IA{
func copy() -> IA {
return A(text)
}
}
protocol IB:IA{var number:Int {get set}}
class B:A,IB{
var number:Int
init(_ text:String, _ number:Int){
self.number = number
super.init(text)
}
}
extension IB{
func copy() -> IB {
return B(text,number)
}
}
let original = B("hello",42)
var uniqueCopy = original.copy()
uniqueCopy.number = 15
Swift.print("uniqueCopy.number: " + "\(uniqueCopy.number)")//15
Swift.print("original.number: " + "\(original.number)")//42
NOTE:
To see an implementation of this approach in real code: Then check out this Graphic Framework for OSX: (PERMALINK) https://github.com/eonist/Element/wiki/Progress2#graphic-framework-for-osx
The different shapes uses the same style but each style uses a style.copy() call to create an unique instance. Then a new gradient is set on this copy rather than on the original reference like this:
The code for the above example goes like this:
/*Gradients*/
let gradient = Gradient(Gradients.red(),[],GradientType.Linear,π/2)
let lineGradient = Gradient(Gradients.teal(0.5),[],GradientType.Linear,π/2)
/*Styles*/
let fill:GradientFillStyle = GradientFillStyle(gradient);
let lineStyle = LineStyle(20,NSColorParser.nsColor(Colors.green()).alpha(0.5),CGLineCap.Round)
let line = GradientLineStyle(lineGradient,lineStyle)
/*Rect*/
let rect = RectGraphic(40,40,200,200,fill,line)
addSubview(rect.graphic)
rect.draw()
/*Ellipse*/
let ellipse = EllipseGraphic(300,40,200,200,fill.mix(Gradients.teal()),line.mix(Gradients.blue(0.5)))
addSubview(ellipse.graphic)
ellipse.draw()
/*RoundRect*/
let roundRect = RoundRectGraphic(40,300,200,200,Fillet(50),fill.mix(Gradients.orange()),line.mix(Gradients.yellow(0.5)))
addSubview(roundRect.graphic)
roundRect.draw()
/*Line*/
let lineGraphic = LineGraphic(CGPoint(300,300),CGPoint(500,500),line.mix(Gradients.deepPurple()))
addSubview(lineGraphic.graphic)
lineGraphic.draw()
NOTE:
The copy call is actually done in the mix() method. This is done so that code can be more compact and an instance is conveniently returned right away.
PERMALINK for all the supporting classes for this example: https://github.com/eonist/swift-utils
Only if you are using ObjectMapper library :
do like this
let groupOriginal = Group(name:"Abc",type:"Public")
let groupCopy = Mapper<Group>().mapAny(group.toJSON())! //where Group is Mapable
Swift making copies of passed class instances
If you use the code in the accepted answer(the OP answered their own question) here, so long as your class is a subclass of NSObject and uses the Copying protocol in that post it will work as expected by calling the copyOfValues() function.
With this, no tedious setup or copy functions where you need to assign all the instance variables to the new instance.
I should know, I wrote that code and just tested it XD

Is there a way to set associated objects in Swift?

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)
}
}
}