Swift - Can I set the value of self when calling a closure? - swift

TLDR: Is there a equivalent for JavaScript call or apply in Swift?
Let's say I have Foo class that have an instance variable bar and a method baz that receives a closure as argument:
class Foo {
var bar: String = ""
func baz(closure: (Void) -> Void) {
closure()
}
}
I want to change the self value inside the closure. So the code is executed by the Foo instance.
Like this:
let foo = Foo()
foo.baz {
// I want to be able to change bar value without calling foo.bar, like this:
bar = "Hello world"
}
// Then foo.bar would be "Hello world"
Is that possible?

You can't access Foo's members in the closure in the way you've described, but what you can do is modify the closure to take an instance of Foo as an argument, and pass in self. The result could look something like this.
class Foo {
var bar: String = ""
func baz(closure: (this: Foo) -> Void) {
closure(this: self)
}
}
let foo = Foo()
foo.baz { this in
this.bar = "Hello world"
}
print(foo.bar) // Hello world

Here's a generic type version, which looks closer to call in javascript.
class Foo {
var bar: String = ""
}
func call<T>( this: T, closure: (T->Void) ){
closure(this)
}
let foo = Foo()
call(foo){ this in
this.bar = "Hello World"
}

Related

Pass in argument predefined or custom function in swift

I want to pass to setter as argument one of predefined functions (in enum maybe or static) or custom function in closure.
like UIColor to UIView.backgroundColor (i can set .black or UIColor(...)). How can I do it with my custom class?
class MyClass {
var fun: ((String)->Void)?
}
var obj = MyClass()
obj.fun = {print($0)} . // It works now
obj.fun = .predefinedFunc // It's how i want to be able do
What you seem to want to do is "implicit member access". Unfortunately, this is not possible on closure types like (String) -> Void, because it only works on enums, as well as types with static members. (String) -> Void doesn't and can't have any static members.
It seems like what you want is simply a bunch of predefined functions. This can be done with an enum:
enum Function {
case predefinedFunc1
case predefinedFunc2
case predefinedFunc3 // name these properly!
var closure: (String) -> Void {
switch self {
case .predefinedFunc1: return { print($0) }
case .predefinedFunc2: ...
case .predefinedFunc3: ...
}
}
}
And then you'll be able to do:
class MyClass {
var fun: Function?
}
var obj = MyClass()
obj.func = .predefinedFunc1
If you also want to include an option to use a custom function, add an extra case with an associated value:
enum Function {
...
case custom((String) -> Void)
var closure: (String) -> Void {
switch self {
case .predefinedFunc1: return { print($0) }
...
case .custom(let f): return f
}
}
}
view.backgroundColor = .black
works because black is a static property of struct UIColor. The right-hand side is called an “implicit member expression,” see for example What is the Swift syntax " .bar" called?.
Function types are neither classes nor structs, and you cannot define a static property for a function type. Therefore an identical syntax is not possible.
What you can do is to define a “wrapper” struct for the function, with static properties for the predefined functions. Here is a simple example:
struct Fun {
let f: (String) -> Void
init(_ f: #escaping (String) -> Void) {
self.f = f
}
// Predefined functions:
static var printer = Fun( { print($0) } )
// ...
}
class MyClass {
var fun: Fun?
}
And then you can do
let obj = MyClass()
obj.fun = Fun( { print($0) } ) // set to custom function
obj.fun = .printer // set to predefined function
This approach also allows to extend the wrapper type by more predefined functions:
extension Fun {
static var printReversed = Fun( { print($0.reversed()) } )
}
// ...
obj.fun = .printReversed
Please define the func same as you callback func or nameless fun, then you can pass it as an argument.
class MyClass {
var fun: ((String)->Void)?
}
//MARK:- you have to provide the same param and return type in your predefined func.
func printer(Str :String)->Void{
print(Str)
}
var obj = MyClass()
obj.fun = {print($0)}
obj.fun = printer

Pass anonymous array as paramter in Swift

