Swift: How to add a class method in 'String" extension - swift

I want to add a class function into extension:
extension String {
class func test () {
}
}
I get the error: Class methods are only allowed within classes; use 'static' to declare a static method
Or how should i call " String.test()"
But for NSString
extension NSString {
class func aaa () {
}
}
no errors.
If i add static keyword:
extension String {
static func aaa () {
self.stringByAppendingString("Hello")
}
}
Got: Expression resolves to an unused function,
So how should i add a class function also want to use self. method.
EDIT: This works!
extension String {
static func aaa (path:String) -> String {
return path.stringByAppendingString("Hello")
}
}
but about #lan's answer:
mutating func bbb(path: String) {
self += "world"
}
When i type it appears like this:
String.bbb(&<#String#>)
String.bbb(&"nihao")
Cannot invoke 'bbb' with an argument list of type '(String)'

Class and static functions are not called on an instance of a class/struct, but on the class/struct itself, so you can't just append a string to a class.
Apple Documentation:
Within the body of a type method, the implicit self property refers to
the type itself, rather than an instance of that type.
You can, however, append a string to a variable instance of a String using the mutating keyword:
extension String {
mutating func aaa() {
self += "hello"
}
}
let foo = "a"
foo.aaa() // ERROR: Immutable value of type 'String' only has mutating members named 'aaa'
var bar = "b"
bar.aaa() // "bhello"
If you are trying to use a pointer to a string as a parameter, you can use the inout keyword to alter the inputed string:
extension String {
static func aaa(inout path: String) {
path += "Hello"
}
}
var foo = "someText"
String.aaa(&foo)
foo //someTextHello

While correct, it's somewhat atypical to see a mutating member added to a String extension as shown in Ian's answer. Strings (and value types in general) are meant to be immutable so the only way to use a mutating method is to declare instances var at the call site. Most of the time in your code you should be using let constants.
As such, it is much more common to extend structs to return new instances. So this is typical:
extension String {
func appending(_ string: String) -> String {
return self + string
}
}
and then at the call site:
let hello = "Hello, "
let helloWorld = hello.appending("World!")
You'll note of course that I'm not using static at all. That's because appending(_:) needs to use the current instance value of the String we're appending to, and class/static do not refer to instances and therefore do not have values.

"Within the body of a type method, the implicit self property refers to the type itself, rather than an instance of that type."
Thus when you extend a type by adding a type method you can only call other type methods through self. If you want to call an instance method you need to create an instance and call a method on that.

Related

Swift: Creating array of objects with generic type parameter

I have a type called Setting that takes a generic type parameter as such:
Setting<T>
Every setting contains a value that can be an Int32, String, Bool, or a custom object type, etc. Here is some of the full implementation of Setting:
class Setting<T> {
var key:String?
var defaultValue:T?
//...
}
This all works with various type params as expected, however, now there is a requirement for a collection that contains multiple Setting objects that could have various type parameters. When I declare an array variable of type [Setting], obviously the compiler expects a type which is unknown at runtime.
I've tried using a protocol and an extension on the types that could be used for the generic type parameter such as this:
protocol SettingProtocol {
func getType() -> Self.Type
}
extension Int32:SettingProtocol {
func getType() -> Int32.Type {
return Int32.self
}
}
extension String:SettingProtocol {
func getType() -> String.Type {
return String.self
}
}
//...
and declaring my array as
var settings = [Setting<SettingProtocol>]()
but this does not work when I try to append a Setting instance to the array as follows:
var newSetting = Setting<String>()
newSetting.setDefaultValue(value: "SomeString")
settings?.append(newSetting) // compile error here
and results in the following compiler error:
Cannot convert value of type 'Setting<String>' to expected argument type 'Setting<SettingProtocol>'
Also, using the protocol/extension route might require an extension on every type that might be encountered when building these objects which seems really clunky.
I feel like there should be a way to accomplish this. Also hoping that when I pull these items out of the array that I can avoid a lot of type checking.
Can anyone offer any advice?
Change
class Setting<T>
to
class Setting<T:SettingProtocol>
and try compiling.
Actually, you can't define:
var settings = [Setting<SettingProtocol>]()
because the generic type of Setting must be one of the concrete types but not the protocol itself. For example, you could declare it as:
var settings = [Setting<String>]() // since you already implemented extension String:SettingProtocol { ...
Therefore you could append objects of type Setting<String>, however that's not what are you looking for, you need settings to be a heterogeneous container.
So what you could do is:
class Setting {
var key:String?
var defaultValue:SettingProtocol?
}
protocol SettingProtocol { }
extension Int32:SettingProtocol {}
extension String: SettingProtocol {}
At this point, you declared defaultValue to be of type SettingProtocol, without the need of dealing with a generic.
Therefore:
var newStringSetting = Setting()
newStringSetting.defaultValue = "My String"
settings.append(newStringSetting)
var newInt32Setting = Setting()
newInt32Setting.defaultValue = Int32(100)
settings.append(newInt32Setting)
for setting in settings {
print(setting.defaultValue)
// Optional("My String")
// Optional(100)
}

How to reference an attribute as a method default in a Swift class? [duplicate]

In a Swift class, I want to use a property as a default parameter value for a method of the same class.
Here is my code :
class animal {
var niceAnimal:Bool
var numberOfLegs:Int
init(numberOfLegs:Int,animalIsNice:Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(animalIsNice:Bool = niceAnimal,numberOfLegs:Int) {
// I'll write my code here
}
}
The problem is that I can't use my niceAnimal property as a default function value, because it triggers me a compile-time error :
'animal.Type' does not have a member named 'niceAnimal'
Am I doing something wrong ? Or is it impossible in Swift ? If that's impossible, do you know why ?
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
I think for now you can only use literals and type properties as default arguments.
The best option would be to overload the method, and you can implement the shorter version by calling the full one. I only used a struct here to omit the initializer.
struct Animal {
var niceAnimal: Bool
var numberOfLegs: Int
func description(#numberOfLegs: Int) {
description(niceAnimal, numberOfLegs: numberOfLegs)
}
func description(animalIsNice: Bool, numberOfLegs: Int) {
// do something
}
}

Swift dynamicType does not work with generic function

Say I have a protocol:
protocol VehicleModel {...}
It is implemented by a number of different structs. (e.g. CarModel, TruckModel, etc.)
I have a generic method to get the vehicle's 'model identifier'.
func modelIdentifierForVehicle<V: VehicleModel>(vehicleType: V.Type) -> String {
return "\(vehicleType)"
}
If I call modelIdentifierForVehicle(CarModel.self) this returns "Car" just fine. But if I have a polymorphic collections of VehicleModel's and I try to call modelIdentifierForVehicle(model.dynamicType) on each of them, Xcode says "Cannot invoke 'modelIdentifierForVehicle' with argument list of type (VehicleModel.Type)" Why is this? And how can I work around it?
Since you're only converting vehicleType to a String in modelIdentifierForVehicle, I would argue why you need to use constrain V to VehicleModel, or even use generics at all:
func typeIdentifier(t: Any.Type) -> String {
return "\(t)"
}
let vehicles: [VehicleModel.Type] = [CarModel.self, TruckModel.self]
typeIdentifier(vehicles[0]) // CarModel
If there's a reason you need use a VehicleModel, assuming VehicleModel doesn't use Self or associated type requirements, you could do:
func modelIdentifierForVehicle(vehicleType: VehicleModel.Type) -> String {
return "\(vehicleType)"
}
If you're using Swift 2, you could instead use a protocol extension:
extension VehicleModel {
static var modelIdentifier: String {
return "\(self.dynamicType)"
}
}
// The array from earlier.
vehicles[1].modelIdentifier // TruckModel.Type

Swift function alias

How can I create an alias for a function in swift?
For example
I want to call
LocalizedString("key")
and it should call
NSLocalizedString("key", comment:"")
I saw typealias command but it looks like it works only for types.
Functions are named closures, so you can just assign a function to a variable:
let LocalizedString = NSLocalizedString
You can create pseudo-aliases for class/struct methods as well. Each method is actually a static (class) curried function, taking a class instance as its first parameter. So given a class:
class MyClass {
var data: Int
init(data: Int) {
self.data = data
}
func test() {
println("\(data)")
}
}
you can assign the test method to a variable:
let test = MyClass.test
and then invoke it as:
var instance = MyClass(data: 10)
test(instance)()
UPDATE
I've just realized that I missed one important detail in your question: you want to hide the comment parameter. And my proposed solution doesn't allow that, whereas #rintaro's solution does.
However I use a different approach for that: I create a String extension implementing a computed property:
extension String {
var localized: String {
return NSLocalizedString(self, comment: "")
}
}
and then I can just call it on any string variable or literal:
var string = "test_resource"
string.localized
"another_resource".localized
The shortest one is:
let LocalizedString = { NSLocalizedString($0, comment:"") }
But, it's actually a new function. Just wrapping NSLocalizedString.
Maybe you can use undocumented #transparent attribute. It inlines function call. see this topic on Developer Forum.
#transparent LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
But it's not recommended. Moreover, as long as my tests, all of following codes eventually emit exact the same LLVM IR code with -O optimization.
script1: with #transparent
import Foundation
#transparent func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
println(LocalizedString("key"))
script2: without #transparent
import Foundation
func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
println(LocalizedString("key"))
script3: Direct NSLocalizedString call
import Foundation
func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}
println(NSLocalizedString("key", comment:""))
All of above are inlined to perform direct NSLocalizedString call.
But, the following code emits different:
script4: Closure wrapping
import Foundation
let LocalizedString = { NSLocalizedString($0, comment:"") }
println(NSLocalizedString("key", comment:""))
It's also inlined, but additional refcount instruction to LocalizedString is inserted.
So, as a conclusion, you should simply use this:
func LocalizedString(key:String) -> String {
return LocalizedString(key, comment:"")
}

Using a property as a default parameter value for a method in the same class

In a Swift class, I want to use a property as a default parameter value for a method of the same class.
Here is my code :
class animal {
var niceAnimal:Bool
var numberOfLegs:Int
init(numberOfLegs:Int,animalIsNice:Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(animalIsNice:Bool = niceAnimal,numberOfLegs:Int) {
// I'll write my code here
}
}
The problem is that I can't use my niceAnimal property as a default function value, because it triggers me a compile-time error :
'animal.Type' does not have a member named 'niceAnimal'
Am I doing something wrong ? Or is it impossible in Swift ? If that's impossible, do you know why ?
I don't think you're doing anything wrong.
The language specification only says that a default parameter should come before non-default parameters (p169), and that the default value is defined by an expression (p637).
It does not say what that expression is allowed to reference. It seems like it is not allowed to reference the instance on which you are calling the method, i.e., self, which seems like it would be necessary to reference self.niceAnimal.
As a workaround, you could define the default parameter as an optional with a default value of nil, and then set the actual value with an "if let" that references the member variable in the default case, like so:
class animal {
var niceAnimal: Bool
var numberOfLegs: Int
init(numberOfLegs: Int, animalIsNice: Bool) {
self.numberOfLegs = numberOfLegs
self.niceAnimal = animalIsNice
}
func description(numberOfLegs: Int, animalIsNice: Bool? = nil) {
if let animalIsNice = animalIsNice ?? self.niceAnimal {
// print
}
}
}
I think for now you can only use literals and type properties as default arguments.
The best option would be to overload the method, and you can implement the shorter version by calling the full one. I only used a struct here to omit the initializer.
struct Animal {
var niceAnimal: Bool
var numberOfLegs: Int
func description(#numberOfLegs: Int) {
description(niceAnimal, numberOfLegs: numberOfLegs)
}
func description(animalIsNice: Bool, numberOfLegs: Int) {
// do something
}
}