Providing a default binding to String of an enum with rawValue type of String [closed] - swift

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 days ago.
Improve this question
enum Tab : String {
case front, back
}
I would like to extend any enum that has raw value type of String to have an easily accessible bindingToString. Is there a way? Probably a protocol would have to be declared on any enum with String raw value type, but I have no idea how to do that. I'm looking for something like this:
enum Tab : String, BindableToString {
case front, back
}
var tab : Tab = .front
TextField(tab.bindingToString, ...)
However, we can create a Binding<Tab> extension to create a conversion to/from String:
extension Binding where Value == Tab {
var bindingToString : Binding<String> {
.init(get: { self.wrappedValue.rawValue }, set: { v in
if let newValue = Tab.init(rawValue: v) {
self.wrappedValue = newValue
}
})
}
}
This is almost fine, but first we need to explicitly create binding to Tab and then a binding to String, and we need to define this on every enum out there.
I was hoping we could somehow generalize this further so that we don't need to define explicitly a bindingToString on every such enum that has a raw value type of String; a protocol or a generic extension would do.
A solution utilizing the RawRepresentable idea from #loremipsum based on the proxy() func:
extension Binding where Value : RawRepresentable<String> {
var bindingToString : Binding<String> {
.init {
self.wrappedValue.rawValue
} set: { value in
if let newValue = Value(rawValue: value){
self.wrappedValue = newValue
}
}
}
}

Related

