I am new to RxSwift and I apologise how badly the question is framed but I couldn't find the proper terminology.
So basically I have a let infoIconTapped: Driver<Void> in one struct, and I have let shouldShowInfoPopup: Driver<Bool> in another struct.
What I want to do is I want to change the value of (or drive it) shouldShowInfoPopup to true or false using infoIconTapped. This should be done using Driver only which is my requirement.
Any idea on how to do this?
It sounds like what you want to do is toggle the visibility of the popup based on the tapping of the button. If so, then you need to maintain state and that means using the .scan operator.
func shouldShowInfoPopup(infoIconTapped: Driver<Void>) -> Driver<Bool> {
return infoIconTapped
.scan(false) { current, _ in !current }
.startWith(false)
}
The above would be a free function (not in any class or struct) and could be used like this:
let showInfo = shouldShowInfoPopup(infoIconTapped: infoIconTapped)
If you must put the function in a class or struct, then put it in an extension on Driver itself like this:
extension SharedSequence where SharingStrategy == DriverSharingStrategy {
var shouldShowInfoPopup: Driver<Bool> {
scan(false) { current, _ in !current }
.startWith(false)
}
}
Which could be used like this:
let shouldShowInfoPopup = infoIconTapped.shouldShowInfoPopup
I know I can get the list of class properties using Mirror(reflecting:) but I can only print them. But what if I want to set properties to them and return the mirrored object.
Somwhat like this -
let mirroredObj = Mirror(reflecting: User())
for (index, property) in mirroredObj.children.enumerate() {
property.value = <SOME_VALUE>
}
return mirroredObj
Or maybe some totally different approach to do this?
You're trying to modify a class during runtime, which is impossible in Swift.
You are able to add a dictionary [String: Any] as a property though. It can be modified during runtime.
Swift 2 NSMutableArray() value don't shows in variable
My codes here.
var detail_desc : NSMutableArray! = NSMutableArray()
when i added this code gives me
label.text = self.detail_desc.description
And my array output
{
{
"cat_img" = "http://asdasd.com/asd.jpg";
"cat_name" = Good;
id = 45;
"is_active" = 3;
"last_update" = "2015-11-21 15:32:42";
}
}
I want to use array item to variable ;
label.text = cat_img
label2.text = cat_name
any idea ?
There's a lot wrong with what you have, I'll try to tackle it one by one:
Underscore
Underscore naming isn't common in cocoa and reads very strange. Change detail_desc to detailDesc.
Note: There are situations in which we use the underscore, one of which is extensions on core classes. In these cases, it is prefix - underscore - name. ie: my_specialFunctionOrVariable
NSMutableArray
NSArray and NSDictionary classes shouldn't be used in Swift unless absolutely necessary. They behave slightly differently, and you lose all of the type safety that makes swift so great.
Naming
In general, naming schemes like label, label2, and detailDescription are very confusing. They don't tell me anything about what each represents. Try something like catImageUrlLabel, and catNameLabel to be more specific. Also, detailDescription could be catInformation
Single Object Encapsulated In Array
You really have a dictionary as the single object in an array, I think you should just have the dictionary reference. (unless there could be more in the future). I'll move forward assuming there won't be.
How it might look
Change your variable to something like this:
var detailDescription: [String : AnyObject] = ...
Then, in your UI setting code, do something like this:
label.text = detailDescription["cat_img"] as? String
label2.text = detailDescription["cat_name"] as? String
Modelling Objects
Json is dirty and we prefer not to deal with it as much as possible. Consider modeling your Cat json to a Cat object:
struct Cat {
let imageUrl: String
let name: String
let id: Int
// etc.
}
Then parse the json into this model when you receive it and pass that to your view controller.
Then our code might look like this:
nameLabel.text = cat.name
imageUrlLabel.text = cat.imageUrl
// etc.
This is very clear what everything represents, and will make for a more readable, and more maintainable code base.
Hope this gets you off on the right track.
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 the problem, to have a high amount of buttons which have a number as their label, so i thought i could take the label as an integer instead of creating an action for every button?!
#IBAction func NumberInput(sender: UIButton) {
var input:Int = sender.titleLabel as Int
}
If you want to do this, you can convert the string to an Int by using string.toInt() such as:
if let input = sender.titleLabel?.text?.toInt() {
// do something with input
} else {
// The label couldn't be parsed into an int
}
However, I'd suggest either using UIView.tag or subclassing UIButton and adding an Int property to it to accomplish this, in case you ever change the display of your labels.
You should make sure that the text exists
var input:Int = (sender.titleLabel.text! as NSString).integerValue
You can't convert a UILabel to an Int. I think you want this instead:
var input : Int? = sender.titleLabel.text?.toInt()
Another way to convert a label in swift:
let num = getIntFromLabel(labelView)
connect all your buttons to 1 IBAction. then create the following variable and the set/get methods based on how you will use it.
note: "something" is a UILabel. The variable I wrote below should help you do conversions easily and with cleaner syntax. "newValue" comes with all setter methods. It basically takes into account any value that could possibly used to set "num" to a new value.
var num : Int {
get {
return Int(something!)!
}
set {
something.text = Int(newValue)
}
}
For Swift 3, what you can do is to directly convert it from an String input to an integer, like this
Int(input.text!)
And then, if for any reason, if you wish to print it out or return is as a String again, you can do
String(Int(input.text!)!)
The exclamation mark shows that it is an optional.