Home > Back-end >  Swift: string with placeholder for variables to be passed in during 'print()'
Swift: string with placeholder for variables to be passed in during 'print()'

Time:09-25

EDIT: Ok, the responses have helped me figure out the terminology I need to be using to more clearly describe this question. Thank you!

Transitioning from Python to Swift, I'm trying to replicate some educational exercises, and there's a process Python can do with string interpolation that uses a sort of a generic "placeholder" space in a string literal that can be passed a value at a later time.

It would look something like:

color = "purple"
size = "large"

description = "My car is the {} one over there."

print(description.format(color))
print(description.format(size))

and would return:

> My car is the purple one over there.
> My car is the large one over there.

So:

  • The sentence string is assigned to a variable
  • It has a "placeholder" for variables with the {}
  • Invoking print() and attaching the format() function to the sentence variable allows it to pass in another variable to be inserted as a string literal at the "placeholder"

For the life of me, I cannot figure out how to replicate this particular method of string interpolation in Swift. In particular, attempting to mimic Python's {} with \() isn't allowed, [EDIT: looks like %@ is the answer for that issue] and trying to attach functions to variables in print() when strings are involved is causing a bunch of different issues depending on what I try.

How can I perform the Python task in Swift? Am I missing something simple and just don't know the terminology [likely] which is making this more difficult than it should be, or is this kind of string interpolation not something that Swift can do?

EDIT 2: Attached snippets of code I'm working with

let hilarious = false
let joke_evaluation = "Isn't that joke so funny?! %@"

print(joke_evaluation)
print(String(format: joke_evaluation, arguments: [hilarious])) // Extra argument 'arguments' in call
struct carStuff {
    let color = "red"
    let size = "large"
    let description = "The %@ car."
    init(format: String, arguments: [CVarArg]) // Initializer requires a body
}

print(String(format: carStuff.description, arguments: carStuff.color)) // Extra argument 'arguments' in call

Code Snippet

CodePudding user response:

Normally in Swift you can just use String Interpolation \(). This is just like Python's f-strings.

let color = "purple"
let size = "large"
print("My car is the \(color) one over there.") /// My car is the purple one over there.
print("My car is the \(size) one over there.") /// My car is the large one over there.

That should be fine for most cases. But if you want an explicit placeholder, check out init(format:_:).

let color = "purple"
let size = "large"
let description = "My car is the %@ one over there."
print(String(format: description, color)) /// My car is the purple one over there.
print(String(format: description, size)) /// My car is the large one over there.

Use %@ for String values like color. Here's the full list of placeholders, in case you want format an Int or something else.


Edit: Based off OP's code snippets

Your first code snippet compiles fine for me. However, %@ only supports String and other objects that bridge to obj-cBools like hilarious don't work. You need to convert it to a String first.

let hilarious = false
let joke_evaluation = "Isn't that joke so funny?! %@"
print(String(format: joke_evaluation, arguments: [String(hilarious)])) /// Need to convert `hilarious` to a String first

Result:
Isn't that joke so funny?! false

Your second code snippet has a couple problems. I think you might be confusing Apple structs like String with custom, made-by-you structs like CarStuff. But anyway, if you want to make your own struct CarStuff (again, why?), here's how:

  • carStuff is a struct, so it should be capitalized to CarStuff (just good conventions).
  • The purpose of an initializer is to initialize all of the struct's properties.
  • structs get initializers for free, so you don't need to define one. But if you want to make your own, you need to make sure that all the properties are initialized once init() is called. See the docs for more information.
  • Your initializer, init(format: String, arguments: [CVarArg]), doesn't have a body. This is a syntax error - you need the curly braces {}.
  • Also, why is your initializer defined as init(format: String, arguments: [CVarArg])? The properties of CarStuff are color, size, and description. What does format and arguments have to do with those? Remember: The purpose of an initializer is to initialize all of the struct's properties.
init(format: String, arguments: [CVarArg]) {
    /**
     initialize the struct's properties here.
     but, you already provided default values to the properties...
     ... for example, `let color = "red"`
     so you don't even need your custom init — Swift does it automatically for you.
     And besides, what is `format` and `arguments` for?
     Just remove this `init` entirely.
     */
}

Anyway, all of that was just some extra info, not really related to your original question. The real problem is that there are 2 very similar String initializers:

The first one requires you to pass in an Array. The second one takes in a variadic number of parameters, so you can just directly pass in your variable to be substituted.

struct CarStuff {
    let color = "red"
    let size = "large"
    let description = "The %@ car."
}
let carStuff = CarStuff() /// initialize a `CarStuff` (using Swift's auto-generated initializer)
print(String(format: carStuff.description, arguments: [carStuff.color])) // init(format:arguments:) requires an array
print(String(format: carStuff.description, carStuff.color)) // init(format:_:) lets you omit `arguments` and directly pass in your arguments

Result:
The red car.
The red car.

  • Related