How can i import view which bring a closure with itself in SwiftUI? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 months ago.
Improve this question
Here is my code, it does not work:
struct ContainerView<MyContent: View>: View {
let myContent: () -> MyContent
#State private var myValue: String = ""
var body: some View {
myContent() { value in
myValue = value
}
}
}
I want make this logic works, when I am bringing my view as myContent to body, I want be able to bring a string value with it like in code! I am not looking for reaching my goal with other ways, the goal of this question is be able to access value like in code as clouser.
Warning: I'm not sure what the use-case of this is -- it's not clear what problem is trying to be solved here, but there's likely a better fit that trying to make something like this work. This answer, though, does solve the compilation errors presented above.
Your syntax inside body implies that you want a trailing closure on myContent, but it's not defined in the signature. This would solve the compilation error:
struct ContainerView<MyContent: View>: View {
let myContent: (#escaping (String) -> Void) -> MyContent
#State private var myValue: String = ""
var body: some View {
Text(myValue)
myContent() { value in
myValue = value
}
}
}
Call site:
ContainerView { closure in
Button("Test") {
closure("Value")
}
}

How to access the variable out of scope of `if let` in swift [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 months ago.
Improve this question
I am new to swift that I would like to use the variable funcReturn out of if let scope. anyway better way for me to do it other than using var to declare the variable?
if let funcReturn = localFunction() {
print(funcReturn)
}
print(funcReturn == "local function return value")
// i think I can do something like this but I do not need to change the value of funcReturn
if var funcReturn = localFunction() {
print(funcReturn)
}
print(funcReturn == "local function return value")
if let and if var only define the new variable inside the braces that follow. That is the way the language works.
Consider this code:
func foo() {
let aVar: Int? = 3
if let bar = aVar {
// bar is defined
}
// bar is not defined.
}
The same is true with an if var:
func foo() {
let aVar: Int? = 3
if var bar = aVar {
// bar is defined
}
// bar is not defined.
}
(The compiler will tell you that your var was never mutated.) But at the "// bar is not defined." comment, the variable bar still won't exist.
As the other poster says, guard works the opposite way:
func foo() {
let aVar: Int? = 3
guard let bar = aVar else {
return
}
// bar is defined for the rest of the function.
}
guard requires that you leave the current scope if it fails. (Usually with a return, although you can also do other things like break or continue from an outer loop.
The variable will only be in scope for the function. Another way to do it is a guard statement

Get the coding key for a keypath in Swift [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
What I have: A codable struct with different properties.
What I want: A function, where I can get the exact name of the property when it is encoded in a Json. I think the most promising approach is using Keypath, but I have no idea how and whether it is possible at all. Thanks!
There is no way to do this out of the box, since there's no 1-1 mapping between properties of a Codable type and its coding keys, since there might be properties that aren't part of the encoded model or properties that depend on several encoded keys.
However, you should be able to achieve your goals by defining a mapping between the properties and their coding keys. You were on the right track with KeyPaths, you just need to define a function that takes a KeyPath whose root type is your codable model and return the coding key from said function.
struct MyCodable: Codable {
let id: Int
let name: String
// This property isn't part of the JSON
var description: String {
"\(id) \(name)"
}
enum CodingKeys: String, CodingKey {
case name = "Name"
case id = "identifier"
}
static func codingKey<Value>(for keyPath: KeyPath<MyCodable, Value>) -> String? {
let codingKey: CodingKeys
switch keyPath {
case \MyCodable.id:
codingKey = .id
case \MyCodable.name:
codingKey = .name
default: // handle properties that aren't encoded
return nil
}
return codingKey.rawValue
}
}
MyCodable.codingKey(for: \.id)

Swift protocol covariant properties [duplicate]

This question already has answers here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
(3 answers)
Closed 5 years ago.
Can anyone explain why this doesn't compile?
// In MyViewKit.framework
protocol UserRenderable {
var name : String { get }
}
protocol PostRenderable {
var title: String { get }
var author: UserRenderable { get }
}
// In MyDataKit.framework
struct User {
let id: String
let name : String
}
struct Post {
let id: String
let title: String
let author: User
}
// In MyApp
extension User : UserRenderable {}
extension Post: PostRenderable {}
But this (below) does? Apparently, read-only property requirements in a protocol can't be satisfied by a property which conforms to them (according to the answers to this question.)
What is typealias doing here?
// In MyViewKit.framework
protocol UserRenderable {
var name : String { get }
}
protocol PostRenderable {
var title: String { get }
var author: UserRenderable { get }
}
// In MyDataKit.framework
struct User {
let id: String
let name : String
}
struct Post {
let id: String
let title: String
let author: User
}
// In MyApp
extension User : UserRenderable {}
extension Post: PostRenderable {
// what is type alias doing here? Note: there are no associated types.
typealias User = UserRenderable
}
UPDATE Just clarifying why IMO this isn't a duplicate to Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?. I understand the limitation in the Swift language: I'm trying to also understanding whether or not this is a valid workaround.
A typealias has nothing to do with associated types in this particular instance. Typealias is just a way to let the compiler know what is the associated type in a protocol if it cannot be inferred, but a typealias can be used in different contexts as well.
Your second example compiles, because you change User from a concrete type to a typealias for UserRenderable in your Post extension. If you check the type of author in Xcode, you'll see that its type is actually UserRenderable after the extension rather than User. Due to the typealias, inside Post, User won't refer to the User struct, but rather it will be UserRenderable due to the local overwrite you did with the typealias.

Working with generic constraints

I know this question has been asked before but I have no idea how to solve this current problem. I have defined a protocol MultipleChoiceQuestionable with an associatedtype property:
protocol Questionable {
var text: String {get set}
var givenAnswer: String? {get set}
}
protocol MultipleChoiceQuestionable: Questionable {
associatedtype Value
var answers: Value { get }
}
struct OpenQuestion: Questionable {
var text: String
var givenAnswer: String?
}
struct MultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [String]
var text: String
var givenAnswer: String?
var answers: Value
}
struct NestedMultipleChoiceQuestion: MultipleChoiceQuestionable {
typealias Value = [MultipleChoiceQuestion]
var text: String
var answers: Value
var givenAnswer: String?
}
Types which conform to this protocol are saved in an array as Questionable like so:
// This array contains OpenQuestion, MultipleChoiceQuestion and NestedMultipleChoiceQuestion
private var questions: [Questionable] = QuestionBuilder.createQuestions()
Somewhere in my code I want to do something like:
let question = questions[index]
if let question = question as? MultipleChoiceQuestionable {
// Do something with the answers
question.answers = .....
}
This is not possible because Xcode warns me: Protocol MultipleChoiceQuestionable can only be used as a generic constraint. I've been searching around on how to solve this issue since generics are quite new for me. Apparently Swift doesn't know the type of the associatedtype during compile time which is the reason this error is thrown. I've read about using type erasure but I don't know if that solves my problem. Maybe I should use generic properties instead or perhaps my protocols are defined wrong?
If the action you want to apply to your sub-protocol objects does not rely on the associated type (i.e. neither has the a generic parameter nor returns the generic type) you can introduce an auxiliary protocol which just exposes the properties/methods you need, let your type conform to that protocol, and declare the question in terms of that protocol.
For example, if you just want to know some info about the question:
protocol MultipleChoiceInfo {
var numberOfAnswers: Int { get }
}
extension MultipleChoiceQuestion: MultipleChoiceInfo {
var numberOfAnswers: Int { return answers.count }
}
// do the same for the other multiple-choice types
Then you can access the questions through the new protocol like this:
let question = questions[index]
if let info = question as? MultipleChoiceInfo {
print(info.numberOfAnswers)
}
As I said, if you cannot provide an abstract (non-generic) interface then this won't work.
EDIT
If you need to process the generic data inside your questions you can extract the logic depending on the concrete generic type into another "processing" type which provides an interface to your questions. Each question type then dispatches its data to the processor interface:
protocol MultipleChoiceProcessor {
func process(stringAnswers: [String])
func process(nestedAnswers: [MultipleChoiceQuestion])
}
protocol MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor)
}
extension MultipleChoiceQuestion: MultipleChoiceProxy {
func apply(processor: MultipleChoiceProcessor) {
processor.process(stringAnswers: answers)
}
}
Just create a type conforming to MultipleChoiceProcessor and do the type-check dance again:
if let proxy = question as? MultipleChoiceProxy {
proxy.apply(processor:myProcessor)
}
As an aside, if you don't have more protocols and structs in your real application, you might also just ditch the protocol stuff altogether... for this kind of problem it seems a bit over-engineered.