I have a base class and a derived class... the base class has a class function userDefaultKeyPrefix() which indicates the user defaults key for user default storage using a wrapper
however the AdvancedManager class ends up with the BaseManager class value of userDefaultKeyPrefix() "BASE.someVar" instead of "ADVANCED.someVar"
Why is this happening and how to achieve the desired outcome so the keys are given by the class
The init of the someVar is called as the Base class, not the Advanced
class BaseManager {
class func userDefaultKeyPrefix() -> String {
return "BASE"
}
#UserDefault(userDefaultKeyPrefix() + ".someVar", defaultValue: false) var someVar: Bool
}
class AdvancedManager: BaseManager {
override class func userDefaultKeyPrefix() -> String {
return "ADVANCED"
}
}
#propertyWrapper
struct UserDefault<T> {
let key: String
let defaultValue: T
init(_ key: String, defaultValue: T) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T {
get {
return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
//let oldValue = wrappedValue
UserDefaults.standard.set(newValue, forKey: key)
UserDefaults.standard.synchronize()
}
}
}
UPDATE: This bellow works (the key is the "lazy") as expected, but defeats the clean code of a property wrapper
func prefix() -> String {
return String(describing: type(of: self))
}
lazy var _someVar = UserDefault(self.prefix() + ".someVar", defaultValue: "DEFAULT")
var someVar: String {
get {
return _someVar.wrappedValue
}
set {
_someVar.wrappedValue = newValue
}
}
Any better solutions are welcome as answer
Related
A struct with a generic type can be extended with a where clause to add new functionality when the generic type conforms to a certain protocol. What am trying to accomplish is slightly different. I have a struct with a generic type, and a function that I want to change its implementation if the generic type conforms to Codable. Unfortunately, the overriden function in the extension never gets triggered, if I call the function from within the struct itself. But if I called it from outside, the right implementation is triggered.
struct GenericStruct<T> {
private var _value: T
var value: T {
get {
printText()
return _value
}
set {
_value = newValue
}
}
init(value: T) {
self._value = value
}
func printText() {
print("General Print Function")
}
}
extension GenericStruct where T: Codable {
func printText() {
print("Codable Function")
}
}
let test = GenericStruct(value: 1)
print(test.value) // print General Print Function
test.printText() // print Codable Function
Is there a way to invoke printText() function based on T type from within the struct?
EDIT:
Am trying to invoke the right implementation from inside a propertyWrapper struct
#propertyWrapper struct Caching<Value> {
var key: String
var defaultValue: Value
var cachingType = CachingType.userDefaults
enum CachingType {
case userDefaults
case custom
}
var wrappedValue: Value {
get {
switch cachingType {
case .userDefaults:
return UserDefaults.standard.value(forKey: key) as? Value ?? defaultValue
case .custom:
return retrieveValueFromCachingLayer()
}
}
set {
switch cachingType {
case .userDefaults:
UserDefaults.standard.set(newValue, forKey: key)
case .custom:
store(value: newValue)
}
}
}
func store(value: Value) {
assertionFailure("This value type is not supported by the property wrapper")
}
func retrieveValueFromCachingLayer() -> Value {
assertionFailure("This value type is not supported by the property wrapper")
return defaultValue
}
}
extension Caching where Value: Codable {
func retrieveValueFromCachingLayer() -> Value {
print("retrieve value from a custom caching layer")
return defaultValue
}
func store(value: Value) {
print("store value in a custom caching layer")
}
}
Not without redefinition. The cleanest way, I think, is to separate implementations in extensions, but you can also leave what you have already and just add the specialized value.
struct GenericStruct<T> {
private var _value: T
init(value: T) {
_value = value
}
}
extension GenericStruct {
var value: T {
get {
printText()
return _value
}
set {
_value = newValue
}
}
func printText() {
print("General Print Function")
}
}
extension GenericStruct where T: Codable {
var value: T {
get {
printText()
return _value
}
set {
_value = newValue
}
}
func printText() {
print("Codable Function")
}
}
Edit after learning that you're trying to work with property wrappers:
#propertyWrapper struct UserDefault<Value> {
var key: String
var defaultValue: Value
var wrappedValue: Value {
get {
UserDefaults.standard.value(forKey: key) as? Value ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
}
#propertyWrapper struct Caching<Value: Codable> {
var defaultValue: Value
var wrappedValue: Value {
get {
print("retrieve value from a custom caching layer")
return defaultValue
}
set {
print("store value in a custom caching layer")
}
}
}
From within a property wrapper in Swift, can you someone refer back to the instance of the class or struck that owns the property being wrapped? Using self doesn't obviously work, nor does super.
I tried to pass in self to the property wrapper's init() but that doesn't work either because self on Configuration is not yet defined when #propertywrapper is evaluated.
My use case is in a class for managing a large number of settings or configurations. If any property is changed, I just want to notify interested parties that something changed. They don't really need to know which value just, so use something like KVO or a Publisher for each property isn't really necessary.
A property wrapper looks ideal, but I can't figure out how to pass in some sort of reference to the owning instance that the wrapper can call back to.
References:
SE-0258
enum PropertyIdentifier {
case backgroundColor
case textColor
}
#propertyWrapper
struct Recorded<T> {
let identifier:PropertyIdentifier
var _value: T
init(_ identifier:PropertyIdentifier, defaultValue: T) {
self.identifier = identifier
self._value = defaultValue
}
var value: T {
get { _value }
set {
_value = newValue
// How to callback to Configuration.propertyWasSet()?
//
// [self/super/...].propertyWasSet(identifier)
}
}
}
struct Configuration {
#Recorded(.backgroundColor, defaultValue:NSColor.white)
var backgroundColor:NSColor
#Recorded(.textColor, defaultValue:NSColor.black)
var textColor:NSColor
func propertyWasSet(_ identifier:PropertyIdentifier) {
// Do something...
}
}
The answer is no, it's not possible with the current specification.
I wanted to do something similar. The best I could come up with was to use reflection in a function at the end of init(...). At least this way you can annotate your types and only add a single function call in init().
fileprivate protocol BindableObjectPropertySettable {
var didSet: () -> Void { get set }
}
#propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
var value: T {
didSet {
self.didSet()
}
}
var didSet: () -> Void = { }
init(initialValue: T) {
self.value = initialValue
}
}
extension BindableObject {
// Call this at the end of init() after calling super
func bindProperties(_ didSet: #escaping () -> Void) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if var child = child.value as? BindableObjectPropertySettable {
child.didSet = didSet
}
}
}
}
You cannot do this out of the box currently.
However, the proposal you refer to discusses this as a future direction in the latest version:
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
For now, you would be able to use a projectedValue to assign self to.
You could then use that to trigger some action after setting the wrappedValue.
As an example:
import Foundation
#propertyWrapper
class Wrapper {
let name : String
var value = 0
weak var owner : Owner?
init(_ name: String) {
self.name = name
}
var wrappedValue : Int {
get { value }
set {
value = 0
owner?.wrapperDidSet(name: name)
}
}
var projectedValue : Wrapper {
self
}
}
class Owner {
#Wrapper("a") var a : Int
#Wrapper("b") var b : Int
init() {
$a.owner = self
$b.owner = self
}
func wrapperDidSet(name: String) {
print("WrapperDidSet(\(name))")
}
}
var owner = Owner()
owner.a = 4 // Prints: WrapperDidSet(a)
My experiments based on : https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
protocol Observer: AnyObject {
func observableValueDidChange<T>(newValue: T)
}
#propertyWrapper
public struct Observable<T: Equatable> {
public var stored: T
weak var observer: Observer?
init(wrappedValue: T, observer: Observer?) {
self.stored = wrappedValue
}
public var wrappedValue: T {
get { return stored }
set {
if newValue != stored {
observer?.observableValueDidChange(newValue: newValue)
}
stored = newValue
}
}
}
class testClass: Observer {
#Observable(observer: nil) var some: Int = 2
func observableValueDidChange<T>(newValue: T) {
print("lol")
}
init(){
_some.observer = self
}
}
let a = testClass()
a.some = 4
a.some = 6
The answer is yes! See this answer
Example code for calling ObservableObject publisher with a UserDefaults wrapper:
import Combine
import Foundation
class LocalSettings: ObservableObject {
static var shared = LocalSettings()
#Setting(key: "TabSelection")
var tabSelection: Int = 0
}
#propertyWrapper
struct Setting<T> {
private let key: String
private let defaultValue: T
init(wrappedValue value: T, key: String) {
self.key = key
self.defaultValue = value
}
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
public static subscript<EnclosingSelf: ObservableObject>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Setting<T>>
) -> T {
get {
return object[keyPath: storageKeyPath].wrappedValue
}
set {
(object.objectWillChange as? ObservableObjectPublisher)?.send()
UserDefaults.standard.set(newValue, forKey: object[keyPath: storageKeyPath].key)
}
}
}
I have two protocols and two objects that implement them. One object uses name:String as its primary key, the other uses code:Int.
protocol AlphaProtocol{
var name:String {get set}
init(name: String)
}
protocol BetaProtocol{
var code: Int {get set}
init(code:Int)
}
class AlphaObject: AlphaProtocol{
var name: String
required init(name: String){
self.name = name
}
}
class BetaObject: BetaProtocol{
var code: Int
required init(code: Int){
self.code = code
}
}
Right now, to store the these objects I use two different memory stores that implement two different protocols, one for each kind of object.
protocol AlphaStoreProtocol{
func addObject(object anObject: AlphaProtocol)
func getObject(name aName:String)->AlphaProtocol?
func removeObject(name aName: String)
}
protocol BetaStoreProtocol{
func addObject(object anObject: BetaProtocol)
func getObject(code aCode:Int)->BetaProtocol?
func removeObject(code aCode: Int)
}
class AlphaStore{
fileprivate var objects = [AlphaProtocol]()
func addObject(object anObject: AlphaProtocol){
if getObject(name: anObject.name) == nil{
objects.append(anObject)
}
}
func getObject(name aName:String)->AlphaProtocol?{
for o in objects{
if o.name == aName{
return o
}
}
return nil
}
func removeObject(name aName: String){
self.objects = self.objects.filter({$0.name != aName})
}
}
class BetaStore: BetaStoreProtocol{
fileprivate var objects = [BetaProtocol]()
func addObject(object anObject: BetaProtocol){
if getObject(code: anObject.code) == nil{
objects.append(anObject)
}
}
func getObject(code aCode:Int)->BetaProtocol?{
for o in objects{
if o.code == aCode{
return o
}
}
return nil
}
func removeObject(code aCode: Int){
self.objects = self.objects.filter({$0.code != aCode})
}
}
Test Code using two tailor made stores.
let alpha = AlphaObject(name: "Alpha")
let beta = BetaObject(code: 12345)
let alphaStore = AlphaStore()
let betaStore = BetaStore()
alphaStore.addObject(object: alpha)
if (alphaStore.getObject(name: alpha.name) != nil){
print("alpha object has been added to alphaStore")
}
alphaStore.removeObject(name: alpha.name)
if (alphaStore.getObject(name: alpha.name) == nil){
print("alpha object has been removed from alphaStore")
}
betaStore.addObject(object: beta)
if (betaStore.getObject(code: beta.code) != nil){
print("beta object has been added to betaStore")
}
betaStore.removeObject(code: beta.code)
if (betaStore.getObject(code: beta.code) == nil){
print("beta object has been removed from betaStore")
}
The goal: using a single generic class for both the stores but I'm stuck because the two objects use two different primary keys (different type and different name) and I can't simply force a generic "Id" as the primary key in the objects. One has to be named "name" and the other "code".
Is there a way to write the getObject and removeObject methods to accept both kind of objects?
protocol GenericStoreProtocol{
associatedtype T
func addObject(object anObject: T)
// func getObject()->T // One object use a name:String, the other code:Int as its primary key!
// func removeObject() // One object use a name:String, the other code:Int as its primary key!
}
class GenericStore<T>: GenericStoreProtocol{
fileprivate var objects = [T]()
func addObject(object anObject: T){
objects.append(anObject)
}
// ...
}
let genericAlphaStore = GenericStore<AlphaProtocol>()
let genericBetaStore = GenericStore<BetaProtocol>()
To generalize the problem, we need a store that can:
add items of any types (or ones we specify)
look up and delete items by id
use the correct id property for different stored objects
First, I'd create a protocol called Storable which has an identifier computed property. This should be of type Equatable as we will eventually be using equality comparisons when looking up objects by id in our Store.
protocol Storable {
associatedtype Identifier: Equatable
var identifier: Identifier { get }
}
We can now define the classes of the objects we are going to store (AlphaObject and BetaObject). Both of these classes should conform to their own protocol as well as the Stored protocol. Here is where you'd define what property should be used as the identifier. For AlphaObject it's name and for BetaObject it's code. These can be read-only computed properties that return the values of name and code respectively.
protocol AlphaProtocol {
var name: String { get set }
init(name: String)
}
protocol BetaProtocol {
var code: Int { get set }
init(code: Int)
}
class AlphaObject: AlphaProtocol, Storable {
typealias Identifier = String
internal var identifier: Identifier {
return self.name
}
var name: String
required init(name: String) {
self.name = name
}
}
class BetaObject: BetaProtocol, Storable {
typealias Identifier = Int
internal var identifier: Identifier {
return self.code
}
var code: Int
required init(code: Int){
self.code = code
}
}
Finally, our Store will take any objects that are Storable and will access, insert, and delete based on T's specified identifier.
class Store<T: Storable> {
fileprivate var objects = [T]()
func addObject(object: T) {
if getObject(identifier: object.identifier) == nil {
objects.append(object)
}
}
func getObject(identifier: T.Identifier) -> T? {
for o in objects {
if o.identifier == identifier {
return o
}
}
return nil
}
func removeObject(identifier: T.Identifier) {
self.objects = self.objects.filter({$0.identifier != identifier})
}
}
The full code with tests!
protocol Storable {
associatedtype Identifier: Equatable
var identifier: Identifier { get }
}
protocol AlphaProtocol {
var name: String { get set }
init(name: String)
}
protocol BetaProtocol {
var code: Int { get set }
init(code: Int)
}
class AlphaObject: AlphaProtocol, Storable {
typealias Identifier = String
internal var identifier: Identifier {
return self.name
}
var name: String
required init(name: String) {
self.name = name
}
}
class BetaObject: BetaProtocol, Storable {
typealias Identifier = Int
internal var identifier: Identifier {
return self.code
}
var code: Int
required init(code: Int){
self.code = code
}
}
class Store<T: Storable> {
fileprivate var objects = [T]()
func addObject(object: T) {
if getObject(identifier: object.identifier) == nil {
objects.append(object)
}
}
func getObject(identifier: T.Identifier) -> T? {
for o in objects {
if o.identifier == identifier {
return o
}
}
return nil
}
func removeObject(identifier: T.Identifier) {
self.objects = self.objects.filter({$0.identifier != identifier})
}
}
/* Tests */
let alpha = AlphaObject(name: "Alpha")
let beta = BetaObject(code: 12345)
let alphaStore = Store<AlphaObject>()
let betaStore = Store<BetaObject>()
alphaStore.addObject(object: alpha)
if (alphaStore.getObject(identifier: alpha.name) != nil){
print("alpha object has been added to alphaStore")
}
alphaStore.removeObject(identifier: alpha.name)
if (alphaStore.getObject(identifier: alpha.name) == nil){
print("alpha object has been removed from alphaStore")
}
betaStore.addObject(object: beta)
if (betaStore.getObject(identifier: beta.code) != nil){
print("beta object has been added to betaStore")
}
betaStore.removeObject(identifier: beta.code)
if (betaStore.getObject(identifier: beta.code) == nil){
print("beta object has been removed from betaStore")
}
I can't simply force a generic "Id" as the primary key in the objects.
Yep, you totally can if you use a single protocol instead of two unrelated ones (AlphaProtocol and BetaProtocol).
protocol KeyedObject {
associatedtype PrimaryKey : Equatable
var key: PrimaryKey { get }
}
Just make your objects conform to this protocol; they can declare whatever equatable type you require for the key, they just have to provide some way to access it.
class AlphaObject: KeyedObject {
typealias PrimaryKey = String
var name: String
required init(name: String) {
self.name = name
}
var key: String {
return self.name
}
}
Then you can use a straightforward generic class that contains only objects you provided:
class GenericStore<T : KeyedObject> {
fileprivate var objects = [T]()
func addObject(object anObject: T){
objects.append(anObject)
}
func getObject(key: T.PrimaryKey) -> T? {
for o in objects{
if o.key == key {
return o
}
}
return nil
}
...
}
I'm trying to implement a class in Swift 2 for a single setting of any type that uses NSUserDefaults under the covers.
Problem: How do I define a class for storing and retrieving any type of object, including Dictionary?
I have a solution that works with AnyObject which consists of a generic protocol (Settable) and a generic class (Setting). SettingsStore is a wrapper around NSUserDefaults.
// MARK: Settable Protocol
public protocol Settable {
typealias T
init(key: String, defaultValue: T, settingsStore: SettingsStore)
var value: T { get set }
func loadCurrentValue()
}
// MARK: Settings Class
public class Setting<T: AnyObject>: Settable {
private let key: String
private let defaultValue: T
private let settingsStore: SettingsStore
private var currentValue: T?
public required init(key: String, defaultValue: T, settingsStore: SettingsStore) {
self.key = key
self.defaultValue = defaultValue
self.settingsStore = settingsStore
}
public var value: T {
get {
if self.currentValue == nil {
self.loadCurrentValue()
}
return self.currentValue!
}
set {
self.currentValue = newValue
self.settingsStore.setObject(newValue.toAnyObject(), forKey: self.key)
}
}
public func loadCurrentValue() {
let optionalValue: T? = self.settingsStore.objectForKey(key) as? T
if let value = optionalValue {
self.currentValue = value
} else {
self.currentValue = self.defaultValue
}
}
}
This allows me to create a setting like this:
let specialId: Setting<String>
init() {
self.specialId = Setting<String>(
key: "specialId",
defaultValue: "<somevalue>",
settingsStore: self.settingsStore)
}
The problem with this is that it doesn't work with value types, such as String, Bool, Int, Double, Array, or Dictionary because they are all value types and value types don't conform to the AnyObject protocol.
I've solved the problem for some of these using a protocol and extensions based on NSString and NSNumber, but a solution Dictionary is proving to be elusive (I don't need a solution for Array at the moment so I haven't spent time trying to solve that one).
// Change definition of Setting class like this:
public class Setting<T: AnyObjectRepresentable>: Settable {
...
}
public protocol AnyObjectRepresentable {
func toAnyObject() -> AnyObject
static func fromAnyObject(value: AnyObject) -> Self?
}
extension AnyObjectRepresentable where Self: AnyObject {
public func toAnyObject() -> AnyObject {
return self
}
public static func fromAnyObject(value: AnyObject) -> AnyObject? {
return value
}
}
extension String: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSString(string: self)
}
public static func fromAnyObject(value: AnyObject) -> String? {
let convertedValue = value as? String
return convertedValue
}
}
extension Bool: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSNumber(bool: self)
}
public static func fromAnyObject(value: AnyObject) -> Bool? {
let convertedValue = value as? Bool
return convertedValue
}
}
// Add extensions for Int and Double that look like the above extension for Bool.
I tried two different approaches for Dictionary. The first one is similar to the String approach:
extension Dictionary: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
let value = self as NSDictionary
return value
}
public static func fromAnyObject(value: AnyObject) -> Dictionary? {
let convertedValue = value as? Dictionary
return convertedValue
}
}
Xcode gives me the following error on the first line of the toAnyObject() method implementation:
'Dictionary' is not convertible to 'NSDictionary'
Next I tried extending NSDictionary directly:
extension NSDictionary: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSDictionary(dictionary: self)
}
public static func fromAnyObject(value: AnyObject) -> NSDictionary? {
let convertedValue = value as? NSDictionary
return convertedValue
}
}
Xcode gives me the following error on the declaration of fromAnyObject():
Method 'fromAnyObject' in non-final class 'NSDictionary' must return Self to conform to protocol 'AnyObjectRepresentable'
I'm at my wits. Is this solvable?
Thanks,
David
UPDATED 2015-09-15 16:30
For background, here is the definition and an implementation of SettingsStore:
public protocol SettingsStore {
func objectForKey(key: String) -> AnyObject?
func setObject(value: AnyObject?, forKey key: String)
func dictionaryForKey(key: String) -> [String:AnyObject]?
}
public class UserDefaultsSettingsStore {
private let userDefaults: NSUserDefaults
public init() {
self.userDefaults = NSUserDefaults.standardUserDefaults()
}
public init(suiteName: String) {
self.userDefaults = NSUserDefaults(suiteName: suiteName)!
}
}
extension UserDefaultsSettingsStore: SettingsStore {
public func objectForKey(key: String) -> AnyObject? {
return self.userDefaults.objectForKey(key)
}
public func setObject(value: AnyObject?, forKey key: String) {
self.userDefaults.setObject(value, forKey: key)
self.userDefaults.synchronize()
}
public func dictionaryForKey(key: String) -> [String : AnyObject]? {
return self.userDefaults.dictionaryForKey(key)
}
}
If you substitute AnyObject with Any, I think you'll get the results you're looking for. Specifically, replace this line:
public class Setting<T: AnyObject>: Settable {
with this line
public class Setting<T: Any>: Settable {
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)
}
}
}