How to observe change of value and show it? In Swift - ios

I have a variable that takes the number of objects in the array in a different class, how can I keep track of the change of this variable in the current class? I did a lot of different attempts but failed.
var digitIndex: Int! {
set {
self.digitIndex = newValue
}
get {
return firstClass.indexesOfSelectedNodes().count
}
}
override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
if context == &digitIndex {
if let newValue = change?[NSKeyValueChangeNewKey] {
print("newValue")
infoLabel.text = "\(newValue)"
}
}
}

1) in your code
var digitIndex: Int! {
set {
self.digitIndex = newValue
}
get {
return firstClass.indexesOfSelectedNodes().count
}
}
digitIndex is computed property! You are not able to set self.digitIndex within its own setter!
this code, even though compiled, will run forever :-)
var i: Int {
get {
return 10
}
set {
i = newValue
}
}
i = 100
print(i)
2) How to use willSet and didSet (for STORED properties)?
class C1 {}
class C2 {}
class C {
var objects: [AnyObject] = [C1(),C1(),C2()] {
willSet {
print(objects, objects.count, newValue)
}
didSet {
print(objects, objects.count)
}
}
func add(object: AnyObject) {
objects.append(object)
}
}
let c = C()
c.add(C1())
/*
[C1, C1, C2] 3 [C1, C1, C2, C1]
[C1, C1, C2, C1] 4
*/
var i: Int = 0 {
willSet {
print(newValue, "will replace", i)
}
didSet {
print(oldValue, "was replaced by", i)
}
}
i = 100
/*
100 will replace 0
0 was replaced by 100
*/
you could combine the computed and stored properties for your advantage
// 'private' storage
var _j:Int = 0
var j: Int {
get {
return _j
}
set {
print(newValue)
if newValue < 300 {
_j = newValue
} else {
print(newValue, "refused")
}
}
}
print(j) // prints 0
j = 200 // prints 200
print(j) // prints 200
j = 500 // prints 500 refused
print(j) // prints 200

Try this :
var digitIndex: Int! {
set {
self.digitIndex = newValue
}
get {
return firstClass.indexesOfSelectedNodes().count
}
didSet {
//you will get new value here
}
}
No need to add observer for our class properties, you just required to add observer for the properties provided by super class.

You can make use of a delegate handling the communication between your two classes.
An example follows below, using a custom delegate MyDelegate (protocol). A delegate instance is initialized in MyOtherClass (e.g., a view controller class), which makes a delegate callback whenever the array myArr is updated in this class. This, in turn, updates the value of digitIndex in MyCurrentClass (e.g., some custom control), which conforms to MyDelegate by implementing the blueprinted arrUpdated(..) method. Finally, the didSet property observer on digitIndex in MyCurrentClass tells us via console print-out that its value has been updated.
protocol MyDelegate: class {
func arrUpdated(arr: [Int])
}
class MyDifferentClass {
private var myArr : [Int] = [] {
didSet {
// Call delegate.
delegate?.arrUpdated(myArr)
}
}
weak var delegate: MyDelegate?
}
class MyCurrentClass: MyDelegate {
var myDifferentClass : MyDifferentClass
var digitIndex: Int = 0 {
didSet {
print("digitIndex updated: \(digitIndex)")
}
}
init(a: MyDifferentClass) {
myDifferentClass = a
myDifferentClass.delegate = self
}
// MyDelegate
func arrUpdated(arr: [Int]) {
digitIndex = arr.count
}
}
Test:
var a = MyDifferentClass()
var b = MyCurrentClass(a: a)
a.myArr.append(1) // prints 'digitIndex updated: 1'

Related

Setters and Getters with Generics Swift

I was wondering if there is a way to do this in a better way with generics. I have this singleton which needs a setter and another getter for every object. Plus, I have to check that the property is not nil in every getter, which is a lot of repeated code. ex:
class DataManager : NSObject {
private override init(){}
private var postData : [PostModel]?
private var userData : [UserModel]?
private var commentsData : [CommentsModel]?
private var photosData : [PhotosModel]?
private var albumsData : [AlbumsModel]?
private var todosData : [TodosModel]?
static let shared = DataManager()
//MARK : Setters
func setUserData(data : [UserModel]) {
self.userData = data
}
func setPostData(data : [PostModel]) {
self.postData = data
}
func setCommentsData(data : [CommentsModel]) {
self.commentsData = data
}
func setAlbumsData(data : [AlbumsModel]) {
self.albumsData = data
}
func setPhotosData(data : [PhotosModel]) {
self.photosData = data
}
func setTodosData(data : [TodosModel]) {
self.todosData = data
}
//MARK : Getters
func getUserData() -> [UserModel]? {
if self.userData != nil {
return self.userData!
}
return nil
}
func getPostData() -> [PostModel]? {
if self.postData != nil {
return self.postData!
}
return nil
}
func getCommentsData() -> [CommentsModel]? {
if self.commentsData != nil {
return self.commentsData!
}
return nil
}
func getAlbumsData() -> [AlbumsModel]? {
if self.albumsData != nil {
return self.albumsData!
}
return nil
}
func getPhotosData() -> [PhotosModel]? {
if self.photosData != nil {
return self.photosData!
}
return nil
}
func getTodosData() -> [TodosModel]? {
if self.todosData != nil {
return self.todosData!
}
return nil
}
}
I was wondering if all this logic could be done in one single method, maybe using generics?
If you want to force all object to set as a none optional and also get as a none optional, you don't need to define them as optional at the first place:
So instead of:
private var postData : [PostModel]?
you should have:
private var postData = [PostModel]()
This will gives you an empty none optional array and it can not be set or get as an optional.
If you want them to be nil before someone get them (for memory management or etc.), You can make them lazy:
private lazy var postData = [PostModel]()
So now postData will be nil until someone tries to read the value of it.
If you need to do some extra job when someone tries to set one of these, you can observe for changes before set and after set of the value:
private var postData = [PostModel]() {
willSet { /* right before the value is going to set */ }
didSet { /* right after the value is set */ }
}
Note that lazy properties can not have observers
So seems like you don't have any of the functions at all. And you can refactor your code to something like this:
class DataManager : NSObject {
private override init(){}
lazy var postData = [PostModel]()
lazy var userData = [UserModel]()
lazy var commentsData = [CommentsModel]()
lazy var photosData = [PhotosModel]()
lazy var albumsData = [AlbumsModel]()
lazy var todosData = [TodosModel]()
}

