I am new to Swift and maybe it's a stupid question, but I can't find an answer to it.
I have created an extension:
extension UITextField {
var placeholderLabel: UILabel {
get {
return self.placeholderLabel
}
set {
self.placeholderLabel = newValue
}
}
}
When the property is set, the application crashes.
You can't have a stored property in extension.
Extensions are not allowed to add a property to existing class because adding a property structure of the class will change. And because Objective C, Swift or any other programming language that am aware of could not afford it, it won't allow you to add the stored property to extension.
Isn't there any work around then ??
This is what you can do to save the label as stored property in your extension :)
import Foundation
import UIKit
fileprivate var ascociatedObjectPointer : UInt8 = 99
extension UITextField {
var myLabel : UILabel {
get {
return objc_getAssociatedObject(self, &ascociatedObjectPointer) as! UILabel
}
set {
objc_setAssociatedObject(self, &ascociatedObjectPointer, myLabel, .OBJC_ASSOCIATION_RETAIN)
}
}
}
How it works ??
Simple by writing setter and getter for the variable which you are posing or pretending to be stored property and by internally holding a pointer which has nothing to do with the existing class, hence it won't affect the structure of existing class.
Hope it helps.
You can use NSMapTable like this:
extension UITextField {
private static var placeholderLabelMap: NSMapTable<UITextField, UILabel> = .weakToStrongObjects()
var placeholderLabel: UILabel? {
get {
return UITextField.placeholderLabelMap.object(forKey: self)
}
set {
UITextField.placeholderLabelMap.setObject(newValue, forKey: self)
}
}
}
The advantage of Sandeep's answer might be thread safety. You can see this Stack Overflow topic for comparison between the approaches.
Related
In my app I need to add to a bunch of UIKit objects some properties; my original thought was to create a subclass of each element I needed and create the properties inside the new class but I realized that this means to write a new class for each UI element type I'm using.
In my specific case, I wanna add to some different views, such as UIImageView and UILabel, two properties called initial position and final position of type CGRect to store the initial and final position in order to use it inside a method which translates this views.
Is there any way to accomplish this without creating lots of classes?
You can do the following:
protocol Position {
var initialPosition: Int { get set }
}
It is not possible to just declare properties in extensions, so you'd need to set your get and set. You can just associate a value:
private var initialPositionKey: UInt = 0
extension Position {
var initialPosition: Int {
get {
return objc_getAssociatedObject(self, &initialPositionKey) as! Int
}
set {
objc_setAssociatedObject(self, &initialPositionKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}
Then you extend your UIView like this:
extension UIView: Position {}
And the following works:
var view = UIView()
view.initialPosition = 5
print (view.initialPosition) // 5
I made a simple struct that handles the management of the UI background (the user can choose to use a gradient or image). Inside this struct is a computed property called preference which gets and sets the user's preference to UserDefaults.
When I try to set the preference property using the following code:
Background().preference = .gradient
I get an error: "Cannot assign to property: function call returns immutable value"
I have to use this instead:
var background = Background()
background.preference = .gradient
I would prefer not having to assign an instance of Background to a variable before finally setting the property.
I've found that changing Background from a struct to a class allows me to set the property using Background().preference = .gradient directly.
Can anyone give me some insight as to why this occurs? Is using class better than using struct for this situation or does it not matter?
struct Background {
enum Choice {
case gradient
case image
}
var preference: Choice {
get {
if let type = UserDefaults.standard.value(forKey: "background_type"), type as! String == "background" {
return .image
}
return .gradient
}
set(value){
if value == .image {
UserDefaults.standard.setValue("background", forKey: "background_type")
}else{
UserDefaults.standard.setValue("gradient", forKey: "background_type")
}
}
}
You're not really getting any value from making an instance of a struct / class to just wrap UserDefaults. It's a very common problem and there are lots of clever solutions out there on google if you search around. For a really simple example you could just extend UserDefaults
//: Playground - noun: a place where people can play
import Cocoa
enum BackgroundChoice {
case gradient
case image
}
extension UserDefaults {
var backgroundChoice: BackgroundChoice {
get {
if let type = string(forKey: "background_type"), type == "image" {
return .image
}
return .gradient
}
set(value){
if value == .image {
setValue("background", forKey: "background_type")
}else{
setValue("gradient", forKey: "background_type")
}
}
}
}
UserDefaults.standard.backgroundChoice = .image
I know this doesn't answer your exact question, but I think you'll find there are better solutions to this problem if you dig around.
If I attempt to run the following code:
photographer = photographer
I get the error:
Assigning a property to itself.
I want to assign the property to itself to force the photographer didSet block to run.
Here's a real-life example: In the "16. Segues and Text Fields" lecture of the Winter 2013 Stanford iOS course (13:20), the professor recommends writing code similar to the following:
#IBOutlet weak var photographerLabel: UILabel!
var photographer: Photographer? {
didSet {
self.title = photographer.name
if isViewLoaded() { reload() }
}
}
override func viewDidLoad() {
super.viewDidLoad()
reload()
}
func reload() {
photographerLabel.text = photographer.name
}
Note: I made the following changes: (1) the code was switched from Objective-C to Swift; (2) because it's in Swift, I use the didSet block of the property instead of the setPhotographer: method; (3) instead of self.view.window I am using isViewLoaded because the former erroneously forces the view to load upon access of the view property; (4) the reload() method (only) updates a label for simplicity purposes, and because it resembles my code more closely; (5) the photographer IBOutlet label was added to support this simpler code; (6) since I'm using Swift, the isViewLoaded() check no longer exists simply for performance reasons, it is now required to prevent a crash, since the IBOutlet is defined as UILabel! and not UILabel? so attempting to access it before the view is loaded will crash the application; this wasn't mandatory in Objective-C since it uses the null object pattern.
The reason we call reload twice is because we don't know if the property will be set before or after the view is created. For example, the user might first set the property, then present the view controller, or they might present the view controller, and then update the property.
I like how this property is agnostic as to when the view is loaded (it's best not to make any assumptions about view loading time), so I want to use this same pattern (only slightly modified) in my own code:
#IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
photographerLabel?.text = photographer.name
}
}
override func viewDidLoad() {
super.viewDidLoad()
photographer = photographer
}
Here instead of creating a new method to be called from two places, I just want the code in the didSet block. I want viewDidLoad to force the didSet to be called, so I assign the property to itself. Swift doesn't allow me to do that, though. How can I force the didSet to be called?
Prior to Swift 3.1 you could assign the property name to itself with:
name = (name)
but this now gives the same error: "assigning a property to itself".
There are many other ways to work around this including introducing a temporary variable:
let temp = name
name = temp
This is just too fun not to be shared. I'm sure the community can come up with many more ways to do this, the crazier the better
class Test: NSObject {
var name: String? {
didSet {
print("It was set")
}
}
func testit() {
// name = (name) // No longer works with Swift 3.1 (bug SR-4464)
// (name) = name // No longer works with Swift 3.1
// (name) = (name) // No longer works with Swift 3.1
(name = name)
name = [name][0]
name = [name].last!
name = [name].first!
name = [1:name][1]!
name = name ?? nil
name = nil ?? name
name = name ?? name
name = {name}()
name = Optional(name)!
name = ImplicitlyUnwrappedOptional(name)
name = true ? name : name
name = false ? name : name
let temp = name; name = temp
name = name as Any as? String
name = (name,0).0
name = (0,name).1
setValue(name, forKey: "name") // requires class derive from NSObject
name = Unmanaged.passUnretained(self).takeUnretainedValue().name
name = unsafeBitCast(name, to: type(of: name))
name = unsafeDowncast(self, to: type(of: self)).name
perform(#selector(setter:name), with: name) // requires class derive from NSObject
name = (self as Test).name
unsafeBitCast(dlsym(dlopen("/usr/lib/libobjc.A.dylib",RTLD_NOW),"objc_msgSend"),to:(#convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
unsafeBitCast(class_getMethodImplementation(type(of: self), #selector(setter:name)), to:(#convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
unsafeBitCast(method(for: #selector(setter:name)),to:(#convention(c)(Any?,Selector,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
_ = UnsafeMutablePointer(&name)
_ = UnsafeMutableRawPointer(&name)
_ = UnsafeMutableBufferPointer(start: &name, count: 1)
withUnsafePointer(to: &name) { name = $0.pointee }
//Using NSInvocation, requires class derive from NSObject
let invocation : NSObject = unsafeBitCast(method_getImplementation(class_getClassMethod(NSClassFromString("NSInvocation"), NSSelectorFromString("invocationWithMethodSignature:"))),to:(#convention(c)(AnyClass?,Selector,Any?)->Any).self)(NSClassFromString("NSInvocation"),NSSelectorFromString("invocationWithMethodSignature:"),unsafeBitCast(method(for: NSSelectorFromString("methodSignatureForSelector:"))!,to:(#convention(c)(Any?,Selector,Selector)->Any).self)(self,NSSelectorFromString("methodSignatureForSelector:"),#selector(setter:name))) as! NSObject
unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setSelector:")),to:(#convention(c)(Any,Selector,Selector)->Void).self)(invocation,NSSelectorFromString("setSelector:"),#selector(setter:name))
var localVarName = name
withUnsafePointer(to: &localVarName) { unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setArgument:atIndex:")),to:(#convention(c)(Any,Selector,OpaquePointer,NSInteger)->Void).self)(invocation,NSSelectorFromString("setArgument:atIndex:"), OpaquePointer($0),2) }
invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self)
}
}
let test = Test()
test.testit()
There are some good workarounds but there is little point in doing that.
If a programmer (future maintainer of the code) sees code like this:
a = a
They will remove it.
Such a statement (or a workaround) should never appear in your code.
If your property looks like this:
var a: Int {
didSet {
// code
}
}
then it's a not a good idea to invoke the didSet handler by assignment a = a.
What if a future maintainer adds a performance improvement to the didSet like this?
var a: Int {
didSet {
guard a != oldValue else {
return
}
// code
}
}
The real solution is to refactor:
var a: Int {
didSet {
self.updateA()
}
}
fileprivate func updateA() {
// the original code
}
And instead of a = a directly call updateA().
If we are speaking about outlets, a suitable solution is to force the loading of views before assigning for the first time:
#IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
_ = self.view // or self.loadViewIfNeeded() on iOS >= 9
photographerLabel?.text = photographer.name // we can use ! here, it makes no difference
}
}
That will make the code in viewDidLoad unnecessary.
Now you might be asking "why should I load the view if I don't need it yet? I want only to store my variables here for future use". If that's what you are asking, it means you are using a view controller as your model class, just to store data. That's an architecture problem by itself. If you don't want to use a controller, don't even instantiate it. Use a model class to store your data.
I hope one day #Swift developers will fix this miscuzzi :)
Simple crutch:
func itself<T>(_ value: T) -> T {
return value
}
Use:
// refresh
style = itself(style)
image = itself(image)
text = itself(text)
(optionals including)
Make a function that the didSet calls then call that function when you want to update something? Seems like this would guard against developers going WTF? in future
#vacawama did a great job with all those options. However in iOS 10.3, Apple banned some of these ways and most likely will be doing it in the future again.
Note: To avoid the risk and future errors, I will use a temporary variable.
We can create a simple function for that:
func callSet<T>(_ object: inout T) {
let temporaryObject = object
object = temporaryObject
}
Would be used like: callSet(&foo)
Or even a unary operator, if there is a fitting one ...
prefix operator +=
prefix func +=<T>(_ object: inout T) {
let temporaryObject = object
object = temporaryObject
}
Would be used like: +=foo
I have a custom swift class like this
class NichedHelper: NSObject {
private var _theController:UIViewController? = nil
var theController:UIViewController? {
get {
return self._theController
}
set {
self._theController = newValue
}
}...
it has an implementation function like this and _theController passing a Lobb class that inherit UIViewController
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController!
abil.bottomConst.constant = -80
}
it throw error 'AnyObject' does not have a member named 'bottomConst'.
since i don't know what the english word for this kind of technique, so that will be my first question.
my second question, is it possible if i am sure Lobb class (or other class) have a variable called bottomConst, how can i access it from class NichedHelper?
you have declared the _theController as private , remove that just declare as
var _theController:UIViewController!
// this is how we roll in swift ;) bye bye Objective-C
I don't know exactly what you are trying to do and why you have two UIViewController instances. So I'm not able to answer your first question but regarding your second one, you have to cast the object to a UIViewController object:
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController as! UIViewController
abil.bottomConst.constant = -80
}
This at least should make the compiling error away, if you have the bottomConst attribute declared as a variable of UIViewControllers in an extension (since they do not have this variable normally.
Well, i change from passing the UIViewController to NSLayoutConstraint
I’m currently writing some Swift code in a project that is predominately Objective-C. In our ObjC code, we have a header that declares typedef GPUImageOutput<GPUImageInput> MyFilter;. We can then declare e.g. a #property that can only be a GPUImageOutput subclass that implements GPUImageInput.
(NOTE: GPUImageOutput and GPUImageInput are not defined by me; they are part of the GPUImage library)
Our Swift code doesn't seem to recognize this, even though the header is #imported in our Bridging Header. I’ve tried to replicate the declaration in Swift, but neither of these are proper syntax:
typealias MyFilter = GPUImageOutput, GPUImageInput
typealias MyFilter = GPUImageOutput : GPUImageInput
You can't declare typealias like that.
The best we can do is something like this:
class MyClass {
private var filter:GPUImageOutput
init<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func setFilter<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func someMethod() {
let output = self.filter
let input = self.filter as GPUImageInput
output.someOutputMethod()
input.someInputMethod()
}
}
In Swift 4 you can achieve this with the new & sign (Below an example of a parameter confirming to UIViewController and UITableViewDataSource:
func foo(vc: UIViewController & UITableViewDataSource) {
// access UIViewController property
let view = vc.view
// call UITableViewDataSource method
let sections = vc.numberOfSectionsInTableView?(tableView)
}
In Swift, something like the following should accomplish your task, but it's different than its ObjC counterpart:
typealias GPUImageOutput = UIImage
#objc protocol GPUImageInput {
func lotsOfInput()
}
class GPUImageOutputWithInput: GPUImageOutput, GPUImageInput
{
func lotsOfInput() {
println("lotsOfInput")
}
}
// ...
var someGpuImage = GPUImageOutput()
var specificGpuImage = GPUImageOutputWithInput()
for image in [someGpuImage, specificGpuImage] {
if let specificImage = image as? GPUImageInput {
specificImage.lotsOfInput()
} else {
println("the less specific type")
}
}
UPDATE: now that I understand where/why you have these types ...
GPUImage seems to have a swift example that does what you want, as Swift-ly as possible.
See here:
class FilterOperation<FilterClass: GPUImageOutput where FilterClass: GPUImageInput>: FilterOperationInterface {
...
The type constraint syntax can be applied to functions, too, and with a where clause, that's probably as good as you're going to get directly in Swift.
The more I tried to understand how to port this somewhat common objc trope, the more I realized it was the most Swift-way. Once I saw the example in GPUImage itself, I was convinced it was at least your answer. :-)
UPDATE 2: So, besides the specific GPUImage example I linked to above that uses Swift, the more and more I think about this, either using a where clause to guard the setter function, or using a computable property to filter the set functionality seems the only way to go.
I came up with this strategy:
import Foundation
#objc protocol SpecialProtocol {
func special()
}
class MyClass {}
class MyClassPlus: MyClass, SpecialProtocol {
func special() {
println("I'm special")
}
}
class MyContainer {
private var i: MyClass?
var test: MyClass? {
get {
return self.i
}
set (newValue) {
if newValue is SpecialProtocol {
self.i = newValue
}
}
}
}
var container = MyContainer()
println("should be nil: \(container.test)")
container.test = MyClass()
println("should still be nil: \(container.test)")
container.test = MyClassPlus()
println("should be set: \(container.test)")
(container.test as? MyClassPlus)?.special()
Outputs:
should be nil: nil
should still be nil: nil
should be set: Optional(main.MyClassPlus)
I'm special
(Optionally, you could also use precondition(newValue is SpecialProtocol, "newValue did not conform to SpecialProtocol") in place of the is check, but that will act like an assert() can crash the app if the case isn't met. Depends on your needs.)
#rintaro's answer is a good one, and is a good example of using a where clause as a guard (both nice functional-ly, and Swift-ly). However, I just hate to write a setFoo() function when computable properties exist. Then again, even using a computable property has code smell, since we can't seem to be able to apply a generic type-constraint to the set'er, and have to do the protocol conformance test in-line.
You can use typealias keyword. Here is how to do it:
typealias MyNewType = MyExistingType
It doesn't matter whether MyExistingType is protocol or function or enum. All it needs to be some type. And the best part is you can apply access control on it. You can say
private typealias MyNewType = MyExistingType
That makes MyNewType is only accessible the context that is defined in.