Creating singleton in Swift - ios

I have successfully been able to create a singleton object in Swift, but I feel that the implementation is rather verbose. Is there a way to shorten this code up? And combine multiple formatters into one class where each formatter is its own singleton?
import Foundation
class sharedNumberFormatterWithOneDecimalPlace : NSNumberFormatter {
class var sharedInstance: sharedNumberFormatterWithOneDecimalPlace {
struct Singleton {
static let instance = sharedNumberFormatterWithOneDecimalPlace()
}
return Singleton.instance
}
override init () {
super.init()
self.minimumIntegerDigits = 1
self.maximumFractionDigits = 1
self.minimumFractionDigits = 1
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
In my other class I can call it by:
NSNumberFormatter *formatter = sharedNumberFormatterWithOneDecimalPlace.sharedInstance;
NSLog(#"%#", [formatter stringFromNumber:aNumber]);
I would like to be able to have 1 class of "MultipleFormatters" where I set up many formatters that get used all over the place, and then call something like "MultipleFormatters.OneDecimalPlace" for example.
PS. I have already read post: Using a dispatch_once singleton model in Swift
Thanks.

Best way in my opinion is :
private let _MultipleFormattersSharedInstance = MultipleFormatters()
class MultipleFormatters {
class var sharedInstance: MultipleFormatters {
return _MultipleFormattersSharedInstance
}
}
and you can use
MultipleFormatters.sharedInstance
Then you can create all the "formatters" that you need as methods from your class MultipleFormatters and call them all over the place.
Thanks to that thing in swift
Edit for example:
Let's say you want to have a formatter called "increment". I don't know what you mean by formatters but here's a stupid example.
private let _MultipleFormattersSharedInstance = MultipleFormatters()
class MultipleFormatters {
class var sharedInstance: MultipleFormatters {
return _MultipleFormattersSharedInstance
}
func increment(number: Int, amount: Int) -> Int{
//Maybe you need something that was initialized with
//the singleton here.
return number + amount
}
}
and you would use
let singleton : MultipleFormatters = MultipleFormatters.sharedInstance
let result = singleton.increment(1, amount: 25)

import Cocoa
import Foundation
class MultipleFormatters : NSNumberFormatter {
class var oneDecimalPlace: MultipleFormatters {
struct Singleton0 {
static let instance = MultipleFormatters(numberOfDigits: 1)
}
return Singleton0.instance
}
class var twoDecimalPlace: MultipleFormatters {
struct Singleton {
static let instance = MultipleFormatters(numberOfDigits: 2)
}
return Singleton.instance
}
convenience init(numberOfDigits:Int){
self.init()
self.maximumFractionDigits = numberOfDigits
}
override init () {
super.init()
self.minimumIntegerDigits = 1
self.maximumFractionDigits = 1
self.minimumFractionDigits = 1
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
var res = MultipleFormatters.oneDecimalPlace.stringFromNumber(1.234)
var asdf = MultipleFormatters.twoDecimalPlace.stringFromNumber(1.234)

My coworker helped out on this, this is what we think is the best way to create some formatters from one class and also be able to set options for different number formatter styles.
private let _oneDecimalPlaceInstance = MultipleFormatters(minFracDigits: 1, maxFracDigits: 1)
private let _twoDecimalPlaceInstance = MultipleFormatters(minFracDigits: 2, maxFracDigits: 2)
class MultipleFormatters : NSNumberFormatter {
class var oneDecimalPlace: MultipleFormatters {
return _oneDecimalPlaceInstance
}
class var twoDecimalPlace: MultipleFormatters {
return _twoDecimalPlaceInstance
}
convenience init(minFracDigits:Int, maxFracDigits:Int) {
self.init()
self.minimumIntegerDigits = 1
self.minimumFractionDigits = minFracDigits
self.maximumFractionDigits = maxFracDigits
}
}

class TestClass{
private static var instance = TestClass()
public static func shareInstance() ->TestClass{
return TestClass.instance
}
}
let a1 = TestClass.shareInstance()
let a2 = TestClass.shareInstance()
let a3 = TestClass()
print(a1===a2)
print(a1===a3)

Related

Attempting to override supportssecurecoding but doesn't compile

I'm converting older code to Swift 4 which uses SecureCoding. Apple documentation says this is a requirement however my code doesn't compile. I've tried changing the declarations between static, func, var, and class however none of these do the trick to convert to Swift 4.
Here is the code where I'm attempting to make the override:
override public class func supportsSecureCoding() -> Bool {
return true
}
The error is shown saying method does not override any method from its superclass. Reference Doc
Here is the full updated code which includes the solution:
import CareKit
public class ZCCareMonthlySchedule : OCKCareSchedule {
var calendar: NSCalendar?
public class func monthlyScheduleWithStartDate(startDate: NSDateComponents, occurrencesFromJanuaryToDecember: [NSNumber], monthsToSkip: UInt, endDate: NSDateComponents?) -> ZCCareMonthlySchedule? {
guard occurrencesFromJanuaryToDecember.count == 12
else { return nil}
//TODO: Requires fixing after CareKit is updated to handle sub classes
// let schedule = super.initWithStartDate(startDate: startDate, endDate: endDate, occurrences: occurrencesFromJanuaryToDecember, timeUnitsToSkip: monthsToSkip)
return nil
}
override public var type: OCKCareScheduleType {
return OCKCareScheduleType.other
}
override public func numberOfEvents(onDate date: DateComponents) -> UInt {
calendar = NSCalendar.init(calendarIdentifier: NSCalendar.Identifier.gregorian)
calendar!.timeZone = NSTimeZone(abbreviation: "UTC")! as TimeZone
let startMonth = calendar?.ordinality(of: NSCalendar.Unit.month, in: NSCalendar.Unit.era, for: self.startDate.date! )
let endMonth = calendar?.ordinality(of: NSCalendar.Unit.month, in: NSCalendar.Unit.era, for: date.date! )
let monthsSinceStart = startMonth! - endMonth!
let month = calendar?.component(NSCalendar.Unit.month, from: date.date!)
//TODO: Add a unit test to verify this works
let occurrences : UInt = ((UInt(monthsSinceStart) % (self.timeUnitsToSkip + 1)) == 0) ? self.occurrences[month!-1].uintValue : 0;
return occurrences;
}
//MARK: NSSecureCoding Support
override public static var supportsSecureCoding: Bool{
return true
}
required convenience public init?(coder aDecoder: NSCoder) {
self.init(coder: aDecoder)
}
//MARK: NSCopying Support
override public func copy(with zone: NSZone?) -> Any {
let theCopy = super.copy(with: zone) as! ZCCareMonthlySchedule
return theCopy
}
}
Thanks to #Paulw11, the answer was to place this into a variable as a static var such as the following:
override public static var supportsSecureCoding: Bool{
return true
}

DidSet not working in init function swift 3

I have already seen
Is it possible to allow didSet to be called during initialization in Swift?
for me it is not working..
I am working in project where I have created class below
protocol FileManagerHelper {
var fileName:String {get}
var fileCategory:FileCategory {get set}
var isFileExitsAtPath:Bool {get}
var filePath:String {get}
var fileType:FileTypes {get set}
}
class FileManager:FileManagerHelper {
// Other property
//STORED PROPERY INIT WHEN OBJECT WILL CREATED WITH FileCategory OBJECT
var fileCategory:FileCategory {
didSet {
switch fileCategory {
case .XYZ:
print("Test")
... other cases
}
}
required init(fileCategory:FileCategory,fileType:FileTypes = .Image) {
self.fileCategory = fileCategory
self.path = self.folderPath + self.fileName
}
}
did set method is not calling of fileCategory
NOTE: I don't want to give default value , I want to pass it runtime from init method
Tries
1) defer
use of self in method called $defer before all stored property are initialised
2) Create custom method that will assign that value and call it from init
private func setCategory(with category:FileCategory) {
self.fileCategory = category
}
Use of method call setCategory before stored property ...
I know that all stored property should be initialised before instance created. Till that instance will not been created so i won't call methods (using self) may be that why above solution not working
Please help me if any one have idea
For me, using the defer is better readable.
import Foundation
class A {
var b: String {
didSet {
print("didSet called with value: \(b)")
}
}
init(x: String) {
self.b = x
defer { self.b = x }
}
}
let a = A(x: "It's Working!") // didSet called with value: It's Working!
print(a.b) // It's Working
One way to solve this is to extract the didSet logic into a separate method and call this method from both didSet and init:
class FileManager: FileManagerHelper {
var fileCategory:FileCategory {
didSet {
didSetFileCategory()
}
}
required init(fileCategory:FileCategory,fileType:FileTypes = .Image) {
self.fileCategory = fileCategory
self.path = self.folderPath + self.fileName
didSetFileCategory()
}
private func didSetFileCategory() {
switch fileCategory {
case .XYZ:
print("Test")
//... other cases
}
}
}

How to access elements from an NSObject in swift?

I've set some variables as an Object
import UIKit
class SpeedTestResult: NSObject {
var testTime: NSDate?
}
Then in the controller I set this object and pass it to a class to store it:
testResult.testTime = NSDate()
SpeedTestManager().addTestResult(testResult)
I need to store this object and then access the elements within in a view later, This is what I have:
import Foundation
class SpeedTestManager : NSObject {
var testResultArray = [NSObject]()
func addTestResult(testResult: NSObject) {
testResultArray.append(testResult)
print("Printing testResultArray: \(testResultArray)")
}
}
But when I try to print the the object I just get
Printing testResultArray: [<ProjectName.SpeedTestResult: 0x127b85e50>]
How do I access elements within the object and store this object and retrieve it for later use in a view?
class TestResult : NSObject, NSSecureCoding {
var testTime: NSDate?
override init() {
super.init()
}
#objc required init?(coder decoder: NSCoder) {
self.testTime = decoder.decodeObjectForKey("testTime") as? NSDate
}
#objc func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.testTime, forKey: "testTime")
}
#objc static func supportsSecureCoding() -> Bool {
return true
}
override var description: String {
return String.init(format: "TestResult: %#", self.testTime ?? "null")
}
}
class SpeedTestManager : NSObject, NSSecureCoding {
var testResultArray = [NSObject]()
func addTestResult(testResult: NSObject) {
testResultArray.append(testResult)
print("Printing testResultArray: \(testResultArray)")
}
override init() {
super.init()
}
#objc func encodeWithCoder(encoder: NSCoder) {
encoder.encodeObject(self.testResultArray, forKey: "testResultArray")
}
#objc required init?(coder decoder: NSCoder) {
self.testResultArray = decoder.decodeObjectForKey("testResultArray") as! [NSObject]
}
#objc static func supportsSecureCoding() -> Bool {
return true
}
override var description: String {
return String.init(format: "SpeedManager: [%#]", self.testResultArray.map({"\($0)"}).joinWithSeparator(","))
}
}
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let testResult = TestResult()
testResult.testTime = NSDate()
let speedManager = SpeedTestManager()
speedManager.addTestResult(testResult)
NSUserDefaults.standardUserDefaults().setObject(NSKeyedArchiver.archivedDataWithRootObject(speedManager), forKey: "speedManager")
NSUserDefaults.standardUserDefaults().synchronize()
if let archivedSpeedManager = NSUserDefaults.standardUserDefaults().objectForKey("speedManager") as? NSData {
let unarchivedSpeedManager = NSKeyedUnarchiver.unarchiveObjectWithData(archivedSpeedManager)
print("SpeedManager: \(unarchivedSpeedManager ?? "null")")
}
else {
print("Failed to unarchive speed manager")
}
}
}
Here is one way you can do it:
import Foundation
class SpeedTestResult: NSObject {
var testTime: NSDate?
}
class SpeedTestManager : NSObject {
var testResultArray = [NSObject]()
func addTestResult(testResult: NSObject) {
testResultArray.append(testResult)
for result in testResultArray {
// This will crash if result is not a SpeedTestResult.
// print((result as! SpeedTestResult).testTime)
// This is better:
if let timeResult = result as? SpeedTestResult
{
print(timeResult.testTime)
}
else
{
print("Not time type...")
}
}
}
}
var testResult = SpeedTestResult()
testResult.testTime = NSDate()
SpeedTestManager().addTestResult(testResult)
This addresses your specific question, but there are some other problems here:
If you are going to store only SpeedTestResult instances in
testResultArray, then why not make it of type
[SpeedTestResutl]()?
If you will store different types of tests in the array, then how do
you find out which type of test an NSObject element represents?
There are ways... In the above code we at least make sure we are not treating a wrong type of object as a SpeedTestResult.
When you do SpeedTestManager().addTestResult(testResult), you don't
keep a reference to the SpeedTestManager instance. The next time
you make the same call, you will be creating a different
SpeedTestManager instance.
This is not really a problem, but SpeedTestManager does not have to
be a sub-class of NSObject, unless you want to use it in
Objective-C.
You probably don't want to print the content of testResultArray in
the addTestResult() method. You could have other methods for
accessing the array.
To add your test results to the same test manager, you could do:
let myTestManager = SpeedTestManager()
myTestManager.addTestResult(testResult)
// create other test results and add them ...