What am I doing wrong on passing data through protocol

I'm trying to pass data between viewControllers, but something seems wrong.
The first viewController I want to set the "Bool" to the protocol function to be able to recover in the other screen. What am I doing wrong, I always used protocols but at this time I got in trouble.
That's how I'm doing that:
//
// ComboBoxNode.swift
//
import Foundation
import SWXMLHash
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool)
}
class ComboBoxNode: FormControlNode, IFormControlDataSource {
var listType: String?
var dataSource: String?
var dataSourceValue: String?
var dataSourceText: String?
var hasCustomOption:Bool?
var customOptionText: String?
var ctrlDataSourceType: String?
var parameters = [ParameterNode]()
var staticList: FormControlStaticListNode?
var delegate:ComboBoxNodeDelegate?
override init(indexer: XMLIndexer) {
super.init(indexer: indexer)
guard let element = indexer.element else {
preconditionFailure("Error")
}
let isCustomOption = element.bool(by: .hasCustomOption) ?? hasCustomOption
if isCustomOption == true {
self.delegate?.getCustomOption(data: hasCustomOption!)
}
self.readFormControlDataSource(indexer: indexer)
}
override func accept<T, E: IViewVisitor>(visitor: E) -> T where E.T == T {
return visitor.visit(node: self)
}
}
That's how I'm trying to recover on next screen:
// FormPickerViewDelegate.swift
import Foundation
import ViewLib
import RxSwift
class FormPickerViewDelegate: NSObject {
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
private var controlViewModel: FormControlViewModel
private var customText:Bool?
private var PickerNodeDelegate:ComboBoxNodeDelegate?
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
}
func getItemByValue(_ value: Any) -> (AnyHashable, String)? {
if value is AnyHashable {
let found = items.value.filter {$0.value == value as! AnyHashable}
if found.count >= 1 {
return found[0]
}
}
return nil
}
}
extension FormPickerViewDelegate:ComboBoxNodeDelegate {
func getCustomOption(data: Bool) {
customText = data
}
}
Instead of setting PickerNodeDelegate = self in didSet {} closure
var items = Variable([(value: AnyHashable, text: String)]()) {
didSet {
PickerNodeDelegate = self
self.setDefaultValues()
}
}
Assign it in your init() function instead
init(controlViewModel: FormControlViewModel) {
self.controlViewModel = controlViewModel
PickerNodeDelegate = self
}
Note, your should declare your delegate to be weak also, since it's a delegate, your protocol should conform to be a class type in order to be weakified.
protocol ComboBoxNodeDelegate: class
...
weak var delegate: ComboBoxNodeDelegate?
Here is an example, hope it helps!
protocol ComboBoxNodeDelegate {
func getCustomOption(data:Bool) -> String
}
class ViewOne:ComboBoxNodeDelegate {
var foo:Bool = false
var bar:String = "it works!"
/** Return: String */
func getCustomOption(data:Bool) -> String { //conform here to protocol
// do whatever you wanna do here ...example
self.foo = data // you can set
return bar // even return what you want
}
//initialize
func initalizeViewTwo() {
let v2 = ViewTwo()
v2.delegate = self //since `self` conforms to the ComboBoxNodeDelegate protcol you are allowed to set
}
}
class ViewTwo {
var delegate:ComboBoxNodeDelegate?
func getCustomOption_forV1() {
let view2_foo = delegate.getCustomOption(data:true)
print(view2_foo) // should print "it works!"
}
}
All parameters passed around in Swift are constants -- so you cannot change them.
If you want to change them in a function, you must declare your protocol to pass by reference with inout:
protocol ComboBoxNodeDelegate {
func getCustomOption(data: inout Bool)
}
Note: you cannot pass a constant (let) to this function. It must be a variable -- which I see you are doing!

Cannot assign to property: 'self' is immutable swift error

