NSClassFromString from myClass [duplicate] - swift

This question already has an answer here:
NSClassFromString returning nil for nested class
(1 answer)
Closed 3 years ago.
I have my custom class in extension UIImage like this
extension UIImage {
class AssetItem: NSObject {
}
}
when I try get class from NSClassFromString like this
NSClassFromString("UIImage.AssetItem")
I receive nil. How I can do it? I need get it class from String.
actually I try to do a tree with names, for can get exec like it NavigationBar.additionMenu and receive string "NavigationBar.additionMenu".
protocol EIAssetRoot {
}
protocol EIAssetFolder {
static func name(_ key: String) -> String
}
extension EIAssetFolder {
static func name(_ key: String = #function) -> String {
let full = "\(String(reflecting: self)).\(key)"
let lastSpace = full.components(separatedBy: ":").last ?? full
let components = lastSpace.components(separatedBy: ".")
var rootComponents = components
var rootFounding = false
repeat {
rootComponents = rootComponents.dropLast()
let name = rootComponents.joined(separator: ".")
if let anyClass = NSClassFromString(name) {
if anyClass is EIAssetRoot {
rootFounding = true
}
}
} while rootComponents.count > 0 && rootFounding == false
let keyComponents = components.dropFirst(rootComponents.count)
let name = keyComponents.joined(separator: ".")
return name
}
}
extension UIImage {
#objc class AssetItem: NSObject, EIAssetRoot {
class NavigationBar: EIAssetFolder {
static var additionMenu: String { get { return name() } }
static var save: String { get { return name() } }
static let toLeft: String { get { return name() } }
static func toRight: String { get { return name() } }
}
}
}
this I try build string from class name and trunc first part to class EIAssetRoot
I don't like doing in by enum because it looks like .NavigationBar(.Menu(.SecondMenu(.additionMenu))) too meny ()

You need to "unhide" AssetItem by moving it out of the UIImage extension and up to the top level. Objective-C can never "see" a nested type like UIImage.AssetItem — and NSClassFromString is Objective-C / Cocoa.
However, it would be even better to ask yourself why you need this. In Swift, an attempt to use NSClassFromString is first and foremost a Bad Smell. You are probably trying to do something here that can be done in a correct Swifty way, without involving Objective-C Cocoa at the back end. But you have not revealed what it is.

