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.
Since Swift 4, objects have gained subscript(keyPath:) which can be used to retrieve values using AnyKeyPath and its subclasses. According to the Swift book, the subscript is available on all types. For example, an instance of a class TestClass may be subscripted with an AnyKeyPath like so:
class TestClass {
let property = true
}
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = TestClass()[keyPath: anyKeyPath]
This compiles correctly as expected. Use of any other valid subclass would also compile including PartialKeyPath<TestClass>, KeyPath<TestClass, Bool>, etc. This functionality is unavailable in a protocol extension. For example, the following is invalid:
class TestClass {
let property = true
}
protocol KeyPathSubscriptable {
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = self[keyPath: anyKeyPath] // Value of type 'Self' has no subscripts
}
}
If we want to use that keyPath subscript in the protocol, we can include it in the protocol definition. However, the compiler will not resolve it automatically:
protocol KeyPathSubscriptable {
subscript(keyPath: AnyKeyPath) -> Any? { get }
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath // This can be any valid KeyPath
_ = self[keyPath: anyKeyPath]
}
}
class TestClass: KeyPathSubscriptable { // Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
let property = true
}
With this, we get a compile error: Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'. In order to resolve this, we must include a redundant implementation of that subscript in TestClass:
class TestClass: KeyPathSubscriptable {
let property = true
subscript(keyPath: AnyKeyPath) -> Any? {
fatalError() // This is never executed
}
}
This resolves the conformance issue and produces the goal result although it is seemingly unnecessary and illogical. I'm not sure how, but the subscript implementation is never even used. It's finding the expected implementation of subscript(keyPath:) and using that instead, but how? Where is that and is there any way to use it in a protocol? Why is this required by the compiler even though it's never used?
The context of this use case is in a logging module. The goal is that an object should be able to adopt a particular protocol which, with no additional setup on the object, would provide a human readable description of the object, instead of the default for many objects which is a memory address. The protocol would use Mirror to fetch KeyPaths of an object, read the values, and print them to the console. It is intended for debugging purposes and would not run in any production environment.
Please let me know if I can make any clarifications. I may post this to the Swift team if others think that this could potentially be a bug of sorts. All help is appreciated. Thanks in advance.
Full gist located here.
For a project I am currently working on, it would be very useful to get the KVC-String from a KeyPath instance my method is receiving. Short example:
struct Person {
var name: String
}
let propertyCache = ["name": "something"]
func method<T>(_ keypath: KeyPath<Person, T>) -> T? {
let kvcName = keypath.kvc
return propertyCache[kvcName]
}
This might seem not very useful, but in my project it is :) I found a property on KeyPath called _kvcKeyPathString which is also public, but it returns nil every time I tried.
Or is their maybe a possibility to use reflection there? Thanks in advance for ideas/solutions!
I don't know of a pure Swift way to get the name of the property as a string yet.
But, if you add the #objc attribute to the property then _kvcKeyPathString will actually have a value instead of always being nil. Also, since Swift structs can't be represented in Objective-C, this method only works for classes.
A minimal working example usage:
class SomeClass {
#objc var someProperty = 5
}
let keyPath = \SomeClass.someProperty
print(keyPath._kvcKeyPathString)
I couldn't find any good explanation to my questions so I'd like to ask you directly. First of all I'd like to refine my code in this post.
My problem is the protocol AnyObject and the Self type. I didn't implement AnyObject into my code because it is marked with #objc and I don't want any Objective-C stuff involved in my code (don't judge me for that). I also couldn't find any explanation about the Self type. It just worked as expected, but Xcode does not replace Self with the type the static function is called at.
Here is some example:
extension Int : Instance {}
Int.singleton { (customInstanceName) -> Self in 0 } // Self shall be replaced with Int
As you can see Xcode produces a Self instead an Int. Is there any chance I could fix this? Am I right that Self does return the dynamicType and my implementation is fine as it is in my post above? I would really appreciate any good explanation about the Self type.
As you have seen in my code. I am using a custom protocol to check whether my instance is a class or not. Is there any other shiny implementation to check my instances if they are classes or structure types, or am I forced to use AnyObject if I want to get rid of my ClassInstance protocol?
Thank you for your time.
UPDATE:
protocol Test {}
class A : Test {}
struct B : Test {}
let aClass : Test = A()
let aStruct : Test = B()
if let someClass = aClass as? AnyObject {
print(someClass) // only this will print
}
if let someStruct = aStruct as? AnyObject {
print(someStruct)
}
This will work, but AnyObject is still marked as an #objc protocol.
The Self type can be only used in protocols where it is a implicit typealias of the type which conforms to it:
protocol Testable {
func test() -> Self
}
If you want to conform to this protocol you than have to replace Self with the name of the type. For instance:
struct Product: Testable {
func test() -> Product {
return Product()
}
}
Important Edit:
As DevAndArtist pointed out in the comments there is a working class check in Swift 1.2 (without automatic bridging to Objective C) but not Swift 2 (Xcode 7 beta 3; probably a bug):
if instance.dynamicType is AnyClass {
// instance is a class
} else {
// instance is not a class
}
You can see workaround (mainly) for Swift 2 below.
End Edit
With respect to classes you should use AnyObject if you want to keep it simple but you can also use reflection which would be much more effort.
Below you can see some reflection results of string interpolations (only the first few characters):
"\(reflect(classType))" // Swift._ClassMirror
"\(reflect(0))" // Swift._LeafMirror
"\(reflect(enumType))" // Swift._EnumMirror
"\(reflect(structure))" // Swift._StructMirror
"\(reflect([0, 4]))" // Swift._ArrayTypeMirror
"\(reflect(NSDate()))" // Foundation._NSDateMirror
"\(reflect(NSURLRelationship.Contains))" // Swift._EnumMirror
"\(reflect(Int?(2)))" // Swift._OptionalMirror
As you can see enums are consistent if they are not defined in the Swift standard library (unfortunately also Optional...). So you can distinguish also structs and enums:
public enum Type {
case Enum, Class, Struct
}
public func getType<T>(anything: T) -> Type {
if anything is AnyObject {
return .Class
}
if "\(reflect(anything))".hasPrefix("Swift._EnumMirror") {
return .Enum
}
return .Struct
}
So for a better result you have to put some effort into it to differentiate between all the different cases.
But the easiest way to distinguish only between reference types and value types (aka classes and structs/enums) is still (unfortunately only works for own declared structs and not built in types because they can be bridged to Objective C; I'm working on it...):
if instance is AnyObject {}
// or: if instance is of type Any
if let classInstance = instance as? AnyObject {}
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