This question already has answers here:
Swift closure syntax using { ... in }
(2 answers)
Closed 1 year ago.
I've seen the word "in" multiple times in SwiftUI, but don't quite understand what it means. Because the word is so short and common, I'm having a hard time searching for its meaning and usage.
Most recently, I've seen it in the onChange method for example:
struct ContentView: View {
#State private var name = ""
var body: some View {
TextField("Enter your name:", text: $name)
.textFieldStyle(RoundedBorderTextFieldStyle())
.onChange(of: name) { newValue in
print("Name changed to \(name)!")
}
}
As like in your code example using in afther newValue let`s Swift and you know the return value of process is going happen on newValue so it is more like a symbol or flag called: "token" to you and to CPU that work is going happen on newValue because you put it before in, for example you gave name to onChange modifier with using newValue in, you will get updated value for name if get any new update, also note you can use $0 instead of newValue in and works same.
.onChange(of: name) { newValue in
return print("Name changed to \(name)!")
}
Or:
.onChange(of: name) {
print("Name changed to \($0)!")
}
Both are the same it depends how you like it.
Another example for better understanding:
let arrayOfInt: [Int] = [1, 2, 3]
let arrayOfString: [String] = arrayOfInt.map({ value in return value.description })
let arrayOfString: [String] = arrayOfInt.map({ $0.description })
For example we have an arrayOfInt and we want create a String array of it, for this work we can use map, and again like last example we used in in 2 way. in the first one as: value in and in the second on as $0. I recommend you work and learn more about closure in Swift/SwiftUI they are very important, without knowing them well, we cannot have good understanding of codes in iOS.
Also do not forget to attention to word of return right after in you can type and use or not use it, Swift is Smart enough to understand because of inference, but if you use it, it make your code much more easy and simple to read and better in syntax of code.
Related
This question already has answers here:
SwiftUI TextField with formatter not working?
(9 answers)
Closed 2 years ago.
Is there a way to capture the value in the TextField without hitting the return key? I have a TextField that I would like to capture the value (years) without hitting the return key.
onEditingChanged is triggered only when the field has focus or left focus
onComit is triggered only when the return key is hit.
The scenario I am looking at is when I enter "5", for example, in the field and hit "Done", I would like to capture the new value not the existing value.
TextField("Number of Years...", value: $years, formatter: integerFormatter, onEditingChanged: { _ in
print("OnEditingChanged: \(years)")
},
onCommit: {
print("\(years)")
})
Firstly, add #State variable 'year':
#State private var year: Int = 0
Secondly, pass Binding to TextField 'text' parameter and use formatter inside, like this:
TextField("Year", text: Binding<String>(
get: { String(year) },
set: {
if let value = NumberFormatter().number(from: $0) {
self.year = value.intValue
print("Year: \(year)")
}
}
))
It will print 'year' value until something else but number will be pressed on the keyboard.
To leave only number input you can set keyboardType to your TextField:
.keyboardType(UIKeyboardType.numberPad)
Edit: Apparently with Formatters the value doesn't just update live. A pattern you could try would be having the text field set a #State String and have .onChange trigger a formatting function ... or have a get/set Binding that runs that function for you.
In this way, any button push to exit the view can pul the live raw String from the user input, as well as set that String to whatever formatted version you want.
If focus change is an issue, you might pick stuff up from: https://www.youtube.com/watch?v=SYBmMugnol0&feature=emb_rel_pause
=============
So, teehee, you might laugh, but you already have it working.
Add
.onChange(of: years) { print($0) }
and you'll see what I mean. The #State property is already delivering you a live feed. If you're using Combine to drive some sort of processing pipeline, you might want to deduplicate and and debounce. Apologies if you're not a beginner and what I just wrote was pedantic.
I'm working on an iOS application adopting the MVVM pattern, using SwiftUI for designing the Views and Swift Combine in order to glue together my Views with their respective ViewModels.
In one of my ViewModels I've created a Publisher (type Void) for a button press and another one for the content of a TextField (type String).
I want to be able to combine both Publishers within my ViewModel in a way that the combined Publisher only emits events when the button Publisher emits an event while taking the latest event from the String publisher, so I can do some kind of evaluation on the TextField data, every time the user pressed the button. So my VM looks like this:
import Combine
import Foundation
public class MyViewModel: ObservableObject {
#Published var textFieldContent: String? = nil
#Published var buttonPressed: ()
init() {
// Combine `$textFieldContent` and `$buttonPressed` for evaulation of textFieldContent upon every button press...
}
}
Both publishers are being pupulated with data by SwiftUI, so i will omit that part and let's just assume both publishers receive some data over time.
Coming from the RxSwift Framework, my goto solution would have been the withLatestFrom operator to combine both observables.
Diving into the Apple Documentation of Publisher in the section "Combining Elements from Multiple Publishers" however, I cannot find something similar, so I expect this kind of operator to be missing currently.
So my question: Is it possible to use the existing operator-API of the Combine Framework to get the same behavior in the end like withLatestFrom?
It sounds great to have a built-in operator for this, but you can construct the same behavior out of the operators you've got, and if this is something you do often, it's easy to make a custom operator out of existing operators.
The idea in this situation would be to use combineLatest along with an operator such as removeDuplicates that prevents a value from passing down the pipeline unless the button has emitted a new value. For example (this is just a test in the playground):
var storage = Set<AnyCancellable>()
var button = PassthroughSubject<Void, Never>()
func pressTheButton() { button.send() }
var text = PassthroughSubject<String, Never>()
var textValue = ""
let letters = (97...122).map({String(UnicodeScalar($0))})
func typeSomeText() { textValue += letters.randomElement()!; text.send(textValue)}
button.map {_ in Date()}.combineLatest(text)
.removeDuplicates {
$0.0 == $1.0
}
.map {$0.1}
.sink { print($0)}.store(in:&storage)
typeSomeText()
typeSomeText()
typeSomeText()
pressTheButton()
typeSomeText()
typeSomeText()
pressTheButton()
The output is two random strings such as "zed" and "zedaf". The point is that text is being sent down the pipeline every time we call typeSomeText, but we don't receive the text at the end of the pipeline unless we call pressTheButton.
That seems to be the sort of thing you're after.
You'll notice that I'm completely ignoring what the value sent by the button is. (In my example it's just a void anyway.) If that value is important, then change the initial map to include that value as part of a tuple, and strip out the Date part of the tuple afterward:
button.map {value in (value:value, date:Date())}.combineLatest(text)
.removeDuplicates {
$0.0.date == $1.0.date
}
.map {($0.value, $1)}
.map {$0.1}
.sink { print($0)}.store(in:&storage)
The point here is that what arrives after the line .map {($0.value, $1)} is exactly like what withLatestFrom would produce: a tuple of both publishers' most recent values.
As improvement of #matt answer this is more convenient withLatestFrom, that fires on same event in original stream
Updated: Fix issue with combineLatest in iOS versions prior to 14.5
extension Publisher {
func withLatestFrom<P>(
_ other: P
) -> AnyPublisher<(Self.Output, P.Output), Failure> where P: Publisher, Self.Failure == P.Failure {
let other = other
// Note: Do not use `.map(Optional.some)` and `.prepend(nil)`.
// There is a bug in iOS versions prior 14.5 in `.combineLatest`. If P.Output itself is Optional.
// In this case prepended `Optional.some(nil)` will become just `nil` after `combineLatest`.
.map { (value: $0, ()) }
.prepend((value: nil, ()))
return map { (value: $0, token: UUID()) }
.combineLatest(other)
.removeDuplicates(by: { (old, new) in
let lhs = old.0, rhs = new.0
return lhs.token == rhs.token
})
.map { ($0.value, $1.value) }
.compactMap { (left, right) in
right.map { (left, $0) }
}
.eraseToAnyPublisher()
}
}
Kind-of a non-answer, but you could do this instead:
buttonTapped.sink { [unowned self] in
print(textFieldContent)
}
This code is fairly obvious, no need to know what withLatestFrom means, albeit has the problem of having to capture self.
I wonder if this is the reason Apple engineers didn't add withLatestFrom to the core Combine framework.
I'm trying to understand how to change #State variables in #ViewBuilder closures. The following is just a simple example:
struct ContentView: View {
#State var width = CGFloat(0)
var body: some View { //Error 1
GeometryReader { geometry in //Error 2
self.width = geometry.size.width
return Text("Hello world!")
}
}
}
I'm getting some errors:
Error 1:
Function declares an opaque return type, but has no return statements
in its body from which to infer an underlying type
But the return is redundant because there's only one line of code inside the View computed property.
Error 2:
Cannot convert return expression of type 'GeometryReader<_>' to return
type 'some View'
Since I explicitly write return Text("...") shouldn't the type be clear?
What's the problem here?
First, you can't make arbitrary swift statements in a functionBuilder. There is a specific syntax allowed. In SwiftUI's case, it's a ViewBuilder, which under the hood is composing your type. When you make consecutive statements in SwiftUI, you are actually relying on the compiler to compose a new type underneath according to the rules of that DSL.
2nd, SwiftUI is a recipe, it's not an event system. You don't change state variables in the body function, you set up how things should react when state variables change externally. If you want another view to react in some way to that width, you need to define that content with respect to the width of the component you want. Without knowing what your ultimate goal is here, it's hard to answer how to relate the content to each other.
EDIT:
I was asked to elaborate on what exactly is allowed. Each functionBuilder has a different allowable syntax which is defined in the functionBuilder itself. This has a good overview on function builders in Swift 5.1: https://www.swiftbysundell.com/posts/the-swift-51-features-that-power-swiftuis-api
As for what SwiftUI is specifically looking for, it's essentially looking for each statement to return an instance of View.
// works fine!
VStack {
Text("Does this")
Text("Work?")
}
// doesn't work!
VStack {
Text("Updating work status...")
self.workStatus = .ok // this doesn't return an instance of `View`!
}
// roundabout, but ok...
VStack {
Text("Being clever")
gimmeView()
}
// fine to execute arbitrary code now, as long as we return a `View`
private func gimmeView() -> some View {
self.workingStatus = .roundabout
return Text("Yes, but it does work.")
}
This is why you got the obtuse error you got:
Cannot convert return expression of type 'GeometryReader<_>' to return type 'some View'
The type system can't construct any View out of View and essentially Void when you execute:
self.width = geometry.size.width
When you do consecutive View statements, underneath, it's still being converted into a new type of View:
// the result of this is TupleView<Text, Text>
Text("left")
Text("right")
This question already has answers here:
Return Statement Error using Firebase in Swift
(1 answer)
Code Not Completing Before Next Method Is Called
(1 answer)
Swift accessing response from function
(1 answer)
Closed 4 years ago.
I am making an app in which I have to return a String representing the existence of a certain reference to my firebase realtime database (I am not using a Bool for other reasons). Despite executing the print commands directly above and below, when I print the function, the value of the String won't change and prints its default value "Unchanged".
My code:
func checkIfMovieHasAlreadyBeenShown(movieID: String) -> String {
var hasItBeenSeen = "Unchanged"
let uid = Auth.auth().currentUser?.uid
let ref = Database.database().reference()
ref.child("Seen Movies").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild(movieID){
print("The Result:")
self.changeStringHasItBeenSeen(YNString: "yes")
print("YES")
}
else {
print("The Result:")
self.changeStringHasItBeenSeen(YNString: "no")
print("NO")
}
})
return hasItBeenSeen
}
It is printed here:
print(checkIfMovieHasAlreadyBeenShown(movieID: "39"))
print(checkIfMovieHasAlreadyBeenShown(movieID: "38"))
observeSingleEvent is called in a closure. The return hasItBeenSeen line will not be affected by anything in the closure, as it will return first.
Imagine this: you tell your friend to go buy some apples. Before he has even left, you ask him for the price. Of course, he won't know, because he hasn't gone to buy the apples yet.
You need some way of performing something AFTER the closure has been called (or after the apples have been bought). Usually this can be done with a completion handler, where you pass a closure into your checkIfMovieHasAlreadyBeenShown function, such as, "do this AFTER you buy the apples".
Then you would call your function this way: checkIfMovieHasAlreadyBeenShown(movieID: "39", completion: { seen in print(seen) })
Does using let _ = ... have any purpose at all?
I've seen question and answers for What's the _ underscore representative of in Swift References? and I know that the underscore can be used to represent a variable that isn't needed.
This would make sense if I only needed one value of a tuple as in the example from the above link:
let (result, _) = someFunctionThatReturnsATuple()
However, I recently came across this code:
do {
let _ = try DB.run( table.create(ifNotExists: true) {t in
t.column(teamId, primaryKey: true)
t.column(city)
t.column(nickName)
t.column(abbreviation)
})
} catch _ {
// Error throw if table already exists
}
I don't get any compiler warnings or errors if I just remove the let _ =. It seems to me like this is simpler and more readable.
try DB.run( table.create(ifNotExists: true) {t in
t.column(teamId, primaryKey: true)
t.column(city)
t.column(nickName)
t.column(abbreviation)
})
The author of the code has written a book and a blog about Swift. I know that authors aren't infallible, but it made me wonder if there is something I am missing.
You will get a compiler warning if the method has been marked with a warn_unused_result from the developer documentation:
Apply this attribute to a method or function declaration to have the compiler emit a warning when the method or function is called without using its result.
You can use this attribute to provide a warning message about incorrect usage of a nonmutating method that has a mutating counterpart.
Using let _ = ... specifically tells the compiler that you know that the expression on the right returns a value but that you don't care about it.
In instances where the method has been marked with warn_unused_result, if you don't use the underscore then the compiler will complain with a warning. (Because in some cases it could be an error to not use the return value.)
Sometimes it is simple and cleaner to use try? than do-catch, when you call something that throws, but decided not to handle any errors. If you leave call with try? as-is, compiler will warn you about unused result, which is not good. So you can discard results using _.
Example:
let _ = try? NSFileManager.defaultManager().moveItemAtURL(url1, toURL: url2)
Also, let _ = or _ = can be used when right side of expression is lazy variable and you want it calculated right now, but have no use for the value of this variable yet.
A lazy stored property is a property whose initial value is not
calculated until the first time it is used. You indicate a lazy stored
property by writing the lazy modifier before its declaration.
Lazy properties are useful when the initial value for a property is
dependent on outside factors whose values are not known until after an
instance’s initialization is complete. Lazy properties are also useful
when the initial value for a property requires complex or
computationally expensive setup that should not be performed unless or
until it is needed.
Example:
final class Example {
private func deepThink() -> Int {
// 7.5 million years of thinking
return 42
}
private(set) lazy var answerToTheUltimateQuestionOfLifeTheUniverseAndEverything: Int = deepThink()
func prepareTheAnswer() {
_ = answerToTheUltimateQuestionOfLifeTheUniverseAndEverything
}
func findTheQuestion() -> (() -> Int) {
// 10 millions of thinking
let theQuestion = {
// something
return self.answerToTheUltimateQuestionOfLifeTheUniverseAndEverything
}
return theQuestion
}
}
let example = Example()
// And you *want* to get the answer calculated first, but have no use for it until you get the question
example.prepareTheAnswer()
let question = example.findTheQuestion()
question()
It's also useful to print() something in SwiftUI.
struct ContentView: View {
var body: some View {
let _ = print("View was refreshed")
Text("Hi")
}
}
View was refreshed
You can use it to see the current value of properties:
struct ContentView: View {
#State var currentValue = 0
var body: some View {
let _ = print("View was refreshed, currentValue is \(currentValue)")
Button(action: {
currentValue += 1
}) {
Text("Increment value")
}
}
}
View was refreshed, currentValue is 0
View was refreshed, currentValue is 1
View was refreshed, currentValue is 2
View was refreshed, currentValue is 3
View was refreshed, currentValue is 4
If you just did _ = print(...), it won't work:
struct ContentView: View {
var body: some View {
_ = print("View was refreshed") /// error!
Text("Hi")
}
}
Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols
You can user also #discardableResult in your own functions if sometimes you don't need the result.
#discardableResult
func someFunction() -> String {
}
someFunction() // Xcode will not complain in this case
if let _ = something {
...
}
is equal to
if something != nil {
...
}
If you replaced the underscore with a property name, the fix that Xcode would suggest is the second option (it would not suggest the first). And that's because the second option makes more programming sense. However—and this is something Apple themselves stress to developers—write the code that is the most readable. And I suspect the underscore option exists because in some cases it may read better than the other.