save and get array of custom object in user default [duplicate]

I try to store Array of objects in NSUserDefaults.
I have following snippets of code:
var accounts = MyAccounts()
var array:Array<MyAccounts.MyCalendar> = accounts.populateFromCalendars()
NSUserDefaults.standardUserDefaults().
setObject(array, forKey: "test_storeAccounts_array") // <- get error here
NSUserDefaults.standardUserDefaults().synchronize()
But I get Exception:
does not implement methodSignatureForSelector: -- trouble ahead
my class structure:
class MyAccounts {
/* ... */
class MyCalendar {
var title:String?
var identifier:String?
var email:String?
var calType:String?
var isActive:Bool?
var isMainAcount:Bool?
init(){}
}
}
Any ideas?
Make sure your class inherits from NSObject
class MyAccounts:NSObject {
/* ... */
class MyCalendar {
var title:String?
var identifier:String?
var email:String?
var calType:String?
var isActive:Bool?
var isMainAcount:Bool?
init(){}
}
}
I was getting this exception in Swift 3.0. In my case, my model class was not inherit from NSObject base class. just inherit my class from NSObject base class and implements NSCoding protocol (if your container array has custom objects)
class Stock: NSObject, NSCoding {
var stockName: String?
override init() {
}
//MARK: NSCoding protocol methods
func encode(with aCoder: NSCoder){
aCoder.encode(self.stockName, forKey: "name")
}
required init(coder decoder: NSCoder) {
if let name = decoder.decodeObject(forKey: "name") as? String{
self.stockName = name
}
}
func getStockDataFromDict(stockDict stockDict:[String:AnyObject]) -> Stock {
if let stockName = stockDict["name"] {
self.stockName = stockName as? String
}
return self
}
}
In Swift 2, I experienced similar error while using the Notification Pattern within a custom class. Note that when the same notification(Observe) is implemented in a ViewController class , it doesn't complain. Its only with the custom class, created from a Swift file without subclassing this error was thrown
class myClass : NSObject {
override init(){
super.init()
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("functionCall:"), name: "NotificationName", object: nil)
}
//Implement function
func functionCall(notification: NSNotification) {
//Extract the object and implement the function
}
}
You need to convert the class into NSData first. Something like this:
var data = NSKeyedArchiver.archivedDataWithRootObject(accounts.populateFromCalendars())
var userDefaults = NSUserDefaults.standardUserDefaults();
userDefaults.setObject(data, forKey: "test_storeAccounts_array");

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