SwiftUI Text(): Using ternary conditional not localizing - swiftui

I'm having trouble printing localized text conditionally. For example, this localizes properly:
if valueFromDb.isEmpty {
Text("value_is_empty") //localized text
} else {
Text(valueFromDb)
}
It prints some text in the user's language if valueFromDb is empty, or it prints valueFromDb as it is if it's not. However, when I try to use the ternary operator it doesn't work:
Text(valueFromDb.isEmpty ? "value_is_empty" : valueFromDb)
When valueFromDb is empty, it prints "value_is_empty" rather than actual localized text. I get an error (a random one higher up in the hierarchy thanks to SwiftUI) when trying to cast it as LocalizedStringKey.
Edit: To be clear, I know I can do this:
valueFromDb.isEmpty ? Text("value_is_empty") : Text(valueFromDb)
However, I want to put the ternary conditional inside the Text() brackets because I will do this for several views, and each one will have many modifiers, so the code will become quite bloated.

The problem is due to type inference. You have to declare myString to be of type LocalizedStringKey and then everything will work as expected.
When you declare:
#State var mySrtring: LocalizedStringKey = "whatever"
Then:
Text(myString.isEmpty ? "error_text_localized" : myString)
uses this initializer:
public init(_ key: LocalizedStringKey,
tableName: String? = nil,
bundle: Bundle? = nil,
comment: StaticString? = nil)
When you declare it like this:
#State var mySrtring: String = "whatever"
Then:
Text(myString.isEmpty ? "error_text_localized" : myString)
uses this initialiser:
public init(verbatim content: String)

You have to put your valueFromDb in quotations, then it should work fine.
Text(valueFromDb.isEmpty ? "value_is_empty" : "\(valueFromDb)")

Related

Pass a published property as binding

I have an ObservableObject with a published dictionary of strings to arrays with arrays with Ints:
class MyObservableObject: ObservableObject {
#Published var myDict: [String: [[Int]]]
}
And I want to pass one array of Ints as a Binding from inside the same class to a function of a different struct:
{
...
func someFunc(key: String, index: Int) {
someStruct.func(myDict[key]![index])
}
...
}
I understand that #Published vars can't be passed as Bindings. I'm still hoping that there's any way to achieve this. I also tried storing a reference to the array in the other struct using an inout variable, also without success.
#Published vars can't be passed as Bindings
It is not true - it is possible, via projected value using '$', but you want to pass not a property, but part of value of a property, and this is different thing.
The context is not clear and this someFunc smells not-well :) - I'd say it is needed some refactoring here, but, anyway, technically it is possible to do what you want using dynamically in-place generated binding, like
func someFunc(key: String, index: Int) {
guard myDict[key] != nil else { return }
someStruct.func(Binding<[Int]>(
get: { self.myDict[key]![index] },
set: { self.myDict[key]![index] = $0 }
))
}

Cannot convert call result type 'Set<String>' to expected type 'String' error

I am working on an old Swift 3 project and I'm getting this error after updating it to Swift 4.2. It seems to work fine in Swift 3. I had to declare let NSURLPboardType = NSPasteboard.PasteboardType(kUTTypeURL as String) because NSURLPboardType does not exist in Swift 4.2 but otherwise the code is the same.
enum SparkleDrag {
static let type = "com.razeware.StickerDrag.AppAction"
static let action = "make sparkles"
}
let NSURLPboardType = NSPasteboard.PasteboardType(kUTTypeURL as String)
var nonURLTYpes: Set<String> {return [String(kUTTypeTIFF), SparkleDrag.type]}
var acceptableTypes: Set<String> {return [nonURLTYpes.union(NSURLPboardType)]}
The "u" in union is underlined with the error but I don't quite understand the nature of the problem. Any guidance would be much appreciated.
The problem is that NSURLPboardType is not a Set<String>, so the union cannot work.
If you're trying to get something like this:
["com.razeware.StickerDrag.AppAction", "public.url", "public.tiff"]
in aceptableTypes, you can simply forgo NSURLPboardType and do this:
enum SparkleDrag {
static let type = "com.razeware.StickerDrag.AppAction"
static let action = "make sparkles"
}
// let NSURLPboardType = NSPasteboard.PasteboardType(kUTTypeURL as String)
var nonURLTYpes: Set<String> {return [String(kUTTypeTIFF), SparkleDrag.type]}
var acceptableTypes: Set<String> {return nonURLTYpes.union([kUTTypeURL as String])}

Use of unresolved identifier when declaring variable inside a function declaration in SwiftUI