I have a simple class in my application:
class MyClass{
var Name: String
var Foo1: Bar
var Foo2: Bar?
//var Foos: [Bar]
}
Foo1 is never nil, and Foo2 is optional, so it might be nil.
Instead of having an init like the one below, I'd like to rather have a list property Foos: [Bar], that might contain 1 or 2 elements.
init(_ Name: String, _ Foo1: Bar, _ Foo2: Bar?){
self.Name = Name
self.Foo1 = Foo1
self.Foo2 = Foo2
}
In C# I'd write something like MyClass m = new MyClass("m", New List<Bar>{Bar1, Bar2}) or MyClass m = new MyClass("m", New List<Bar>{Bar1, null}). I'd prefer to rather have one property in MyClass as a List, instead of two separate fields where one might be nil.
I've tried this initializer:
init(_ Name: String, _ Foos: [Bar]) {
self.Name = Name
self.Foos = Foos
}
But when I try to pass an anonymous List to the initializer, I get this warning:
let m = MyClass("m", [Bar]().append(B1))
Cannot use mutating member on immutable value: function call returns immutable value
How can I pass a populated anonymous list to the initializer in Swift, like I would've done in C#?
Try this code
struct Bar { }
class MyClass {
var name: String
var foos: [Bar?]
init(name: String, foos: [Bar?]) {
self.name = name
self.foos = foos
}
}
let bar0 = Bar()
let bar1: Bar? = nil
let object = MyClass(name: "String", foos: [bar0, bar1])
Hope you got the idea. And read more about how it works in Swift
The most challenging requirement, which is also not met by the accepted answer:
Instead of having an init like the one below, I'd like to rather have a list property Foos: [Bar], that might contain 1 or 2 elements.
One way of dealing with this could be a custom runtime error.
enum BarWrapperError: Error {
case emptyList
}
To pass the list and check if it at least contains one element, you can introduce another type
struct BarWrapper {
init(bars: [Bar]) throws {
guard bars.count > 0 else {
throw BarWrapperError.emptyList
}
self.bars = bars
}
let bars: [Bar]
var firstBar: Bar {
return bars[0]
}
var otherBars:[Bar] {
return Array(bars[1 ..< bars.count])
}
}
BarWrapper is initialised with a list of bars. If this list is empty, it will throw an error.
your MyClass would now look like:
class MyClass {
let name: String
let firstBar: Bar
let otherBars: [Bar]
init(name: String, barWrapper: BarWrapper) {
self.name = name
self.firstBar = barWrapper.firstBar
self.otherBars = barWrapper.otherBars
}
}
If you care for the error and want to continue the execution, you can use this like
do {
let bar0 = Bar()
let bar1 = Bar()
let barWrapper = try BarWrapper(bars: [bar0, bar1])
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
} catch BarWrapperError.emptyList {
print("empty bar list")
}
If you rather want to crash the app if the list is empty, you can shorten it to
let bar0 = Bar()
let bar1 = Bar()
let barWrapper = try! BarWrapper(bars: [bar0, bar1])
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
by using try!
You can also do
let bar0 = Bar()
let bar1 = Bar()
if let barWrapper = try? BarWrapper(bars: [bar0, bar1]) {
let object = MyClass(name: "String", barWrapper: barWrapper)
print(object.otherBars)
}
if you don't need error handling and your app would be still operational if the MyClass instance isn't created.

Stored properties in Swift can't refer to each other?

What is the reason I can't give a value to a stored property that depends on the value of another one in Swift 2.0?
The code below gives an error saying:
Something.Type does not have a member named 'foo'
class Something {
let foo = "bar"
let baz = "\(foo) baz"
}
This is odd, as Something.Type certainly does have a member called foo.
Is there a way around this?
Looks like you're trying to initialise the variable baz, before swift has had a chance to know that foo is a property of Something. Place your initialisation inside the init constructor.
class Something {
let foo: String
let baz: String
init () {
foo = "bar"
baz = "\(foo) baz"
}
}
You can also use lazy initialization but now you have to make it a variable:
class Something {
let foo = "bar"
lazy var baz = { "\(self.foo) baz" }()
}

How to create empty closure properly?