Inside my app I created a Protocol called "Positions" and extended the UIView class to conform to this protocol in order to add some properties to the UIView class. I used the code below.
import Foundation
import UIKit
protocol Positions {
var initialPositions: [CGRect] {get set}
var finalPositions: [CGRect] {get set}
var positionsAreDefined: Bool {get}
}
public enum PositionsType {
case initial
}
private var initialPositionKey: UInt = 0
private var finalPositionKey: UInt = 0
extension Positions {
var initialPositions: [CGRect] {
get {
if objc_getAssociatedObject(self, &initialPositionKey) != nil {
return objc_getAssociatedObject(self, &initialPositionKey) as! [CGRect]
} else {
return [] as! [CGRect]
}
}
set {
objc_setAssociatedObject(self, &initialPositionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var finalPositions: [CGRect] {
get {
if objc_getAssociatedObject(self, &finalPositionKey) != nil {
return objc_getAssociatedObject(self, &finalPositionKey) as! [CGRect]
} else {
return [] as! [CGRect]
}
}
set {
objc_setAssociatedObject(self, &finalPositionKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var positionsAreDefined: Bool {
get {return initialPositions.count != 0 && finalPositions.count != 0 && initialPositions.count == finalPositions.count}
}
var positionsCount: Int {
get {return initialPositions.count}
}
}
extension UIView: Positions {}
Well, today I tried to extend the UIView class with a new method that changes the added-by-me properties but the compiler gave me this error on the line where I tried to modify the properties value:
Cannot assign to property: 'self' is immutable
Here's the method:
public func horizontallyInContainer(withShift shift: CGFloat, forExpansionNumber index: Int, forPositionType type: PositionsType) {
if self.positionsAreDefined && self.superview != nil {
if type == .initial {
var newPositions = self.initialPositions
newPositions[index].origin.x = self.superview!.bounds.width * 0.5 - self.bounds.width + shift
self.initialPositions = newPositions //Here happens the error
} else {
var newPositions = self.finalPositions
newPositions[index].origin.x = self.superview!.bounds.width * 0.5 - self.bounds.width + shift
self.finalPositions = newPositions //Also here happens
}
}
}
Can anyone explain me the nature of that error?
Make the protocol become reference type
protocol Positions: AnyObject {

Queue implementation in Swift language

I m trying to implement Queue collection type in Swift platform. I have got some problems about peek, poll and offer functions. When I try to use these functions in my code, it fails. Do you have any advice or true algorithm for that?
import Foundation
class Node<T> {
var value: T? = nil
var next: Node<T>? = nil
var prev: Node<T>? = nil
init() {
}
init(value: T) {
self.value = value
}
}
class Queue<T> {
var count: Int = 0
var head: Node<T> = Node<T>()
var tail: Node<T> = Node<T>()
var currentNode : Node<T> = Node<T>()
init() {
}
func isEmpty() -> Bool {
return self.count == 0
}
func next(index:Int) -> T? {
if isEmpty() {
return nil
} else if self.count == 1 {
var temp: Node<T> = currentNode
return temp.value
} else if index == self.count{
return currentNode.value
}else {
var temp: Node<T> = currentNode
currentNode = currentNode.next!
return temp.value
}
}
func setCurrentNode(){
currentNode = head
}
func enQueue(key: T) {
var node = Node<T>(value: key)
if self.isEmpty() {
self.head = node
self.tail = node
} else {
node.next = self.head
self.head.prev = node
self.head = node
}
self.count++
}
func deQueue() -> T? {
if self.isEmpty() {
return nil
} else if self.count == 1 {
var temp: Node<T> = self.tail
self.count--
return temp.value
} else {
var temp: Node<T> = self.tail
self.tail = self.tail.prev!
self.count--
return temp.value
}
}
//retrieve the top most item
func peek() -> T? {
if isEmpty() {
return nil
}
return head.value!
}
func poll() -> T? {
if isEmpty() {
return nil
}else{
var temp:T = head.value!
deQueue()
return temp
}
}
func offer(var key:T)->Bool{
var status:Bool = false;
self.enQueue(key)
status = true
return status
}
}
Aside from the bugs, there are a couple of things about your implementation that you probably want to change to make it more Swift-like. One is it looks like you're replicating the Java names like poll and offer – these names are (IMHO) a little strange, and partly related to needing to have two functions, an exception-throwing version and a non-exception version. Since Swift doesn't have exceptions, you can probably just name them using the conventional names other Swift collections use, like append.
The other issue is that your implementation incorporates traversing the queue into the queue itself. It's better to do this kind of traversal outside the collection than mix the two. Swift collections do this with indexes.
Here's a possible Swift-like queue implementation. First, the node and base queue definition:
// singly rather than doubly linked list implementation
// private, as users of Queue never use this directly
private final class QueueNode<T> {
// note, not optional – every node has a value
var value: T
// but the last node doesn't have a next
var next: QueueNode<T>? = nil
init(value: T) { self.value = value }
}
// Ideally, Queue would be a struct with value semantics but
// I'll leave that for now
public final class Queue<T> {
// note, these are both optionals, to handle
// an empty queue
private var head: QueueNode<T>? = nil
private var tail: QueueNode<T>? = nil
public init() { }
}
Then, extend with an append and dequeue method:
extension Queue {
// append is the standard name in Swift for this operation
public func append(newElement: T) {
let oldTail = tail
self.tail = QueueNode(value: newElement)
if head == nil { head = tail }
else { oldTail?.next = self.tail }
}
public func dequeue() -> T? {
if let head = self.head {
self.head = head.next
if head.next == nil { tail = nil }
return head.value
}
else {
return nil
}
}
}
At this point, you're almost done if all you want to do is add and remove. To add traversal, first create an index type, which is a simple wrapper on the node type:
public struct QueueIndex<T>: ForwardIndexType {
private let node: QueueNode<T>?
public func successor() -> QueueIndex<T> {
return QueueIndex(node: node?.next)
}
}
public func ==<T>(lhs: QueueIndex<T>, rhs: QueueIndex<T>) -> Bool {
return lhs.node === rhs.node
}
Then, use this index to conform to MutableCollectionType:
extension Queue: MutableCollectionType {
public typealias Index = QueueIndex<T>
public var startIndex: Index { return Index(node: head) }
public var endIndex: Index { return Index(node: nil) }
public subscript(idx: Index) -> T {
get {
precondition(idx.node != nil, "Attempt to subscript out of bounds")
return idx.node!.value
}
set(newValue) {
precondition(idx.node != nil, "Attempt to subscript out of bounds")
idx.node!.value = newValue
}
}
typealias Generator = IndexingGenerator<Queue>
public func generate() -> Generator {
return Generator(self)
}
}
From conforming to collection type, you get a whole load of stuff for free:
var q = Queue<String>()
q.append("one")
q.append("two")
for x in q {
println(x)
}
isEmpty(q) // returns false
first(q) // returns Optional("one")
count(q) // returns 2
",".join(q) // returns "one,two"
let x = find(q, "two") // returns index of second entry
let counts = map(q) { count($0) } // returns [3,3]
Finally, there's 3 more protocols that are good to conform to: ExtensibleCollectionType, Printable and ArrayLiteralConvertible:
// init() and append() requirements are already covered
extension Queue: ExtensibleCollectionType {
public func reserveCapacity(n: Index.Distance) {
// do nothing
}
public func extend<S : SequenceType where S.Generator.Element == T>
(newElements: S) {
for x in newElements {
self.append(x)
}
}
}
extension Queue: ArrayLiteralConvertible {
public convenience init(arrayLiteral elements: T...) {
self.init()
// conformance to ExtensibleCollectionType makes this easy
self.extend(elements)
}
}
extension Queue: Printable {
// pretty easy given conformance to CollectionType
public var description: String {
return "[" + ", ".join(map(self,toString)) + "]"
}
}
These mean you can now create queues as easily arrays or sets:
var q: Queue = [1,2,3]
println(q) // prints [1, 2, 3]
There are a lot of little issues regarding the internal consistency of your model:
When you first instantiate a new Queue, you are initializing head, tail and current to three different Node objects (even though nothing's been queued yet!). That doesn't make sense. Personally, I'd be inclined to make those three properties optional and leave them as nil until you start enqueuing stuff.
By the way, when you start using optionals for these properties, many of the other methods are simplified.
It looks like you're trying to implement a doubly linked list. So, when you dequeue, you need to not only update the Queue properties, but you also need to update the next pointer for the next item that will be dequeued (because it still will be pointing to that item you already dequeued). You don't want your linked list maintaining references to objects that have been dequeued and should be removed.
When you dequeue the last item, you really should be clearing out head and tail references.
You're implementing a doubly linked list, without regard to the object ownership model. Thus, as soon as you have more than one item in your list, you've got a strong reference cycle between nodes and if not remedied, this will leak if there are still objects in the queue when the queue, itself, is deallocated. Consider making one of the references weak or unowned.
I'd suggest keeping this simple (just enqueue and dequeue). The concept of poll and offer may make sense in terms of an arbitrary linked list, but not in the context of a queue. The implementations of poll and offer are also incorrect (e.g. poll calls deQueue which removes the tail, but the object you return is the head!), but I presume you'd just remove these functions altogether. Likewise, I do not understand the intent of current in the context of a queue.
I'd suggest you make Queue and Node conform to Printable. It will simplify your debugging process.
The following is code of a playground consisting of a queue implemented with an array and a queue implemented with nodes. There are substantial performance differences between the two but if you going to be iterating through a queue you might want to use one with an array.
import UIKit // for NSDate() used in testing)
// QUEUE WITH ARRAY IMPLEMENTATION (For ease of adaptibility, slow enque, faster deque):
struct QueueArray<T> {
private var items = [T]()
mutating func enQueue(item: T) {
items.append(item)
}
mutating func deQueue() -> T? {
return items.removeFirst()
}
func isEmpty() -> Bool {
return items.isEmpty
}
func peek() -> T? {
return items.first
}
}
// QUEUE WITH NODE IMPLEMENTATION (For performance, if all you need is a queue this is it):
class QNode<T> {
var value: T
var next: QNode?
init(item:T) {
value = item
}
}
struct Queue<T> {
private var top: QNode<T>!
private var bottom: QNode<T>!
init() {
top = nil
bottom = nil
}
mutating func enQueue(item: T) {
let newNode:QNode<T> = QNode(item: item)
if top == nil {
top = newNode
bottom = top
return
}
bottom.next = newNode
bottom = newNode
}
mutating func deQueue() -> T? {
let topItem: T? = top?.value
if topItem == nil {
return nil
}
if let nextItem = top.next {
top = nextItem
} else {
top = nil
bottom = nil
}
return topItem
}
func isEmpty() -> Bool {
return top == nil ? true : false
}
func peek() -> T? {
return top?.value
}
}
// QUEUE NODES TEST
let testAmount = 100
var queueNodes = Queue<Int>()
let queueNodesEnqueStart = NSDate()
for i in 0...testAmount {
queueNodes.enQueue(i)
}
let queueNodesEnqueEnd = NSDate()
while !queueNodes.isEmpty() {
queueNodes.deQueue()
}
let queueNodesDequeEnd = NSDate()
// QUEUE ARRAY TEST
var queueArray = QueueArray<Int>()
let queueArrayEnqueStart = NSDate()
for i in 0...testAmount {
queueArray.enQueue(i)
}
let queueArrayEnqueEnd = NSDate()
while !queueArray.isEmpty() {
queueArray.deQueue()
}
let queueArrayDequeEnd = NSDate()
// QUEUE NODES RESULT:
print("queueEnqueDuration: \(queueNodesEnqueEnd.timeIntervalSinceDate(queueNodesEnqueStart)), Deque: \(queueNodesDequeEnd.timeIntervalSinceDate(queueNodesEnqueEnd))")
// QUEUE ARRAY RESULT:
print("queueArrayEnqueDuration: \(queueArrayEnqueEnd.timeIntervalSinceDate(queueArrayEnqueStart)), Deque: \(queueArrayDequeEnd.timeIntervalSinceDate(queueArrayEnqueEnd))")
Queue with Array
struct Queue<T> {
private var list = [T]()
var isEmpty: Bool { return self.list.isEmpty }
var front: T? { return self.list.first }
mutating func enqueue(_ item: T) {
self.list.append(item)
}
mutating func dequeue() -> T? {
guard self.isEmpty == false else { return nil }
return self.list.removeFirst()
}
}
Swift 4 simple Stack for any type; string, int, array, etc.
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
mutating func peek() -> Element {
return items.last!
}
mutating func pushFirst(_ item: Element) {
items.insert(item, at: 0)
}
}
example with strings:
let names = ["Bob", "Sam", "Sue", "Greg", "Brian", "Dave"]
//create stack of string type
var stackOfStrings = Stack<String>()
//add to bottom of stack
for stringName in names {
stackOfStrings.push(stringName)
}
//print and remove from stack
for stringName in names {
print(stringName)
stackOfStrings.pop(stringName)
}
//add to top of stack
for stringName in names {
stackOfStrings.pushFirst(stringName)
}
//look at item in stack without pop
for stringName in names {
//see what Top item is without remove
let whatIsTopItem = stackOfStrings.peek(stringName)
if whatIsTopItem == "Bob" {
print("Best friend Bob is in town!")
}
}
//stack size
let stackCount = stackOfStrings.items.count
more info here:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Generics.html

How to have stored properties in Swift, the same way I had on Objective-C?

I am switching an application from Objective-C to Swift, which I have a couple of categories with stored properties, for example:
#interface UIView (MyCategory)
- (void)alignToView:(UIView *)view
alignment:(UIViewRelativeAlignment)alignment;
- (UIView *)clone;
#property (strong) PFObject *xo;
#property (nonatomic) BOOL isAnimating;
#end
As Swift extensions don't accept stored properties like these, I don't know how to maintain the same structure as the Objc code. Stored properties are really important for my app and I believe Apple must have created some solution for doing it in Swift.
As said by jou, what I was looking for was actually using associated objects, so I did (in another context):
import Foundation
import QuartzCore
import ObjectiveC
extension CALayer {
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, "shapeLayer") as? CAShapeLayer
}
set(newValue) {
objc_setAssociatedObject(self, "shapeLayer", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
var initialPath: CGPathRef! {
get {
return objc_getAssociatedObject(self, "initialPath") as CGPathRef
}
set {
objc_setAssociatedObject(self, "initialPath", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
}
But I get an EXC_BAD_ACCESS when doing:
class UIBubble : UIView {
required init(coder aDecoder: NSCoder) {
...
self.layer.shapeLayer = CAShapeLayer()
...
}
}
Any ideas?
As in Objective-C, you can't add stored property to existing classes. If you're extending an Objective-C class (UIView is definitely one), you can still use Associated Objects to emulate stored properties:
for Swift 1
import ObjectiveC
private var xoAssociationKey: UInt8 = 0
extension UIView {
var xo: PFObject! {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
}
set(newValue) {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN))
}
}
}
The association key is a pointer that should be the unique for each association. For that, we create a private global variable and use it's memory address as the key with the & operator. See the Using Swift with Cocoa and Objective-C
on more details how pointers are handled in Swift.
UPDATED for Swift 2 and 3
import ObjectiveC
private var xoAssociationKey: UInt8 = 0
extension UIView {
var xo: PFObject! {
get {
return objc_getAssociatedObject(self, &xoAssociationKey) as? PFObject
}
set(newValue) {
objc_setAssociatedObject(self, &xoAssociationKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
}
}
}
UPDATED for Swift 4
In Swift 4, it's much more simple. The Holder struct will contain the private value that our computed property will expose to the world, giving the illusion of a stored property behaviour instead.
Source
extension UIViewController {
struct Holder {
static var _myComputedProperty:Bool = false
}
var myComputedProperty:Bool {
get {
return Holder._myComputedProperty
}
set(newValue) {
Holder._myComputedProperty = newValue
}
}
}
Associated objects API is a bit cumbersome to use. You can remove most of the boilerplate with a helper class.
public final class ObjectAssociation<T: AnyObject> {
private let policy: objc_AssociationPolicy
/// - Parameter policy: An association policy that will be used when linking objects.
public init(policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
self.policy = policy
}
/// Accesses associated object.
/// - Parameter index: An object whose associated object is to be accessed.
public subscript(index: AnyObject) -> T? {
get { return objc_getAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque()) as! T? }
set { objc_setAssociatedObject(index, Unmanaged.passUnretained(self).toOpaque(), newValue, policy) }
}
}
Provided that you can "add" a property to objective-c class in a more readable manner:
extension SomeType {
private static let association = ObjectAssociation<NSObject>()
var simulatedProperty: NSObject? {
get { return SomeType.association[self] }
set { SomeType.association[self] = newValue }
}
}
As for the solution:
extension CALayer {
private static let initialPathAssociation = ObjectAssociation<CGPath>()
private static let shapeLayerAssociation = ObjectAssociation<CAShapeLayer>()
var initialPath: CGPath! {
get { return CALayer.initialPathAssociation[self] }
set { CALayer.initialPathAssociation[self] = newValue }
}
var shapeLayer: CAShapeLayer? {
get { return CALayer.shapeLayerAssociation[self] }
set { CALayer.shapeLayerAssociation[self] = newValue }
}
}
So I think I found a method that works cleaner than the ones above because it doesn't require any global variables. I got it from here:
http://nshipster.com/swift-objc-runtime/
The gist is that you use a struct like so:
extension UIViewController {
private struct AssociatedKeys {
static var DescriptiveName = "nsh_DescriptiveName"
}
var descriptiveName: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DescriptiveName) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.DescriptiveName,
newValue as NSString?,
UInt(OBJC_ASSOCIATION_RETAIN_NONATOMIC)
)
}
}
}
}
UPDATE for Swift 2
private struct AssociatedKeys {
static var displayed = "displayed"
}
//this lets us check to see if the item is supposed to be displayed or not
var displayed : Bool {
get {
guard let number = objc_getAssociatedObject(self, &AssociatedKeys.displayed) as? NSNumber else {
return true
}
return number.boolValue
}
set(value) {
objc_setAssociatedObject(self,&AssociatedKeys.displayed,NSNumber(bool: value),objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
The solution pointed out by jou doesn't support value types,
this works fine with them as well
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)
}
}
}
}
This is really such a great solution, I wanted to add another usage example that included structs and values that are not optionals. Also, the AssociatedKey values can be simplified.
struct Crate {
var name: String
}
class Box {
var name: String
init(name: String) {
self.name = name
}
}
extension UIViewController {
private struct AssociatedKey {
static var displayed: UInt8 = 0
static var box: UInt8 = 0
static var crate: UInt8 = 0
}
var displayed: Bool? {
get {
return getAssociatedObject(self, associativeKey: &AssociatedKey.displayed)
}
set {
if let value = newValue {
setAssociatedObject(self, value: value, associativeKey: &AssociatedKey.displayed, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
var box: Box {
get {
if let result:Box = getAssociatedObject(self, associativeKey: &AssociatedKey.box) {
return result
} else {
let result = Box(name: "")
self.box = result
return result
}
}
set {
setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.box, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
var crate: Crate {
get {
if let result:Crate = getAssociatedObject(self, associativeKey: &AssociatedKey.crate) {
return result
} else {
let result = Crate(name: "")
self.crate = result
return result
}
}
set {
setAssociatedObject(self, value: newValue, associativeKey: &AssociatedKey.crate, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
You can't define categories (Swift extensions) with new storage; any additional properties must be computed rather than stored. The syntax works for Objective C because #property in a category essentially means "I'll provide the getter and setter". In Swift, you'll need to define these yourself to get a computed property; something like:
extension String {
public var Foo : String {
get
{
return "Foo"
}
set
{
// What do you want to do here?
}
}
}
Should work fine. Remember, you can't store new values in the setter, only work with the existing available class state.
My $0.02. This code is written in Swift 2.0
extension CALayer {
private struct AssociatedKeys {
static var shapeLayer:CAShapeLayer?
}
var shapeLayer: CAShapeLayer? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.shapeLayer) as? CAShapeLayer
}
set {
if let newValue = newValue {
objc_setAssociatedObject(self, &AssociatedKeys.shapeLayer, newValue as CAShapeLayer?, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}
}
I have tried many solutions, and found this is the only way to actually extend a class with extra variable parameters.
Why relying on objc runtime? I don't get the point. By using something like the following you will achieve almost the identical behaviour of a stored property, by using only a pure Swift approach:
extension UIViewController {
private static var _myComputedProperty = [String:Bool]()
var myComputedProperty:Bool {
get {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
return UIViewController._myComputedProperty[tmpAddress] ?? false
}
set(newValue) {
let tmpAddress = String(format: "%p", unsafeBitCast(self, to: Int.self))
UIViewController._myComputedProperty[tmpAddress] = newValue
}
}
}
I prefer doing code in pure Swift and not rely on Objective-C heritage. Because of this I wrote pure Swift solution with two advantages and two disadvantages.
Advantages:
Pure Swift code
Works on classes and completions or more specifically on Any object
Disadvantages:
Code should call method willDeinit() to release objects linked to specific class instance to avoid memory leaks
You cannot make extension directly to UIView for this exact example because var frame is extension to UIView, not part of class.
EDIT:
import UIKit
var extensionPropertyStorage: [NSObject: [String: Any]] = [:]
var didSetFrame_ = "didSetFrame"
extension UILabel {
override public var frame: CGRect {
get {
return didSetFrame ?? CGRectNull
}
set {
didSetFrame = newValue
}
}
var didSetFrame: CGRect? {
get {
return extensionPropertyStorage[self]?[didSetFrame_] as? CGRect
}
set {
var selfDictionary = extensionPropertyStorage[self] ?? [String: Any]()
selfDictionary[didSetFrame_] = newValue
extensionPropertyStorage[self] = selfDictionary
}
}
func willDeinit() {
extensionPropertyStorage[self] = nil
}
}
With Obj-c Categories you can only add methods, not instance variables.
In you example you have used #property as a shortcut to adding getter and setter method declarations. You still need to implement those methods.
Similarly in Swift you can add use extensions to add instance methods, computed properties etc. but not stored properties.
Notice: after further analyzing, the code below works fine, but does not release the view object, so if I can find a way around it I'll edit the answer. meanwhile, read the comments.
How about storing static map to class that is extending like this :
extension UIView {
struct Holder {
static var _padding:[UIView:UIEdgeInsets] = [:]
}
var padding : UIEdgeInsets {
get{ return UIView.Holder._padding[self] ?? .zero}
set { UIView.Holder._padding[self] = newValue }
}
}
I also get an EXC_BAD_ACCESS problem.The value in objc_getAssociatedObject() and objc_setAssociatedObject() should be an Object. And the objc_AssociationPolicy should match the Object.
I tried using objc_setAssociatedObject as mentioned in a few of the answers here, but after failing with it a few times I stepped back and realized there is no reason I need that. Borrowing from a few of the ideas here, I came up with this code which simply stores an array of whatever my extra data is (MyClass in this example) indexed by the object I want to associate it with:
class MyClass {
var a = 1
init(a: Int)
{
self.a = a
}
}
extension UIView
{
static var extraData = [UIView: MyClass]()
var myClassData: MyClass? {
get {
return UIView.extraData[self]
}
set(value) {
UIView.extraData[self] = value
}
}
}
// Test Code: (Ran in a Swift Playground)
var view1 = UIView()
var view2 = UIView()
view1.myClassData = MyClass(a: 1)
view2.myClassData = MyClass(a: 2)
print(view1.myClassData?.a)
print(view2.myClassData?.a)
Here is simplified and more expressive solution. It works for both value and reference types. The approach of lifting is taken from #HepaKKes answer.
Association code:
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 associated<T>(to base: AnyObject,
key: UnsafePointer<UInt8>,
policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN,
initialiser: () -> T) -> T {
if let v = objc_getAssociatedObject(base, key) as? T {
return v
}
if let v = objc_getAssociatedObject(base, key) as? Lifted<T> {
return v.value
}
let lifted = Lifted(initialiser())
objc_setAssociatedObject(base, key, lifted, policy)
return lifted.value
}
func associate<T>(to base: AnyObject, key: UnsafePointer<UInt8>, value: T, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN) {
if let v: AnyObject = value as AnyObject? {
objc_setAssociatedObject(base, key, v, policy)
}
else {
objc_setAssociatedObject(base, key, lift(value), policy)
}
}
Example of usage:
1) Create extension and associate properties to it. Let's use both value and reference type properties.
extension UIButton {
struct Keys {
static fileprivate var color: UInt8 = 0
static fileprivate var index: UInt8 = 0
}
var color: UIColor {
get {
return associated(to: self, key: &Keys.color) { .green }
}
set {
associate(to: self, key: &Keys.color, value: newValue)
}
}
var index: Int {
get {
return associated(to: self, key: &Keys.index) { -1 }
}
set {
associate(to: self, key: &Keys.index, value: newValue)
}
}
}
2) Now you can use just as regular properties:
let button = UIButton()
print(button.color) // UIExtendedSRGBColorSpace 0 1 0 1 == green
button.color = .black
print(button.color) // UIExtendedGrayColorSpace 0 1 == black
print(button.index) // -1
button.index = 3
print(button.index) // 3
More details:
Lifting is needed for wrapping value types.
Default associated object behavior is retain. If you want to learn more about associated objects, I'd recommend checking this article.
if you are looking to set a custom string attribute to a UIView, this is how I did it on Swift 4
Create a UIView extension
extension UIView {
func setStringValue(value: String, key: String) {
layer.setValue(value, forKey: key)
}
func stringValueFor(key: String) -> String? {
return layer.value(forKey: key) as? String
}
}
To use this extension
let key = "COLOR"
let redView = UIView()
// To set
redView.setStringAttribute(value: "Red", key: key)
// To read
print(redView.stringValueFor(key: key)) // Optional("Red")
In PURE SWIFT with WEAK reference handling
import Foundation
import UIKit
extension CustomView {
// can make private
static let storedProperties = WeakDictionary<UIView, Properties>()
struct Properties {
var url: String = ""
var status = false
var desc: String { "url: \(url), status: \(status)" }
}
var properties: Properties {
get {
return CustomView.storedProperties.get(forKey: self) ?? Properties()
}
set {
CustomView.storedProperties.set(forKey: self, object: newValue)
}
}
}
var view: CustomView? = CustomView()
print("1 print", view?.properties.desc ?? "nil")
view?.properties.url = "abc"
view?.properties.status = true
print("2 print", view?.properties.desc ?? "nil")
view = nil
WeakDictionary.swift
import Foundation
private class WeakHolder<T: AnyObject>: Hashable {
weak var object: T?
let hash: Int
init(object: T) {
self.object = object
hash = ObjectIdentifier(object).hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(hash)
}
static func ==(lhs: WeakHolder, rhs: WeakHolder) -> Bool {
return lhs.hash == rhs.hash
}
}
class WeakDictionary<T1: AnyObject, T2> {
private var dictionary = [WeakHolder<T1>: T2]()
func set(forKey: T1, object: T2?) {
dictionary[WeakHolder(object: forKey)] = object
}
func get(forKey: T1) -> T2? {
let obj = dictionary[WeakHolder(object: forKey)]
return obj
}
func forEach(_ handler: ((key: T1, value: T2)) -> Void) {
dictionary.forEach {
if let object = $0.key.object, let value = dictionary[$0.key] {
handler((object, value))
}
}
}
func clean() {
var removeList = [WeakHolder<T1>]()
dictionary.forEach {
if $0.key.object == nil {
removeList.append($0.key)
}
}
removeList.forEach {
dictionary[$0] = nil
}
}
}
Another example with using Objective-C associated objects and computed properties for Swift 3 and Swift 4
import CoreLocation
extension CLLocation {
private struct AssociatedKeys {
static var originAddress = "originAddress"
static var destinationAddress = "destinationAddress"
}
var originAddress: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.originAddress) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.originAddress,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
var destinationAddress: String? {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.destinationAddress) as? String
}
set {
if let newValue = newValue {
objc_setAssociatedObject(
self,
&AssociatedKeys.destinationAddress,
newValue as NSString?,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
}
}
First, Associated Objects should be the best right solution for the extended stored properties, because it comes from the Objective-C runtime, this is a great powerful feature that we should use before there are other native features of Swift language.
You should always aware that the associated objects will be released after there are no other objects to retain them, including swift objects, so don't use custom containers to retain the target values which won't be released automatically.
Second, for those additional associated key structure definitions, the core functions just need a UnsafeRawPointer for that, actually there is another best choice for that, #function is a static string which generated when compiling the source code, it also has its own address to use.
So, here is it:
var status: Bool? {
get { objc_getAssociatedObject(self, #function) as? Bool }
set { objc_setAssociatedObject(self, #function, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)}
}
Build for swift 5.
Last, keep in mind the object type with the association policy.
I tried to store properties by using objc_getAssociatedObject, objc_setAssociatedObject, without any luck. My goal was create extension for UITextField, to validate text input characters length.
Following code works fine for me. Hope this will help someone.
private var _min: Int?
private var _max: Int?
extension UITextField {
#IBInspectable var minLength: Int {
get {
return _min ?? 0
}
set {
_min = newValue
}
}
#IBInspectable var maxLength: Int {
get {
return _max ?? 1000
}
set {
_max = newValue
}
}
func validation() -> (valid: Bool, error: String) {
var valid: Bool = true
var error: String = ""
guard let text = self.text else { return (true, "") }
if text.characters.count < minLength {
valid = false
error = "Textfield should contain at least \(minLength) characters"
}
if text.characters.count > maxLength {
valid = false
error = "Textfield should not contain more then \(maxLength) characters"
}
if (text.characters.count < minLength) && (text.characters.count > maxLength) {
valid = false
error = "Textfield should contain at least \(minLength) characters\n"
error = "Textfield should not contain more then \(maxLength) characters"
}
return (valid, error)
}
}
Why not just do something like this, i see other solutions are way out of the small need.
private var optionalID: String {
UUID().uuidString
}
Here is an alternative that works also
public final class Storage : AnyObject {
var object:Any?
public init(_ object:Any) {
self.object = object
}
}
extension Date {
private static let associationMap = NSMapTable<NSString, AnyObject>()
private struct Keys {
static var Locale:NSString = "locale"
}
public var locale:Locale? {
get {
if let storage = Date.associationMap.object(forKey: Keys.Locale) {
return (storage as! Storage).object as? Locale
}
return nil
}
set {
if newValue != nil {
Date.associationMap.setObject(Storage(newValue), forKey: Keys.Locale)
}
}
}
}
var date = Date()
date.locale = Locale(identifier: "pt_BR")
print( date.locale )
I found this solution more practical
UPDATED for Swift 3
extension UIColor {
static let graySpace = UIColor.init(red: 50/255, green: 50/255, blue: 50/255, alpha: 1.0)
static let redBlood = UIColor.init(red: 102/255, green: 0/255, blue: 0/255, alpha: 1.0)
static let redOrange = UIColor.init(red: 204/255, green: 17/255, blue: 0/255, alpha: 1.0)
func alpha(value : CGFloat) -> UIColor {
var r = CGFloat(0), g = CGFloat(0), b = CGFloat(0), a = CGFloat(0)
self.getRed(&r, green: &g, blue: &b, alpha: &a)
return UIColor(red: r, green: g, blue: b, alpha: value)
}
}
...then in your code
class gameController: UIViewController {
#IBOutlet var game: gameClass!
override func viewDidLoad() {
self.view.backgroundColor = UIColor.graySpace
}
}

Resources