I am declaring a function for my program that includes a variable, but the variable is not read by the TextField in the function. Here is my code(which is not inside a struct):
func FieldText() {
var text : String = ""
TextField("Type here", text : $text)
}
The $text inside the TextField function is giving the error. I have tried declaring the variable like this:
var text = String("")
This also gave the error. How can I fix this?
If you don't care about the result of a TextField, you can pass it a .constant. You also need to return the TextField, however, so that the caller can use it.
func FieldText() -> some View {
TextField("Type here", text: .constant(""))
}

Does not conform to String protocol SwiftUI Picker View

I have a simple struct that I will build upon. Right now it has one field, an Int.
struct Card: CustomStringConvertible {
let value: Int
init(value: Int) {
self.value = value
}
var description: String {
return "\(String(value))"
}
}
If I do this, I get the Card to print it's value
let c = Card(value: 1)
print(c)
Now if I put an array of Cards in a CardController like this:
class CardController: ObservableObject {
#Published
var cards: [Card] = [
Card(value: 1),
Card(value: 2),
Card(value: 3)
]
Picker(selection: $selectedCardValue, label: Text("Choose a card")) {
ForEach(0..<cardController.cards.count) {
Text(self.cardController.cards[$0])
}
}
Text("You selected \(selectedCardValue)")
I'll get the error Initializer 'init(_:)' requires that 'Card' conform to StringProtocol. I'm not sure why I get this error. If I instead just change the cards to a type of [String] and values ["1", "2", "3"], the code works fine.
Any idea what's wrong here?
As E.Coms noted, the solution is to use one of the following:
Text(self.cardController.cards[$0].description)
Text(String(describing: self.cardController.cards[$0]))
Here's an explanation of why you have to do this inside the Text initializer, but not print().
Look at the two initializers for Text:
init(verbatim content: String) (docs)
init<S>(_ content: S) where S : StringProtocol (docs)
You must pass either a String or a Substring, the only two types conforming to StringProtocol. In this case, even though your type conforms to CustomStringConvertible, you are still passing a Card.
Contrast this with something like the print function:
func print(_ items: Any..., separator: String = " ", terminator: String = "\n") (docs)
Notice that the print function's arguments are denoted by Any, which is explained as
Any can represent an instance of any type at all, including function types.
The print function then converts whatever you passed it to a String:
The textual representation for each item is the same as that obtained by calling String(item).
String has an initializer which takes a type conforming to CustomStringConvertible and returns the description property.
So the reason you can write print(Card()) and not Text(Card() is because the print function has an intermediate step through String that can understand your conformance to CustomStringConvertible, but Text does not. If Text allowed you to pass it any type, it would be both more ambiguous ("What is the text representation of this type?" is not necessarily immediately apparent, as it depends on a hierarchical set of protocols), and more work for the SwiftUI system, which is already doing a lot.
You may miss the description by chance.
ForEach(0..<cardController.cards.count) {
Text(self.cardController.cards[$0].description)
}

Global variable in swift 3

I have the variable provincias_ws which has the answer of a service, so far everything is fine but I want to know how I make the variable provincias_ws is global since I want to print it in the textFieldDidBeginEditing method
class ViewCtrl: UIViewController, ValidationDelegate, UITextFieldDelegate {
#IBOutlet weak var txt_ciudadU: SearchTextField!
override func viewDidLoad() {
super.viewDidLoad()
opciones_autocompletado()
txt_ciudadU.delegate = self
}
func opciones_autocompletado(){
txt_provinciaU.inlineMode = true
txt_ciudadU.inlineMode = true
Services.objServices.ServProv().then{
data -> Void in
let res = JSON(data)
let status = res["status"].boolValue
if(status){
let provincias_ws = res["data"]["provincias"] //How to make this variable global
}else{
let error = res["error"]["error_data"].stringValue
print(error)
}
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}.catch{
error -> Void in
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
print(provincias_ws)
}
}
Thank you in advance
One can create a "global" variable (meaning it can be access from anywhere in the project) by defining it outside of the class block.
However, it looks like what you want is a class instance variable.
Your text field txt_ciudadU is already an example of an instance variable or property of the class ViewCtrl.
This would be the same, just without #IBOutlet weak.
So would probably be (assuming it's a String):
var provincias_ws: String?
Note the ? declares it as an optional value because it will not be set until it gets set inside your block. You could also initialize it to some default or empty value (e.g. var provincias_ws = "") or you could use ! to indicate you won't be trying to unwrap it when it's nil, although it's better not to rely on that because it is error-prone. What you choose to do depends on your needs.
And you would put that at the same hierarchical level as your text field variable (i.e. right above or below it, although it could go anywhere really, as long as it's not inside a function or another block).
And you would get rid of the current let next to provincias_ws and prepend it with self. i.e.:
self.provincias_ws = res["data"]["provincias"]
The self. is necessary because it's inside a block and the compiler needs that for context.