You will have to access the class by an explicitly defined name like this. These symbols are defined in the global scope in Objective-C (so no .'s are allowed in the name):
extension UIImage {
#objc(UIImageAssetItem)
class AssetItem: NSObject {
}
}
let cls = NSClassFromString("UIImageAssetItem")

Related

Swift - Referring to a class's type within the class

When using static, type properties and methods from within the instance methods of the type, I'm often repeating the name of the type.
e.g.
class Foo
{
// Type properties, methods
static let kBrandColor = UIColor.red
static let kMeaning = 42
static func makeThing() -> Thing { ... }
// Instance method
func printStuff()
{
print("Brand Color is: \(Foo.kBrandColor)")
print("The meaning of life is: \(Foo.kMeaning)")
let thing = Foo.makeThing()
print("Thing is \(thing)"
}
...
}
These repeated references to "Foo" can (and often do) lead to bugs when copy-pasting, refactoring. It's very easy to forget to change a "Foo", and the code will still compile.
So, I've been using a pattern like this:
class Foo
{
fileprivate typealias _ThisClass = Foo
// Type properties, methods
static let kBrandColor = UIColor.red
static let kMeaning = 42
static func makeThing() -> Thing { ... }
// Instance method
func printStuff()
{
print("Brand Color is: \(_ThisClass.kBrandColor)")
print("The meaning of life is: \(_ThisClass.kMeaning)")
let thing = _ThisClass.makeThing()
print("Thing is \(thing)"
}
...
}
This approach has the advantage of some copy-and-paste safety, but at the expense of a bit of boilerplate.
Is there a better, cleaner solution to this issue? (I've attempted to search SO, but getting the search terms right for this kind of problem has been tricky.)
A protocol would work well here. You can define the properties the protocol requires, and then apply that to any class you want to use these in.
protocol Brandable {
var kBrandColor: UIColor { get }
var kMeaning: Int { get }
}
class Foo: Brandable {
let kBrandColor: UIColor = .red
let kMeaning: Int = 42
}
If you want to reuse the printStuff function, you can also put that in the protocol, and put a base implementation in an extension:
protocol Brandable {
var kBrandColor: UIColor { get }
var kMeaning: Int { get }
func printStuff()
}
extension Brandable {
func printStuff() {
print("Brand Color is: \(kBrandColor)")
print("The meaning of life is: \(kMeaning)")
}
}
class Foo: Brandable {
let kBrandColor: UIColor = .red
let kMeaning: Int = 42
}
class Bar: Brandable {
let kBrandColor: UIColor = .blue
let kMeaning: Int = 100
}
Foo().printStuff()
Bar().printStuff()
The same can be done with the makeStuff() function. Shared functionality goes in the protocol and its extension. If you need to change the behavior in some class, you only need to add your own printStuff or makeStuff function to override the protocol's default implementation.
Create a protocol like Identifiable with an identifier property. Then make any class that you want identifiable conform to it. It's the protocol oriented approach.
protocol Identifiable {
static var identifier: String { get }
}
extension Identifiable {
static var identifier: String {
return String(describing: self)
}
}
class X: Identifiable {}
You also don't need to refer to the class name. Just call type(of: instance).identifier.

Blank constant when trying to get list of classes that have adopted a Protocol

I am trying to get a list of classes that have adopted a certain Protocol Migration: Preparation, and then to append those classes into an array. Here is the function in question:
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
var migrationsList = [Preparation.Type]()
var count = UInt32(0)
let classList = objc_copyClassList(&count)!
for i in 0..<Int(count) {
let classInfo = ClassInfo(classList[i])!
if let cls = classInfo.classObject as? Migration.Type {
migrationsList.append(cls)
print(cls.description)
}
}
return migrationsList
}
}
In principle all that should work, but when debugging I note that the classInfo variable is referring to each class in the iteration, but when assigning and casting in the if let as line, the constant cls is always blank - neither a value/class nor nil, just completely blank.
Any idea what I got wrong with that code?
I am also open to suggestions for any better way to get a list of all classes that have adopted a particular protocol...
EDIT: I forgot to provide the code for ClassInfo
import Foundation
struct ClassInfo: CustomStringConvertible, Equatable {
let classObject: AnyClass
let className: String
init?(_ classObject: AnyClass?) {
guard classObject != nil else { return nil }
self.classObject = classObject!
let cName = class_getName(classObject)!
self.className = String(cString: cName)
}
var superclassInfo: ClassInfo? {
let superclassObject: AnyClass? = class_getSuperclass(self.classObject)
return ClassInfo(superclassObject)
}
var description: String {
return self.className
}
static func ==(lhs: ClassInfo, rhs: ClassInfo) -> Bool {
return lhs.className == rhs.className
}
}
I can't explain why cls is always blank, like I said in my comment it's something I run into every time I'm dealing with meta types. As for making the code work as intended, I found this q&a and updated it with Swift 3 to get this code which should cover your situation. It's important to stress that this will only work if you correctly expose Swift to the Objective-C runtime.
Drop this code anywhere and call print(Migrations.getMigrations()) from a convenient entry point.
struct Migrations {
static func getMigrations() -> [Preparation.Type] {
return getClassesImplementingProtocol(p: Preparation.self) as! [Preparation.Type]
}
static func getClassesImplementingProtocol(p: Protocol) -> [AnyClass] {
let classes = objc_getClassList()
var ret = [AnyClass]()
for cls in classes {
if class_conformsToProtocol(cls, p) {
ret.append(cls)
}
}
return ret
}
static func objc_getClassList() -> [AnyClass] {
let expectedClassCount = ObjectiveC.objc_getClassList(nil, 0)
let allClasses = UnsafeMutablePointer<AnyClass?>.allocate(capacity: Int(expectedClassCount))
let autoreleasingAllClasses = AutoreleasingUnsafeMutablePointer<AnyClass?>(allClasses)
let actualClassCount:Int32 = ObjectiveC.objc_getClassList(autoreleasingAllClasses, expectedClassCount)
var classes = [AnyClass]()
for i in 0 ..< actualClassCount {
if let currentClass: AnyClass = allClasses[Int(i)] {
classes.append(currentClass)
}
}
allClasses.deallocate(capacity: Int(expectedClassCount))
return classes
}
}
class Migration: Preparation {
}
#objc
protocol Preparation {
}

Access static variables within class in Swift

Is ClassName.staticVaribale the only way to access static variable within the class? I want something like self, but for class. Like class.staticVariable.
There are two ways to access a static property/method from a non-static property/method:
As stated in your question, you can prefix the property/method name with that of the type:
class MyClass {
static let staticProperty = 0
func method() {
print(MyClass.staticProperty)
}
}
Swift 2: You can use dynamicType:
class MyClass {
static let staticProperty = 0
func method() {
print(self.dynamicType.staticProperty)
}
}
Swift 3: You can use type(of:) (thanks #Sea Coast of Tibet):
class MyClass {
static let staticProperty = 0
func method() {
print(type(of: self).staticProperty)
}
}
If you're inside a static property/method you do not need to prefix the static property/method with anything:
class MyClass {
static let staticProperty = 0
static func staticMethod() {
print(staticProperty)
}
}
This is solved elegantly in Swift 5.1
you can access it via
Self.yourConstant
Reference: https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md
There is a way in Swift to make Marcel's answer satisfy even most picky style-guide gods
class MyClass {
private typealias `Self` = MyClass
static let MyConst = 5
func printConst() {
print(Self.MyConst)
}
}
That makes Self available like in protocols when you want access associated type declaration.
I am not sure about Swift 1 because never tried it but in Swift 2 it works perfectly
In a future Swift 3 version (yet to be released) you can use Self (yes, that's with a capital) to reference to the containing class. A proposal for this was accepted, but the feature is not implemented yet.
For example:
struct CustomStruct {
static func staticMethod() { ... }
func instanceMethod() {
Self.staticMethod() // in the body of the type
}
}
Source: https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md
You could work around this by defining a self referencing typealias.
class MyClassWithALongName {
typealias CLASS = MyClassWithALongName
static let staticFoo = "foo"
func someInstanceMethod() -> String {
return CLASS.staticFoo
}
}
Though the style-guide gods may not approve.
It looks like in Swift 4.2 the inner class and instance variable can directly access the static variable without the prefix of the class name. However, you still need the class name within a function.
class MyClass {
static let staticProperty = 0
let property = staticProperty //YES
class Inner {
func method() {
print(staticProperty) //YES
}
}
func method() {
print(staticProperty) //NO
print(MyClass.staticProperty) //YES
}
}
I don't like the typealias way in this case. My workaround is:
class MyClass {
static let myStaticConst: Int = 1
var myStaticConst:Int {
return type(of: self).myStaticConst
}
func method() {
let i:Int = myStaticConst
}
}

Get class name of object as string in 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

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