Is there a way to reference instance function when calling SequenceType.forEach?
There are two different problems here. The trailing closure syntax
can be used when calling a function and the last parameter is a closure,
so
let b1 = someFoos.contains({ $0.isBaz })
let b2 = someFoos.contains { $0.isBaz }
are fully equivalent. However, the trailing closure syntax can be problematic in the condition of an if-statement:
if someFoos.contains({ $0.isBaz }) { } // OK
if someFoos.contains { $0.isBaz } { } // Compiler error
if (someFoos.contains { $0.isBaz }) { } // OK, as noted by R Menke
We can only speculate why the second one does not work. It could be that the compiler
takes the first {
as the start of the if-body. Perhaps this will
change in a future version of Swift but probably it is not worth
the effort.
The other problem is about curried functions.
someFoos.forEach(bar2)
compiles because bar2
has the type Foo -> Void
, and that is exactly
what the forEach()
method expects. Foo.bar
, on the other hand,
is a curried function (see http://oleb.net/blog/2014/07/swift-instance-methods-curried-functions/) which takes the instance as the first
argument. It has the type Foo -> () -> ()
. So
Foo.bar(someFoo)
is a closure with type () -> ()
, and
Foo.bar(someFoo)()
calls the bar
method on the someFoo
instance.
(Note: The following is not meant as an actual recommendation,
but only as a demonstration about curried functions and fun
with closures!)
To pass Foo.bar
directly as an argument to forEach()
we need to
"swap" the order of the parameters. Haskell has a "flip" function for that purpose,
and it is also possible in Swift (see e.g. How to write a flip method in Swift?):
func flip<A, B, C>(f: A -> B ->C) -> B -> A ->C {
return { b in { a in f(a)(b) } }
}
Then flip(Foo.bar)
has the type () -> Foo -> ()
, so
the void argument of the bar
method can be applied
flip(Foo.bar)()
to get a Foo -> ()
closure, and
flip(Foo.bar)()(someFoo)
calls the bar
method on the someFoo
instance.
And now we can call
someFoos.forEach (flip(Foo.bar)())
without using a closure expression { .. }
!!
If isBaz
were a method instead of a property
func isBaz() -> Bool { return false }
then you
could do the same in the if-expression:
if someFoos.contains(flip(Foo.isBaz)()) {
// ...
}
Again, this is only meant as a demonstration. Also properties
are not curried functions, so this cannot be done with
your isBaz
property.
Calling instance method on each object in array
What you are doing in
items.forEach { $0.instanceMethod() }
let mappedItems = items.map { $0.mappingInstanceMethod() }
is a clean and Swifty way. As explained in Is there a way to reference instance function when calling SequenceType.forEach?, the first statement cannot be reduced
to
items.forEach(Item.instanceMethod)
There is one exception though: It works with init
methods
which take a single argument. Example:
let ints = [1, 2, 3]
let strings = ints.map(String.init)
print(strings) // ["1", "2", "3"]
Swift, use function UIView.removeFromSuperview as an argument in forEach function over array of views
It's easy enough to just call the method on $0
:
views.forEach {
$0.removeFromSuperview()
}
(And you can name the argument if you like.)
But you could also wrap it into a method:
extension Sequence {
func onEach(_ invoke: (Iterator.Element) -> () -> Void) {
self.forEach { invoke($0)() }
}
}
This works because instance methods can be represented as functions that take an instance of their type and return a function that has their "top-level" signature.
One downside here is that you can't include the rethrows
annotation that's present on forEach
, because rethrows
only applies if the function argument itself is throwing.
How to use a swift Instance method with flatMap?
With instance method, you cannot.
Instance method is a curry method, the type (String) -> () -> Array<String>
mean "a method take string and return a function which takes nothing and return array of string".
So you can do like this, but not as you write.
print(animals.flatMap{ String.double($0)() }) // ["Ant", "Ant", "Bear", "Bear", "Cat", "Cat"]
What you need is a static method. It simply take string and return array of string.
extension String {
static func double(_ string: String) -> [String] {
return [string, string]
}
}
print(animals.flatMap(String.double)) // ["Ant", "Ant", "Bear", "Bear", "Cat", "Cat"]
Calling a function on a single instance?
You can introduce a dismiss callback in PopupViewController
and implement it inside the presenter i.e, TableViewController
as below and get rid of the static
approach.
class PopupViewController: UIViewController {
public var onDismiss: (() -> Void)?
// Using dismiss callback to notify the subscriber
override func dismiss(animated flag: Bool, completion: (() -> Void)? = nil) {
super.dismiss(animated: flag, completion: self.onDismiss)
}
// rest of your code
}
Implement onDismiss
callback in TableViewController
class TableViewController {
func showPopup() {
let popup : PopupViewController = self.storyboard?.instantiateViewController(withIdentifier: "PopupViewController") as! PopupViewController
popup.onDismiss = {[weak self] in
self?.test()
}
self.presentOnRoot(with: popup)
}
// rest of your code
}
Better way to check the instance is kind from list of classes?
If I think about it logically, it would make a lot more sense to define a superclass / protocol for them, something like this:
class MyClassNumber { }
class MyClassOne: MyClassNumber { }
class MyClassTwo: MyClassNumber { }
class MyClassLetter { }
let one = MyClassOne()
let two = MyClassTwo()
let letter = MyClassLetter()
if one is MyClassNumber {
// TRUE
}
if two is MyClassNumber {
// TRUE
}
if letter is MyClassLetter {
// FALSE
}
Don't see any use case for yours
1 global function or many instance functions
I would suggest neither.
You should have a data model class and the upvote
function should be part of that class.
class Post {
var postID: String
public private(set) var votes: Int
...
func upvote() {
self.votes += 1
}
}
Then you would call it as
somePost.upvote()
1 global function or many instance functions
I would suggest neither.
You should have a data model class and the upvote
function should be part of that class.
class Post {
var postID: String
public private(set) var votes: Int
...
func upvote() {
self.votes += 1
}
}
Then you would call it as
somePost.upvote()
Calling a function inside itself
That's called recursion, and it's perfectly legal:
var count = 0
func theFunc() {
print(count)
count += 1
if count < 4 {
theFunc()
}
}
theFunc() // 0 1 2 3
The only trick is not to recurse too deeply, as you risk running out of resources, and don't forget to put some sort of "stopper" (such as your if count < 4
), lest you recurse forever, resulting in (oh the irony) a stack overflow.
[Extra for experts: there are some languages, such as LISP, that are optimized for recursion, and where recursion is actually preferred to looping! But Swift is not really one of those.]
Better way to write this if condition with a force-unwrap?
There are many ways to solve this situation.
The simplest one is to extract the condition into a separate function/closure and check
func fileExists(defaultUrl: URL?) -> Bool {
guard let defaultUrl = defaultUrl else { return false }
return FileManager.default.fileExists(atPath: defaultUrl.path)
}
The same can be done using a variable:
let fileExists: Bool
if let defaultUrl = defaultUrl {
fileExists = FileManager.default.fileExists(atPath: defaultUrl.path)
} else {
fileExists = false
}
Or, you can use Optional.map
:
let fileExists = defaultUrl.map { FileManager.default.fileExists(atPath: $0.path) } ?? false
Related Topics
What's the Equivalent of the Uisplitviewcontroller in Swiftui
How to Prevent from Scrolling Uitableview Up When Nsfetchedresultscontroller Add New Record
Error When Trying to Call Setdata(From: ) in the Cloud Firestore iOS API. How to Fix It
Could Not Find an Overload for "Init" That Accepts the Supplied Arguments in Swift
How to Get Commoncrypto/Commoncrypto File From
Firebase .Indexon Dynamic Keys
Swift - Image Data from Ciimage Qr Code/How to Render Cifilter Output
How to Fill Uitableview with a Data from Dictionary. Swift
How to Pass Data from One Container to Another, Both Embedded in the Same Uiviewcontroller in Swift
Get iOS Contact Image with Abpersoncopyimagedata
Auto Adjust Custom Uitableviewcell and Label in It to the Text
Remove Space Navigationtitle But Not the Back Button
Count Unseen Messages with Firebase in Swift
Set a Maximum Number of Children in Firebase