I have a class with a closure as a property:
class MyClass{
var onChange = {}
func foo(){
onChange()
}
}
A behaviour implemented in closure is used in method foo:
var c = MyClass()
c.onChange = {
println("something is changed");
}
c.foo() // prints 'something is changed'
It's easy to make closures with an argument like {(n: Int) -> Void in println(n); } but how to create a closure without input arguments?
I tried to use {}, {in}, etc., but it gave a compilation error.
How to create empty closure properly?
If I understood your question correctly, you could use:
var closure = {() -> () in
return
}

Satisfy Swift Protocol Methods with Closure Attributes?

I have a protocol with a method. I have thought that methods can be replaced with closures by the same name, but it doesn't seem to work:
protocol Foo {
func bar() // Type: Void -> Void
}
class X: Foo {
func bar() { }
}
class Y: Foo { // Compiler: doesn't conform to protocol Foo
let bar: Void->Void = {}
}
Is there a way to make this work? I want to override the methods behavior for a Test Stub implementation. Currently, I'd have to do this, which I'd like to shorten:
class Z: Foo {
var barClosure: Void -> Void = {}
func bar() {
barClosure()
}
}
let baz = Z()
baz.barClosure = { /* ... */ }
baz.bar() // Calls the closure replacement
Thanks to #Dániel Nagy, I was able to figure out what options I have. The protocol should require a closure. This way, the client code won't change, as closure calls are identical to method calls.
make the property mutable so implementations can decide if they want to lock the value
require a getter only (for the same reason)
initialize the property as immutable (let) in production code
initialize the property as mutable (var) in test code to provide alternate implementations in test cases, like mock observers do
Here's a modified example which works well in a Playground by returning strings:
protocol Foo {
var bar: () -> String { get }
}
class X: Foo {
// cannot be overwritten
let bar: () -> String = { return "default x" }
}
class Y: Foo {
private let _bar: () -> String = { return "default y" }
// Can be overwritten but doesn't have any effect
var bar: () -> String {
get {
return _bar
}
set {
}
}
}
class Z: Foo {
// Can be overwidden
var bar: () -> String = {
return "default z"
}
}
let bax = X()
bax.bar() // => "default x"
// bax.bar = { /* ... */ } // Forbidden
let bay = Y()
bay.bar() // => "default y"
bay.bar = { return "YY" }
bay.bar() // => "default y"
let baz = Z()
baz.bar() // => "default z"
baz.bar = { return "ZZ" }
baz.bar() // => "ZZ"
You declared the protocol to have a function, bar(), but in class Y, you just have a constant instead of a function, this is the problem. But if you want to have something like in class Y, you should change the protocol to:
protocol Foo {
var bar: () -> () {get set}
}
And implement like that:
class Test: Foo {
private var _bar: (() -> ())?
var bar: () -> () {
get {
return {}
}
set {
self._bar = newValue
}
}
}
UPDATED
If you to shorten your class, you can use something like that:
protocol Foo {
var barClosure: Void -> Void {get set}
}
class Z: Foo {
var barClosure: Void -> Void = {
//do here something
}
}
let a = Z()
a.barClosure()
The func keyword does a little bit more magic behind the scenes that you can’t replicate with properties – especially in the case of classes, where functions can be overridden, so vtables need to be built etc.
That said, if you were going to replace methods using closure expressions, you’d need to do more than the code you gave. The equivalent of this:
struct A {
let x: Int
func f() {
println("In f(), x is \(a.x)")
}
}
would be more like this:
struct A {
let x: Int
// returns a function that takes A objects, and
// returns a function that captures them
static let f: (A)->()->() = { a in
{ ()->() in println("In f(), x is \(a.x)") }
}
// equivalent of the dot notation call of f
var f: ()->() {
return A.f(self)
}
}
This replicates how struct methods actually work, and allows you to do all the same things an f method does:
let a = A(x: 5)
// static version of f
let A_f = A.f
// that static version bound to a self:
let f = A_f(a)
f()
// the above is equivalent to:
a.f()
But this still isn’t enough for A to conform to a protocol that requires an f() method.