Difference between "\(string)" and string? - swift

callfunc(string: "\(string)")
callfunc(string: string)
I am calling the same function with same string value but different approach....
let me know what is the difference in it? and also I want to know in terms of memory consumption.

there is no difference, "\()" is used if your string is something like
let someInt: Int = 20
print("my integer is \(someInt)") //"my integer is 20"
i.e. not String in first place.
there is no memory difference because String in Swift is not reference type, it is Struct, so you pass copy of string to your callfunc, not reference to it.

There's a difference when your string is implicitly unwrapped optional. Consider example:
func some(string: String)
{
print(string)
}
let string: String! = "s"
some(string: string)
some(string: "\(string)")
The output will be:
s
Optional("s")

callfunc(string: string)
In the above syntax its a normal function call with a string.
callfunc(string: "(string)")
But here when we will pass "(string)" as a parameter, internally "(string)" creates a new string and pass it as a parameter. So in that particular point of time the memory will go high because of memory allocation for the string, which will again deallocated immediately.
Normally you won't be able to observe it with a small string, but if you will convert an image into base64 and try to pass it as a string. you can able to see the difference.
Apart from that there is no difference in functionality.

Related

Why does the behaviour of LocalizedStringKey depend on whether I pass a string interpolation to its initialiser?

While trying to answer this question, I found a strange behaviour.
Text(LocalizedStringKey("Hello \(Image(systemName: "globe"))"))
displays a globe, but
Text(LocalizedStringKey("Hello {world}".replacingOccurrences(of: "{world}", with: "\(Image(systemName: "globe"))")))
Text(LocalizedStringKey("Hello" + "\(Image(systemName: "globe"))"))
displays "Hello" followed by a jumble of SwiftUI's internal jargon mess.
An even more minimal example would be:
let x = "\(Image(systemName: "globe"))"
print(LocalizedStringKey.init(x))
print(LocalizedStringKey.init("\(Image(systemName: "globe"))"))
The values I'm passing to LocalizedStringKey.init should be the same, both "\(Image(systemName: "globe"))", but The first prints
LocalizedStringKey(key: "%#", hasFormatting: true, arguments: [...])
and the second prints
LocalizedStringKey(key: "Image(provider: SwiftUI.ImageProviderBox<SwiftUI.Image.(unknown context at $7ff91ccb3380).NamedImageProvider>)", hasFormatting: false, arguments: [])
It appears that LocalizedStringKey.init changes its behaviour depends on whether the arguments I pass is an (interpolated) string literal or not.
As far as I can see, the two calls to LocalizedStringKey.init are calling the same initialiser. There is only one parameter-label-less initialiser in LocalizedStringKey, which takes a String.
If there were also an initialiser that takes a LocalizedStringKey, the results would be much more understandable. LocalizedStringKey has custom string interpolation rules, and one specifically for Image, after all. But this is the only initialiser with no parameter labels as far as I know.
It would also be somewhat understandable if the initialiser's parameter is #autoclosure () -> String. If the expression I pass in is lazily evaluated, the method might be able to "peek inside" the closure by some means unknown to me. But the parameter isn't an auto closure.
What seems to be happening here is that the compiler is creating a LocalizedStringKey with key being that same pattern as the interpolation you passed in, even though the parameter is a String!
What is actually going on here? Did I miss a hidden initialiser somewhere?
TL;DR: the behaviour you're seeing comes from ExpressibleByStringInterpolation. But read on for more fun!
LocalizedStringKey becomes easier to understand if you think of it purely as a convenience to allow SwiftUI interface elements to be localizable "for free" when using string literals. There's only one real time you'd use it directly.
Consider Text. There are two relevant initializers:
init(_ key: LocalizedStringKey, tableName: String? = nil, bundle: Bundle? = nil, comment: StaticString? = nil)
which will attempt to localize the text passed in, and
init<S>(_ content: S) where S : StringProtocol
which will display the string without altering it.
If you call Text("Hello"), which initializer is used?
String literals conform to StringProtocol, but LocalizedStringKey is also ExpressibleByStringLiteral. The compiler would not know which one to choose.
To get "free" localization, the StringProtocol initializer is marked with #_disfavoredOverload, which tells the compiler to assume that the string literal is a LocalizableStringKey rather than a String.
Therefore, Text("Hello") and Text(LocalizedStringKey("Hello")) are equivalent.
let string = "Hello"
Text(string)
In this case, there is no conflict - the compiler uses the StringProtocol initializer and the string is not localized.
What does this have to do with your question? LocalizedStringKey is also ExpressibleByStringInterpolation, which is where your "hidden initializer" comes from. But like the examples above, this only comes into play if you are initializing it with a single, interpolated string.
Text("Hello \(Image(systemName: "globe"))")
You're passing an interpolated string, so the compiler can deal with it and add the image into the interpolation.
Text("Hello {world}".replacingOccurrences(of: "{world}", with: "\(Image(systemName: "globe"))"))
Here, replacingOccurrences(of: is evaluated first, meaning your argument is a String, which is not treated as a LocalizedStringKey expressed-via-string-interpolation. You're essentially seeing the description of the image.
A similar thing happens with the example with + in it. That implicitly makes a String, so you lose the special image interpolation that LocalizedStringKey gives you.
For your last code example:
let x = "\(Image(systemName: "globe"))"
print(LocalizedStringKey.init(x))
print(LocalizedStringKey.init("\(Image(systemName: "globe"))"))
x is a string containing a description of the image. Remember, only LocalizedStringKey has the magic power to actually understand and represent Image. Any other string interpolation will fall back to the description of the interpolated object.
The first initializer is passing a string (which is treated as a key, that's the only time you'd really directly use LocalizedStringKey, if you were generating keys at run time and wanted to use them for lookup).
The second initializer is using ExpressibleByStringInterpolation and is using LocalizedStringKey.StringInterpolation to insert images into its internal storage, which can then get rendered by Text.

Get codable's property names as strings in swift

Edit
let disucssionMessageTimestampKey = DiscussionMessage.CodingKeys.messageTimestamp.stringValue gives an error:
'CodingKeys' is inaccessible due to 'private' protection level
I have a message structure defined like this:
struct DiscussionMessage: Codable {
let message, userCountryCode, userCountryEmoji, userName, userEmailAddress: String
let messageTimestamp: Double
let fcmToken, question, recordingUrl, profilePictureUrl: String?
}
I want to define a variable disucssionMessageTimestampKey whose value will be messageTimestamp. I want to use disucssionMessageTimestampKey variable in the following query:
messagesReference.queryOrdered(byChild: "messageTimestamp").queryStarting(atValue: NSDate().timeIntervalSince1970).observe(.childAdded)
So that I don't have to hardcode the string value ("messageTimestamp") of the variable name.
Now I know I could just do let disucssionMessageTimestampKey: String = "messageTimestamp". But this is again prone to errors. So I was wondering if there was a way that I could get the string value messageTimestamp without having to define it anywhere.
By something like this (I know this won't work but just to give an idea of what I am looking for)
let disucssionMessageTimestampKey: String = String(describing: DiscussionMessage.messageTimestamp) // Will store disucssionMessageTimestampKey = "messageTimestamp"
Also, would it be possible to completely define the key values first as strings and then use those as variable names in the actual codable object? I.e. first define let disucssionMessageTimestampKey: String = "messageTimestamp", and then use the variable disucssionMessageTimestampKey to define what the property (messageTimestamp) of the codable object should be called. (This is low priority but curious and seems related to the question at hand)

What is the Different between String(describing: Int) vs String(Int)?

I need to convert an Integer value to String. I made a variable with an Integer value and then I have printed that using print. What is the different between the below approaches?
var word = "Count is "
var count = 100
print(word+String(describing: count)); // Count is 100
print(word+String(count)); // Count is 100
Your question is actually unnecessary because if all you want to do here is print, you can just do it directly:
print("Count is", count) // Count is 100
That's because print takes a variadic parameter and inserts space as the default separator.
However, let's answer the question anyway.
Answer
It's the difference between coercion and representation.
Coercion. Certain types can be changed into certain other types. You can change a Double to an Int and an Int to a Double. You can change an Int to a String and a String (maybe) to an Int. This is possible because the second type has an initializer whose parameter is the first type. This is something you would do in your actual program, like this:
let sum : Int = x + y
self.myLabel.text = "Your total is \(String(sum))"
Representation. For debugging purposes, all types can benefit from being representable as a string. Suppose you have a Person type. You can't change a Person to a String and vice versa — that makes no sense — but you would surely like to be able to print a Person to the console to see if you are getting the right results. This is possible because the Person type itself supplies a printable description. This is something you would do for debugging purposes:
let p = Person(first:"Matt", last:"Neuburg")
print("p is \(String(describing:p))")
Comments
Comment 1. This distinction is fairly new in Swift. It used to be that String(...) was used to express both notions. But the powers that be realized that that was a confusing conflation of the two distinct mechanisms. So nowadays, an attempt to write String(p) will fail, whereas earlier it would have succeeded. String has no Person initializer, so String(p) is forbidden; you now have to say explicitly that you are describing, not coercing.
Comment 2. The need to print a description is so obviously common that you do not have to pass thru String(describing:) if all you want to do is log the object itself. You can write print(p), because this is a shorthand for print(String(describing:p)). Similarly, string interpolation calls String(describing:) for you, so you can write print("p is \(p)"). And, as I said at the outset, print takes a variadic parameter and inserts space as the default separator, so you can write print("p is", p).
Reading the docs might help!
Here's an excerpt from String.init(describing:)
Use this initializer to convert an instance of any type to its
preferred representation as a String instance. The initializer creates
the string representation of instance in one of the following ways,
depending on its protocol conformance:
If instance conforms to the TextOutputStreamable protocol, the result is obtained by calling instance.write(to: s) on an empty string
s.
If instance conforms to the CustomStringConvertible protocol, the result is instance.description.
If instance conforms to the CustomDebugStringConvertible protocol, the result is instance.debugDescription.
An unspecified result is supplied automatically by the Swift standard library.
Int conforms to CustomStringConvertible, so String(describing: someInt) is the same as someInt.description.
Now let's look at String(someInt), this calls this initializer:
public init<T>(_ value: T) where T : LosslessStringConvertible
Int also conforms to LosslessStringConvertible. In fact, LosslessStringConvertible inherits from CustomStringConvertible. LosslessStringConvertible specifies only one requirement - an initializer that takes a string.
The docs for init<T>(_:) says this:
Creates an instance from the description of a given LosslessStringConvertible instance.
Even though description is not in a monospace font, I am quite sure (though not 100%) that this refers to the description property of CustomStringConvertible.
Assuming that init<T>(_:) indeed returns the description, String(describing: someInt) always returns the same value as String(someInt).
Note that this does not hold true for all types. It only holds true for types that conforms to LosslessStringConvertible but not TextOutputStreamable. If you have a type that conforms to both, results can be different:
struct A: TextOutputStreamable, LosslessStringConvertible {
init?(_ description: String) { }
init() { }
var description: String {
return "foo"
}
func write<Target>(to target: inout Target) where Target : TextOutputStream {
target.write("bar")
}
}
String(describing: A()) // bar
String(A()) // foo

Swift: if is let redundancy

I just joined a project that has a lot of existing code. The previous programmer was perhaps unfamiliar with Swift or began development in the early stages of the Swift language. They seemed to be using the if let statement in an odd way. They seemed to want to use the statement as a if is let. Before I edit the code I would like to know if there is any valid use for this:
// In JSON parser
if value is String, let string = value as? String {
document.createdBy = string
}
First checking if value is of type String seems redundant to me. Doesn't Swift check for this in the let string = value as? String portion of the statement?
QUESTION
Why would this need to be checked twice? Or would there be a reason for this?
You're correct, this is redundant. If value is not a string, then value as? String would return nil, and the conditional binding would fail.
To check the type, and not use the casted result:
if value is String {
// Do something that doesn't require `value` as a string
}
To check the type and use the result:
if let value = value as? String { // The new name can shadow the old name
document.createdBy = value
}
Doing both makes no sense.

Convert or cast object to string

how can i convert any object type to a string?
let single_result = results[i]
var result = ""
result = single_result.valueForKey("Level")
now i get the error: could not assign a value of type any object to a value of type string.
and if i cast it:
result = single_result.valueForKey("Level") as! String
i get the error:
Could not cast value of type '__NSCFNumber' (0x103215cf0) to 'NSString' (0x1036a68e0).
How can i solve this issue?
You can't cast any random value to a string. A force cast (as!) will fail if the object can't be cast to a string.
If you know it will always contain an NSNumber then you need to add code that converts the NSNumber to a string. This code should work:
if let result_number = single_result.valueForKey("Level") as? NSNumber
{
let result_string = "\(result_number)"
}
If the object returned for the "Level" key can be different object types then you'll need to write more flexible code to deal with those other possible types.
Swift arrays and dictionaries are normally typed, which makes this kind of thing cleaner.
I'd say that #AirSpeedVelocity's answer (European or African?) is the best. Use the built-in toString function. It sounds like it works on ANY Swift type.
EDIT:
In Swift 3, the answer appears to have changed. Now, you want to use the String initializer
init(describing:)
Or, to use the code from the question:
result = single_result.valueForKey("Level")
let resultString = String(describing: result)
Note that usually you don't want valueForKey. That is a KVO method that will only work on NSObjects. Assuming single_result is a Dictionary, you probably want this syntax instead:
result = single_result["Level"]
This is the documentation for the String initializer provided here.
let s = String(describing: <AnyObject>)
Nothing else is needed. This works for a diverse range of objects.
The toString function accepts any type and will always produce a string.
If it’s a Swift type that implements the Printable protocol, or has overridden NSObject’s description property, you’ll get whatever the .description property returns. In the case of NSNumber, you’ll get a string representation of the number.
If it hasn’t, you’ll get a fairly unhelpful string of the class name plus the memory address. But most standard classes, including NSNumber, will produce something sensible.
import Foundation
class X: NSObject {
override var description: String {
return "Blah"
}
}
let x: AnyObject = X()
toString(x) // return "Blah"
"\(x)" // does the same thing but IMO is less clear
struct S: Printable {
var description: String {
return "asdf"
}
}
// doesn't matter if it's an Any or AnyObject
let s: Any = S()
toString(s) // reuturns "asdf"
let n = NSNumber(double: 123.45)
toString(n) // returns "123.45"
n.stringValue // also works, but is specific to NSNumber
(p.s. always use toString rather than testing for Printable. For one thing, String doesn’t conform to Printable...)
toString() doesn't seem to exist in Swift 3 anymore.
Looks like there's a failable initializer that will return the passed in value's description.
init?(_ description: String)
Docs here https://developer.apple.com/reference/swift/string/1540435-init