Is it possible to access the getter of a property, so that it can be passed along to functions like filter/map/reduce?
For example, assuming I have a User entity:
struct User {
let firstName: String
let lastName: String
let isUnderaged: Bool
func fullName() -> String {
return "\(firstName) \(lastName)"
}
}
let users: [User] = [] // or some computed result
If I need to filter all users that are underaged, then I need to create a temporary, anonymous, closure that only returns the isUnderaged value:
let underagedUsers = users.filter { $0.isUnderaged }
Similar, if I want to collect all the given names:
let givenNames = users.map { $0.firstName }
, I have to create another anonymous closure.
The fullName() method, on the other hand can be nicely passed along:
let allNames = users.map(User.fullName)
#selector accepts the #selector(getter: User.isUnderaged) form (User would need to be a class for this to work, just used it as example).
Does Swift supports something like this - passing getters to functions that allow closures?
Searching for something similar to this users.filter(User.isUnderaged) - this currently doesn't compile as isUnderaged cannot be accessed without an instance.
Starting with Swift 5.2, this is now possible, thanks to this Swift Evolution proposal.
Basically, key paths can now be used in places where functions are needed, like map/filter, etc.
For the example from the question, the usage would be along the lines of:
users.filter(\.isUnderaged)
users.map(\.firstName.count) // yes, chaining is also permitted
Related
I would like to make my class in Swift iterable.
My goal is to be able to create a class called Contact that holds properties such as the givenName, familyName, and middleName, like iOS CNContact. I would like to be able to have a class function that compares two instances of the class Contact, and finds which property the two contact objects have that match, so that say if both contacts have the same value for the givenName property, then the class function returns the result.
Here is a sample code:
class Contact {
static func compare(left: Contact, right: Contact) {
for property in left.properties {
if property == right.property {
// match is found
}
}
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
private var properties = [givenName, familyName, middleName]
}
let left = Contact()
let right = Contact()
Contact.compare(left: left, right: right)
I found posts that used mirroring and reflection, but I want to use Sequence and IteratorProtocol. I suspect there is already the ability to do exactly what I want to do. It seems to be a logical need that would arise.
What is the way to do this that has a balance between simplicity and the ability to address common needs to iterate through the instance properties of a class. An enumeration can be declared with given has values. Is there a way to make that work for this purpose? Is there a protocol that a class can use that assigns a hash value or other identifiable value that would allow for a sequential order to iterate through the properties of a class?
I was able to find posts and documentation that allowed me to get as far as the following code in playground that generated the following in debug window:
struct Name: Sequence {
typealias Iterator = NameIterator
typealias Element = Name
typealias Name = String
var name = "<name>"
func makeIterator() -> NameIterator {
return NameIterator()
}
}
struct NameIterator: IteratorProtocol {
typealias Iterator = String
typealias Element = Name
typealias Name = String
mutating func next() -> Name? {
let nextName = Name()
return nextName
}
}
let nameStrings = ["Debbie", "Harper", "Indivan", "Juniella"]
for nameString in nameStrings {
print(nameString)
}
Debbie
Harper
Indivan
Juniella
If you really don't want to use mirror, a straightforward way is to cycle through a list of key paths. This is particularly easy in your case because the properties are all strings:
class Contact {
static let properties = [\Contact.givenName, \Contact.familyName, \Contact.middleName]
static func compare(left: Contact, right: Contact) {
for property in properties {
if left[keyPath: property] == right[keyPath: property] {
print("got a match"); return
}
}
print("no match")
}
var givenName: String = ""
var familyName: String = ""
var middleName: String = ""
}
I think there's some confusion going on here.
The Sequence protocol and friends (IteratorProtocol, Collection, etc.) exist for you to be able to define custom sequences/collections that can leverage the existing collection algorithms (e.g. if you conform to Sequence, your type gets map "for free"). It has absolutely nothing to do with accessing object properties. If you want to do that, the only official reflection API in Swift is Mirror.
It's possible to...
...just Mirror, to create a standard collection (e.g. Array) of properties of an object
...just Sequence/Collection, to create a custom collection object that lists the property values of an object from hard-coded keypaths
...or you can use both, together, to create a custom collection object that uses Mirror to automatically list the properties of an object and their values
New in iOS 15, we can form a Swift AttributedString like this:
var att = AttributedString("Howdy")
att.font = UIFont(name:"Arial-BoldMT", size:15)
att.foregroundColor = UIColor(red:0.251, green:0.000, blue:0.502, alpha:1)
print(att)
Cool, but there's another way. Instead of successive imperative property setting, we can make an attribute dictionary by way of an AttributeContainer, chaining modifier functions to the AttributeContainer to form the dictionary:
let att2 = AttributedString("Howdy",
attributes: AttributeContainer()
.font(UIFont(name:"Arial-BoldMT", size:15)!)
.foregroundColor(UIColor(red:0.251, green:0.000, blue:0.502, alpha:1))
)
print(att2)
(In real life I'd say .init() instead of AttributeContainer().)
So my question is, how does this work syntactically under the hood? We seem to have here a DSL where we can chain what look like function calls based on the names of the attribute keys. Behind the scenes, there seems to be some combination of dynamic member lookup, callAsFunction, and perhaps some sort of intermediate builder object. I can see that every callAsFunction call is returning the AttributeContainer, which is clearly how the chaining works. But just how would we write our own object that behaves syntactically the way AttributeContainer behaves?
I've made DSLs in the past similar to this.
I can't verify this is exactly what they're doing, but I can describe the way I achieved a similar DSL syntax.
My builder object would have methods like .font and .color return a temporary #dynamicCallable struct. These structs would store their parent build (by analogy, the AttributeContainer), and the keypath they were called originated from (\.font, \.color, etc.). (I don't remember if I used proper keypaths or strings. I can check later and get back to you.)
The implementation of callAsFunction would look something like:
func callAsFunction(_ someParam: SomeType) -> AttributeContainer {
parent[keyPath: keyPath] = someParam
return parent // for further chaining in the fluent interface.
}
Subsequent calls such as .foregroundColor would then repeat that same process.
Here's a bare-bones example:
#dynamicMemberLookup struct DictBuilder<Value> {
struct Helper<Value> {
let key: String
var parent: DictBuilder<Value>
func callAsFunction(_ value: Value) -> DictBuilder<Value> {
var copy = parent
copy.dict[key] = value
return copy
}
}
var dict = [String: Value]()
subscript(dynamicMember key: String) -> Helper<Value> {
return DictBuilder.Helper(key: key, parent: self)
}
}
let dict = DictBuilder<Int>()
.a(1)
.b(2)
.c(3)
.dict
print(dict)
IIRC, you can some generic magic and keypaths (instead of strings) to return different type per keypath, whose callAsFunciton could require arguments of different type, which can be enforced at compile time.
You can use #dynamicCallable instead of #dynamicMemberLookup+callAsFunction, but I don't think worked with the trick I just mentioned.
In the Introduction to Swift WWDC session, a read-only property description is demonstrated:
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheels"
}
}
let vehicle = Vehicle()
println(vehicle.description)
Are there any implications to choosing the above approach over using a method instead:
class Vehicle {
var numberOfWheels = 0
func description() -> String {
return "\(numberOfWheels) wheels"
}
}
let vehicle = Vehicle()
println(vehicle.description())
It seems to me that the most obvious reasons for choosing a read-only computed property are:
Semantics - in this example it makes sense for description to be a property of the class, rather than an action it performs.
Brevity/Clarity - prevents the need to use empty parentheses when getting the value.
Clearly the above example is overly simple, but are there other good reasons to choose one over the other? For example, are there some features of functions or properties that would guide your decision of which to use?
N.B. At first glance this seems like quite a common OOP question, but I'm keen to know of any Swift-specific features that would guide best practice when using this language.
It seems to me that it's mostly a matter of style: I strongly prefer using properties for just that: properties; meaning simple values that you can get and/or set. I use functions (or methods) when actual work is being done. Maybe something has to be computed or read from disk or from a database: In this case I use a function, even when only a simple value is returned. That way I can easily see whether a call is cheap (properties) or possibly expensive (functions).
We will probably get more clarity when Apple publishes some Swift coding conventions.
Well, you can apply Kotlin 's advices https://kotlinlang.org/docs/reference/coding-conventions.html#functions-vs-properties.
In some cases functions with no arguments might be interchangeable
with read-only properties. Although the semantics are similar, there
are some stylistic conventions on when to prefer one to another.
Prefer a property over a function when the underlying algorithm:
does not throw
complexity is cheap to calculate (or caсhed
on the first run)
returns the same result over invocations
While a question of computed properties vs methods in general is hard and subjective, currently there is one important argument in the Swift's case for preferring methods over properties. You can use methods in Swift as pure functions which is not true for properties (as of Swift 2.0 beta). This makes methods much more powerful and useful since they can participate in functional composition.
func fflat<A, R>(f: (A) -> () -> (R)) -> (A) -> (R) {
return { f($0)() }
}
func fnot<A>(f: (A) -> Bool) -> (A) -> (Bool) {
return { !f($0) }
}
extension String {
func isEmptyAsFunc() -> Bool {
return isEmpty
}
}
let strings = ["Hello", "", "world"]
strings.filter(fnot(fflat(String.isEmptyAsFunc)))
There is a difference:
If you use a property you can then eventually override it and make it read/write in a subclass.
Since the runtime is the same, this question applies to Objective-C as well. I'd say, with properties you get
a possibility of adding a setter in a subclass, making the property readwrite
an ability to use KVO/didSet for change notifications
more generally, you can pass property to methods that expect key paths, e.g. fetch request sorting
As for something specific to Swift, the only example I have is that you can use #lazy for a property.
In the read-only case, a computed property should not be considered semantically equivalent to a method, even when they behave identically, because dropping the func declaration blurs the distinction between quantities that comprise the state of an instance and quantities that are merely functions of the state. You save typing () at the call site, but risk losing clarity in your code.
As a trivial example, consider the following vector type:
struct Vector {
let x, y: Double
func length() -> Double {
return sqrt(x*x + y*y)
}
}
By declaring the length as a method, it’s clear that it’s a function of the state, which depends only on x and y.
On the other hand, if you were to express length as a computed property
struct VectorWithLengthAsProperty {
let x, y: Double
var length: Double {
return sqrt(x*x + y*y)
}
}
then when you dot-tab-complete in your IDE on an instance of VectorWithLengthAsProperty, it would look as if x, y, length were properties on an equal footing, which is conceptually incorrect.
From the performance perspective, there seems no difference. As you can see in the benchmark result.
gist
main.swift code snippet:
import Foundation
class MyClass {
var prop: Int {
return 88
}
func foo() -> Int {
return 88
}
}
func test(times: u_long) {
func testProp(times: u_long) -> TimeInterval {
let myClass = MyClass()
let starting = Date()
for _ in 0...times {
_ = myClass.prop
}
let ending = Date()
return ending.timeIntervalSince(starting)
}
func testFunc(times: u_long) -> TimeInterval {
let myClass = MyClass()
let starting = Date()
for _ in 0...times {
_ = myClass.prop
}
let ending = Date()
return ending.timeIntervalSince(starting)
}
print("prop: \(testProp(times: times))")
print("func: \(testFunc(times: times))")
}
test(times: 100000)
test(times: 1000000)
test(times: 10000000)
test(times: 100000000)
Output:
prop: 0.0380070209503174
func: 0.0350250005722046
prop: 0.371925950050354
func: 0.363085985183716
prop: 3.4023300409317
func: 3.38373708724976
prop: 33.5842199325562
func: 34.8433820009232
Program ended with exit code: 0
In Chart:
There are situations where you would prefer computed property over normal functions. Such as: returning the full name of an person. You already know the first name and the last name. So really the fullName property is a property not a function. In this case, it is computed property (because you can't set the full name, you can just extract it using the firstname and the lastname)
class Person{
let firstName: String
let lastName: String
init(firstName: String, lastName: String){
self.firstName = firstName
self.lastName = lastName
}
var fullName :String{
return firstName+" "+lastName
}
}
let william = Person(firstName: "William", lastName: "Kinaan")
william.fullName //William Kinaan
Semantically speaking, computed properties should be tightly coupled with the intrinsic state of the object - if other properties don't change, then querying the computed property at different times should give the same output (comparable via == or ===) - similar to calling a pure function on that object.
Methods on the other hand come out of the box with the assumption that we might not always get the same results, because Swift doesn't have a way to mark functions as pure. Also, methods in OOP are considered actions, which means that executing them might result in side effects. If the method has no side effects, then it can safely be converted to a computed property.
Note that both of the above statements are purely from a semantic perspective, as it might well happen for computed properties to have side effects that we don't expect, and methods to be pure.
Historically description is a property on NSObject and many would expect that it continues the same in Swift. Adding parens after it will only add confusion.
EDIT:
After furious downvoting I have to clarify something - if it is accessed via dot syntax, it can be considered a property. It doesn't matter what's under the hood. You can't access usual methods with dot syntax.
Besides, calling this property did not require extra parens, like in the case of Swift, which may lead to confusion.
An updated/fixed version of Benjamin Wen's answer incorporating Cristik's suggestion.
class MyClass {
var prop: Int {
return 88
}
func foo() -> Int {
return 88
}
}
func test(times: u_long) {
func testProp(times: u_long) -> TimeInterval {
let myClass = MyClass()
let starting = CACurrentMediaTime()
for _ in 0...times {
_ = myClass.prop
}
let ending = CACurrentMediaTime()
return ending - starting
}
func testFunc(times: u_long) -> TimeInterval {
let myClass = MyClass()
let starting = CACurrentMediaTime()
for _ in 0...times {
_ = myClass.foo()
}
let ending = CACurrentMediaTime()
return ending - starting
}
print("prop: \(testProp(times: times))")
print("func: \(testFunc(times: times))")
}
test(times: 100000)
test(times: 1000000)
test(times: 10000000)
test(times: 100000000)
One of the things that bugs me about Swift and Cocoa together is working with NSUserDefaults, because there is no type information and it is always necessary to cast the result of objectForKey to what you are expecting to get. It is unsafe and impractical. I decided to tackle this problem, making NSUserDefaults more practical in Swift-land, and hopefully learning something along the way. Here were my goals in the beginning:
Complete type safety: each key has one type associated with it. When setting a value, only a value of that type should be accepted and when getting a value the result should come out with the correct type
Global list of keys which are clear in meaning and content. The list should be easy to create, modify and extend
Clean syntax, using subscripts if possible. For example, this would
be perfect:
3.1. set: UserDefaults[.MyKey] = value
3.2. get: let value = UserDefaults[.MyKey]
Support for classes that conform to the NSCoding protocol by
automatically [un]archiving them
Support for all property list types accepted by NSUserDefaults
I started by creating this generic struct:
struct UDKey <T> {
init(_ n: String) { name = n }
let name: String
}
Then I created this other struct that serves as a container for all the keys in an application:
struct UDKeys {}
This can then be extended to add keys wherever needed:
extension UDKeys {
static let MyKey1 = UDKey<Int>("MyKey1")
static let MyKey2 = UDKey<[String]>("MyKey2")
}
Note how each key has a type associated with it. It represents the type of the information to be saved. Also, the name property is the string that is to be used as a key for NSUserDefaults.
The keys can be listed all in one constants file, or added using extensions on a per-file basis close to where they are being used for storing data.
Then I created an "UserDefaults" class responsible for handling the getting/setting of information:
class UserDefaultsClass {
let storage = NSUserDefaults.standardUserDefaults()
init(storage: NSUserDefaults) { self.storage = storage }
init() {}
// ...
}
let UserDefaults = UserDefaultsClass() // or UserDefaultsClass(storage: ...) for further customisation
The idea is that one instance for a particular domain is created and then every method is accessed in this way:
let value = UserDefaults.myMethod(...)
I prefer this approach to things like UserDefaults.sharedInstance.myMethod(...) (too long!) or using class methods for everything. Also, this allows interacting with various domains at the same time by using more than one UserDefaultsClass with different storage values.
So far, items 1 and 2 have been taken care of, but now the difficult part is starting: how to actually design the methods on UserDefaultsClass in order to comply with the rest.
For example, let's start with item 4. First I tried this (this code is inside UserDefaultsClass):
subscript<T: NSCoding>(key: UDKey<T>) -> T? {
set { storage.setObject(NSKeyedArchiver.archivedDataWithRootObject(newValue), forKey: key.name) }
get {
if let data = storage.objectForKey(key.name) as? NSData {
return NSKeyedUnarchiver.unarchiveObjectWithData(data) as? T
} else { return nil }
}
}
But then I find out that Swift doesn't allow generic subscripts!! Alright, then I guess I'll have to use functions then. There goes half of item 3...
func set <T: NSCoding>(key: UDKey<T>, _ value: T) {
storage.setObject(NSKeyedArchiver.archivedDataWithRootObject(value), forKey: key.name)
}
func get <T: NSCoding>(key: UDKey<T>) -> T? {
if let data = storage.objectForKey(key.name) as? NSData {
return NSKeyedUnarchiver.unarchiveObjectWithData(data) as? T
} else { return nil }
}
And that works just fine:
extension UDKeys { static let MyKey = UDKey<NSNotification>("MyKey") }
UserDefaults.set(UDKeys.MyKey, NSNotification(name: "Hello!", object: nil))
let n = UserDefaults.get(UDKeys.MyKey)
Note how I can't call UserDefaults.get(.MyKey). I have to use UDKeys.MyKey. And I can't do that because it's not yet possible to have static variables on a generic struct!!
Next, let's try number 5. Now that has been an headache and that's where I need lots of help.
Property list types are, as per the docs:
A default object must be a property list, that is, an instance of (or
for collections a combination of instances of): NSData, NSString,
NSNumber, NSDate, NSArray, or NSDictionary.
That in Swift means Int, [Int], [[String:Bool]], [[String:[Double]]], etc are all property list types. At first I thought that I could just write this and trust whoever is using this code to remember that only plist types are allowed:
func set <T: AnyObject>(key: UDKey<T>, _ value: T) {
storage.setObject(value, forKey: key.name)
}
func get <T: AnyObject>(key: UDKey<T>) -> T? {
return storage.objectForKey(key.name) as? T
}
But as you'll notice, while this works fine:
extension UDKeys { static let MyKey = UDKey<NSData>("MyKey") }
UserDefaults.set(UDKeys.MyKey, NSData())
let d = UserDefaults.get(UDKeys.MyKey)
This doesn't:
extension UDKeys { static let MyKey = UDKey<[NSData]>("MyKey") }
UserDefaults.set(UDKeys.MyKey, [NSData()])
And this doesn't either:
extension UDKeys { static let MyKey = UDKey<[Int]>("MyKey") }
UserDefaults.set(UDKeys.MyKey, [0])
Not even this:
extension UDKeys { static let MyKey = UDKey<Int>("MyKey") }
UserDefaults.set(UDKeys.MyKey, 1)
The problem is that they are all valid property list types yet Swift obviously interprets arrays and ints as structs, not as their Objective-C class counterparts. However:
func set <T: Any>(key: UDKey<T>, _ value: T)
won't work either, because then any value type, not just the ones that have a class cousin courtesy of Obj-C, is accepted, and storage.setObject(value, forKey: key.name) is no longer valid because value has to be a reference type.
If a protocol existed in Swift that accepted any reference type and any value type that can be converted to a reference type in objective-c (like [Int] and the other examples I mention) this problem would be solved:
func set <T: AnyObjectiveCObject>(key: UDKey<T>, _ value: T) {
storage.setObject(value, forKey: key.name)
}
func get <T: AnyObjectiveCObject>(key: UDKey<T>) -> T? {
return storage.objectForKey(key.name) as? T
}
AnyObjectiveCObject would accept any swift classes and swift arrays, dictionaries, numbers (ints, floats, bools, etc that convert to NSNumber), strings...
Unfortunately, AFAIK this doesn't exist.
Question:
How can I have write a generic function (or collection of overloaded generic functions) whose generic type T can be any reference type or any value type that Swift can convert to a reference type in Objective-C?
Solved: With the help of the answers I got, I arrived at what I wanted. In case anyone wants to take a look at my solution, here it is.
I don't mean to brag but ... oh who am I kidding, I totally do!
Preferences.set([NSData()], forKey: "MyKey1")
Preferences.get("MyKey1", type: type([NSData]))
Preferences.get("MyKey1") as [NSData]?
func crunch1(value: [NSData])
{
println("Om nom 1!")
}
crunch1(Preferences.get("MyKey1")!)
Preferences.set(NSArray(object: NSData()), forKey: "MyKey2")
Preferences.get("MyKey2", type: type(NSArray))
Preferences.get("MyKey2") as NSArray?
func crunch2(value: NSArray)
{
println("Om nom 2!")
}
crunch2(Preferences.get("MyKey2")!)
Preferences.set([[String:[Int]]](), forKey: "MyKey3")
Preferences.get("MyKey3", type: type([[String:[Int]]]))
Preferences.get("MyKey3") as [[String:[Int]]]?
func crunch3(value: [[String:[Int]]])
{
println("Om nom 3!")
}
crunch3(Preferences.get("MyKey3")!)
I'd like to introduce my idea. (Sorry for my poor English in advance.)
let plainKey = UDKey("Message", string)
let mixedKey
= UDKey("Mixed"
, array(dictionary(
string, tuple(
array(integer),
optional(date)))))
let ud = UserDefaults(NSUserDefaults.standardUserDefaults())
ud.set(plainKey, "Hello")
ud.set(plainKey, 2525) // <-- compile error
ud.set(mixedKey, [ [ "(^_^;)": ([1, 2, 3], .Some(NSDate()))] ])
ud.set(mixedKey, [ [ "(^_^;)": ([1, 2, 3], .Some(NSData()))] ]) // <-- compile error
The only difference is that UDKey() now requires #2 argument, a value of BiMap class. I've uncoupled the work originally of UDKey into BiMap which converts a value of a type to/from a value of another type.
public class BiMap<A, B> {
public func AtoB(a: A) -> B?
public func BtoA(b: B) -> A?
}
Consequently, types that set/get can accepts are conducted by BiMap, and no longer limited to types as can automatically cast
from/to AnyObject (more specifically, types NSUserDefaults can accepts.).
Because BiMap is a generic class, you can easily create subtypes of that, interchanging arbitrary two types you want.
Here is full source code. (But there are bugs yet to be fixed..)
https://gist.github.com/hisui/47f170a9e193168dc946
In Swift is it possible to retrieve / remove an element in a Dictionary using it's index, not a key value? In this example I'm attempting to create a "to do" list where I can eventually access all the tasks assigned to an individual. But I would also like to remove an element (task) by index.
In the code below how can I remove the second task "wash dishes" by index not using a key value. I was hoping to call this func with something like: taskMgr.removeTaskByIndex(1)
Any additional explanation or advice is helpful as I'm just learning Swift.
import Foundation
class TaskManager{
var taskDictArray = Dictionary<String, String>()
init(){
taskDictArray = [:]
}
func addTask(task: String, person: String){
taskDictArray[task] = person
}
}
var taskMgr = TaskManager()
taskMgr.addTask("take out trash", person: "emma")
taskMgr.addTask("wash dishes", person: "jack")
taskMgr.addTask("clean floor", person: "cleo")
//taskMgr.removeTaskByIndex(1)
//var a = taskMgr.getTaskByIndex(1)
Dictionaries do not maintain their object order and the only way to get a value is using its key. You'll probably want to associate whatever list of tasks you have the index for with its corresponding key and use that manipulate the dictionary. This is the only way. You probably consider restructuring your application logic if it's impossible.
You'll need to add a function such as
func removeTask(task: String) {
taskDictArray[task] = nil
}
So find a way to get the actual task string with which you are associating it.