I'm learning about extension in swift and I want to create a extension for String like the command .hasPrefix(), in that command we send a String, for test it I try this code:
extension String{
var teste:(String) { return "\(self) - \($1)" }
}
texto.teste("other String")
But not working, all I want to do is create a extension that we can send other values like .hasPrefix (that send a string inside) .hasSufix (send a string too), How can i do this?
var teste: String { ... } is a computed property, and computed
properties cannot take parameters.
You'll want to define an extension method:
extension String {
func teste(arg : String) -> String {
return "\(self) - \(arg)"
}
}
println("foo".teste("bar"))
// foo - bar
Related
This question already has answers here:
How can I change the textual representation displayed for a type in Swift?
(7 answers)
Closed 12 months ago.
I've made an class - call it Blah. It contains a string "value". So anytime someone makes a string out of it - I want it to appear as the value. So I try it:
let x = Blah( "hello world")
XCTAassertEqual("hello world", "\(x)")
out of the box - of course it doesn't work - I haven't told it how to do the rendering. So I do some googling and find that thing is "description" - so add this to Blah:
public var description = {get{return _value}}
and then the test becomes this:
let x = Blah( "hello world")
XCTAassertEqual("hello world", "\(x.description)")
XCTAassertEqual("hello world", "\(x)")
the first assert works, but the second is still giving me the whole object
so - more googling - I find that you can add custom string interpolations.. so I add this:
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Blah) {
appendLiteral( value.description)
}
}
and that achieves... well... absolutely nothing.
How do I change the way my objects are represented by string interpolation?
The property description you have seen belongs to the protocol CustomStringConvertible and is used when you want to convert an object to a string representation. So all you need to do is to conform to the protocol and description will be used in your tests
struct Blah: CustomStringConvertible {
let value: String
init(_ value: String) {
self.value = value
}
var description: String {
value
}
}
You were right about var description. However, you forgot to add the protocol CustomStringConvertible:
struct Blah {
let value: String
}
extension Blah: CustomStringConvertible {
var description: String { value }
}
let x = Blah(value: "Hello world")
print("Hello world: ", "\(x)")
Without the protocol, description is just a custom property.
I manage to get it to work.
class Blah {
let value: String
init(_ value: String) {
self.value = value
}
}
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Blah) {
appendLiteral(value.value)
}
}
func testBlah() {
let blah = Blah("Hello world")
XCTAssertEqual("Hello world", "\(blah)")
}
The test passes.
I want to have a function return a type which I can add a new function, but still can be generally recognized as String (still have all of String's methods and still can be received by any parameter that received String).
But when I try to derive a class from String, I get this error:
Inheritance from non-protocol, non-class type 'String'
Of course, I can instead use an extension to extend the existing String to add that function, but I felt that this will pollute the String with unnecessary and unrelated functions for general use.
For example, functions that I want to add might be like this:
class ImageUrl : String {
func getImage (callback: ((UIImage?)->Void)) { ... }
}
or like this:
class Base64 : String {
var image : UIImage { ... }
var data : Data { ... }
var string : String { ... }
}
Which will be confusing if I extend these functions to the main String type.
How can I do this in Swift? Or is there any workaround to this? Thanks.
You cannot inherit from String in Swift, because it is a struct. You can only add functionality by using an extension as you mentioned, but this will not let you use stored properties (computed properties as you wrote in the question are allowed).
However, a better approach for your need would be to use composition:
class Base64 {
let str: String
required init(value: String) {
self.str = value
}
}
Here you can add your desired functionality.
You could create a String extension using the fileprivate modifier to avoid polluting the global namespace. The functions would then only be available to code in the source file rather than the global namespace.
fileprivate extension String {
var image : UIImage { ... }
var data : Data { ... }
var string : String { ... }
}
I've got an issue where I'm calling a method with generic a generic parameter that works properly (at runtime, the parameter is visible to the method), however in the Xcode debugger, the parameter is "not there").
The definition of the method looks like this:
func updateCacheObject<T: PLObjectCacheable>(newCacheObject: T) throws -> T {
NSLog("CacheObject.cacheId: \(newCacheObject.cacheId)")
let cachedObject: T? = try self.getCacheObject(id: newCacheObject.cacheId)
...
Where the PLObjectCacheable is defined as:
protocol PLObjectCacheable {
var cacheId: String { get }
var isFullObject: Bool { get }
func mergeWithOldObject(_ oldObject: Self?) -> Self
static func getCacheIdForString(id: String) -> String
var cacheDate: Date { get }
}
and an implementation of a class that implements PLObjectCacheable is this:
struct FullTrack: PLObjectCacheable {
let id: String
var cacheId: String {
return self.id
}
var isFullObject: Bool {
return true
}
...
}
So, going back to the updateCacheObject method, when it's called, the first line dumps out the newCacheObject parameter's cacheId member variable.
This works fine.
However, if I place a breakpoint in the Xcode debugger anywhere in the method, Xcode can't "see" the newCacheObject parameter.
See the image:
Note that the Blue/Cyan square shows that the newCacheObject.cacheId was successfully logged.
The Red and purple squares show that the debugger can't find the newCacheObject variable.
Is it because it's a generic parameter? I'm confused.
Thanks.
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
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:"")
}