<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">Self-correction: will/didChangeValue still do accept string key paths; it’s just that in that case they are not respelled and still use “forKey:” as their first argument label. Not sure why that didn’t come up in autocomplete when I was doing my tests the other day. I still think that making @objc broadly available would be a more elegant solution.</div><br class=""><div><blockquote type="cite" class=""><div class="">On Jun 10, 2017, at 11:47 AM, Charles Srstka via swift-evolution <<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">INTRODUCTION:<div class=""><br class=""></div><div class="">This pitch proposes to allow the @objc keyword on all property declarations, even ones whose type cannot be represented in Objective-C.</div><div class=""><br class=""></div><div class="">MOTIVATION:</div><div class=""><br class=""></div><div class="">You’re thinking, “But that’s crazy. Why would you ever want to do that?” But hear me out:</div><div class=""><br class=""></div><div class="">- In Swift 4, barring cases where it’s required for technical reasons, properties are exposed to Objective-C only if there’s an actual @objc keyword present. This keyword represents a clear statement of intent that this protocol is meant to be visible to Objective-C, and means that expanding the scope of the @objc keyword will not increase code size anywhere other than where a deliberate decision has been made to do so.</div><div class=""><br class=""></div><div class="">- Since Swift 3, all Swift types are in fact bridgeable to Objective-C, and an ‘Any’ is bridged to an ‘id’.</div><div class=""><br class=""></div><div class="">- While it’s true that Objective-C will generally get an opaque object that it won’t know what to do with, that doesn’t mean that there aren’t cases where this can still be useful, such as:</div><div class=""><br class=""></div><div class="">- Value transformers. Even if a type is completely opaque to Objective-C, that doesn’t mean a value transformer can’t convert it into something that Objective-C can use. There are some quite useful general-purpose value transformers that could be written for this task, such as:</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""><span style="color: #ba2da2" class="">class</span> CustomStringConvertibleValueTransformer: <span style="color: #703daa" class="">ValueTransformer</span> {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">override</span> <span style="color: #ba2da2" class="">class</span> <span style="color: #ba2da2" class="">func</span> transformedValueClass() -> <span style="color: #703daa" class="">AnyClass</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #703daa" class="">NSString</span>.<span style="color: #ba2da2" class="">self</span> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">override</span> <span style="color: #ba2da2" class="">class</span> <span style="color: #ba2da2" class="">func</span> allowsReverseTransformation() -> <span style="color: #703daa" class="">Bool</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">false</span> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255); min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">override</span> <span style="color: #ba2da2" class="">func</span> transformedValue(<span style="color: #ba2da2" class="">_</span> value: <span style="color: #ba2da2" class="">Any</span>?) -> <span style="color: #ba2da2" class="">Any</span>? {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(112, 61, 170); background-color: rgb(255, 255, 255);" class=""><span style="" class=""> </span><span style="color: #ba2da2" class="">return</span><span style="" class=""> (value </span><span style="color: #ba2da2" class="">as</span><span style="" class="">? </span>CustomStringConvertible<span style="" class="">)?.</span>description</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class="">}</div></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">With this value transformer, an enum like this one from the Swift manual could be exposed to Objective-C and bound to a UI element in Interface Builder, resulting in a meaningful value being shown in the UI:</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255);" class=""><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal; color: rgb(112, 61, 170);" class=""><span style="color: #ba2da2" class="">enum</span><span style="" class=""> Suit: </span>CustomStringConvertible<span style="" class=""> {</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">case</span> spades, hearts, diamonds, clubs</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">var</span> description: <span style="color: #703daa" class="">String</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">switch</span> <span style="color: #ba2da2" class="">self</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">case</span> .<span style="color: #31595d" class="">spades</span>:</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">return</span> <span style="color: #d12f1b" class="">"spades"</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">case</span> .<span style="color: #31595d" class="">hearts</span>:</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">return</span> <span style="color: #d12f1b" class="">"hearts"</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">case</span> .<span style="color: #31595d" class="">diamonds</span>:</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">return</span> <span style="color: #d12f1b" class="">"diamonds"</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">case</span> .<span style="color: #31595d" class="">clubs</span>:</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">return</span> <span style="color: #d12f1b" class="">"clubs"</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class="">}</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">Once we have generalized existentials, we could write this value transformer as well, which would be able to handle all Swift enums backed by ObjC-representable types without any special hacks:</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal;" class=""><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="color: #ba2da2" class="">class</span> RawRepresentableValueTransformer: <span style="color: #703daa" class="">ValueTransformer</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">override</span> <span style="color: #ba2da2" class="">class</span> <span style="color: #ba2da2" class="">func</span> transformedValueClass() -> <span style="color: #703daa" class="">AnyClass</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #703daa" class="">AnyObject</span>.<span style="color: #ba2da2" class="">self</span> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">override</span> <span style="color: #ba2da2" class="">class</span> <span style="color: #ba2da2" class="">func</span> allowsReverseTransformation() -> <span style="color: #703daa" class="">Bool</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">false</span> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">override</span> <span style="color: #ba2da2" class="">func</span> transformedValue(<span style="color: #ba2da2" class="">_</span> value: <span style="color: #ba2da2" class="">Any</span>?) -> <span style="color: #ba2da2" class="">Any</span>? {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">return</span> (value <span style="color: #ba2da2" class="">as</span>? <span style="text-decoration: underline ; color: #703daa" class="">R</span><span style="color: #703daa" class="">awRepresentable</span>)<span style="text-decoration: underline" class="">?</span>.rawValue</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class="">}</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">- KVO dependencies. Even without a value transformer, a property of a non-ObjC-representable type may be a dependency of other properties which *are* ObjC-representable, as in this example:</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="color: #ba2da2" class="">class</span> PlayingCard: <span style="color: #703daa" class="">NSObject</span> {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(0, 132, 0);" class=""><span style="" class=""> </span><span style="color: #ba2da2" class="">@objc</span><span style="" class=""> </span><span style="color: #ba2da2" class="">dynamic</span><span style="" class=""> </span><span style="color: #ba2da2" class="">var</span><span style="" class=""> </span><span style="text-decoration: underline;" class="">s</span><span style="" class="">uit: </span><span style="color: #4f8187" class="">Suit</span><span style="" class=""> </span>// currently an error</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: rgb(186, 45, 162);" class="">init</span>(suit: <span style="color: rgb(79, 129, 135);" class="">Suit</span>) {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: rgb(186, 45, 162);" class="">self</span>.<span style="color: rgb(79, 129, 135);" class="">suit</span> = suit</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: rgb(186, 45, 162);" class="">super</span>.<span style="color: rgb(186, 45, 162);" class="">init</span>()</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""><br class=""></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">private</span> <span style="color: #ba2da2" class="">static</span> <span style="color: #ba2da2" class="">let</span> keyPathsForValuesAffectingSuitName: <span style="color: #703daa" class="">Set</span><<span style="color: #703daa" class="">String</span>> = [<span style="color: #ba2da2" class="">#keyPath</span>(<span style="text-decoration: underline" class="">s</span>uit)]</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">var</span> suitName: <span style="color: #703daa" class="">String</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span>.<span style="color: #4f8187" class="">description</span> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class="">}</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">Although the ‘suit’ property is not representable in Objective-C, the ‘suitName’ property is, and it would behoove us to allow it to be updated when ‘suit’ changes. Currently, we have to resort to various hacks to accomplish this. One can manually send the KVO notifications on the original property instead of near the dependent ones, which can be error-prone:</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><span style="color: #ba2da2" class="">class</span> ErrorPronePlayingCard: <span style="color: #703daa" class="">NSObject</span> {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">var</span> suit: <span style="color: #4f8187" class="">Suit</span> {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">willSet</span> { <span style="color: #ba2da2" class="">self</span>.<span style="color: #3e1e81" class="">willChangeValue</span>(for: \.suitName) }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">didSet</span> { <span style="color: #ba2da2" class="">self</span>.<span style="color: #3e1e81" class="">didChangeValue</span>(for: \.suitName) }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">init</span>(suit: <span style="color: #4f8187" class="">Suit</span>) {</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span> = suit</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">super</span>.<span style="color: #ba2da2" class="">init</span>()</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""> <span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">var</span> suitName: <span style="color: #703daa" class="">String</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span>.<span style="color: #4f8187" class="">description</span> }</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class="">}</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">Formerly, one could simply use arbitrary strings as key paths, which was ugly since it required a separate override of value(forKey:) to avoid exceptions if someone actually tried to acquire a value using the key. Also, it doesn’t seem to work anymore in Swift 4, since the will/didChangeValue methods now expect a KeyPath object instead of a String:</div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><div style="margin: 0px; line-height: normal;" class=""><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="color: #ba2da2" class="">class</span> DontWorkNoMorePlayingCard: <span style="color: #703daa" class="">NSObject</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">var</span> suit: <span style="color: #4f8187" class="">Suit</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">willSet</span> { <span style="color: #ba2da2" class="">self</span>.willChangeValue(for: <span style="text-decoration: underline ; color: #d12f1b" class="">"</span><span style="color: #d12f1b" class="">suit"</span>) } <span style="color: #008400" class="">// error: this requires a KeyPath now</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">didSet</span> { <span style="color: #ba2da2" class="">self</span>.didChangeValue(for: <span style="text-decoration: underline ; color: #d12f1b" class="">"</span><span style="color: #d12f1b" class="">suit"</span>) }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: rgb(186, 45, 162);" class="">override</span> <span style="color: rgb(186, 45, 162);" class="">func</span> value(forKey key: <span style="color: rgb(112, 61, 170);" class="">String</span>) -> <span style="color: rgb(186, 45, 162);" class="">Any</span>? {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">switch</span> key {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">case</span> <span style="color: #d12f1b" class="">"suit"</span>:</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">default</span>:</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">super</span>.<span style="color: #3e1e81" class="">value</span>(forKey: key)</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">init</span>(suit: <span style="color: #4f8187" class="">Suit</span>) {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span> = suit</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">super</span>.<span style="color: #ba2da2" class="">init</span>()</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">private</span> <span style="color: #ba2da2" class="">static</span> <span style="color: #ba2da2" class="">let</span> keyPathsForValuesAffectingSuitName: <span style="color: #703daa" class="">Set</span><<span style="color: #703daa" class="">String</span>> = [<span style="color: #d12f1b" class="">"suit"</span>]</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">var</span> suitName: <span style="color: #703daa" class="">String</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span>.<span style="color: #4f8187" class="">description</span> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class="">}</div><div style="font-family: Menlo; font-size: 11px;" class=""><br class=""></div><div style="font-family: Menlo; font-size: 11px;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">One can ugly up the class with separate Any-typed properties, which serve no purpose other than to facilitate KVO dependencies:</span></div><div style="font-family: Menlo; font-size: 11px;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px;" class=""><div style="margin: 0px; line-height: normal;" class=""><span style="color: #ba2da2" class="">class</span> UglyPlayingCard: <span style="color: #703daa" class="">NSObject</span> {</div><div style="margin: 0px; line-height: normal; color: rgb(186, 45, 162);" class=""><span style="" class=""> </span>@objc<span style="" class=""> </span>private<span style="" class=""> </span>var<span style="" class=""> _objCSuit: </span>Any<span style="" class=""> { </span>return<span style="" class=""> </span>self<span style="" class="">.</span><span style="color: #4f8187" class="">suit</span><span style="" class=""> }</span></div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">var</span> suit: <span style="color: #4f8187" class="">Suit</span> {</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">willSet</span> { <span style="color: #ba2da2" class="">self</span>.<span style="color: #3e1e81" class="">willChangeValue</span>(for: \._objCSuit) }</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">didSet</span> { <span style="color: #ba2da2" class="">self</span>.<span style="color: #3e1e81" class="">didChangeValue</span>(for: \._objCSuit) }</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">init</span>(suit: <span style="color: #4f8187" class="">Suit</span>) {</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span> = suit</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">super</span>.<span style="color: #ba2da2" class="">init</span>()</div><div style="margin: 0px; line-height: normal;" class=""> }</div><div style="margin: 0px; line-height: normal; min-height: 13px;" class=""> <br class="webkit-block-placeholder"></div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">private</span> <span style="color: #ba2da2" class="">static</span> <span style="color: #ba2da2" class="">let</span> keyPathsForValuesAffectingSuitName: <span style="color: #703daa" class="">Set</span><<span style="color: #703daa" class="">String</span>> = [<span style="color: #ba2da2" class="">#keyPath</span>(_objCSuit)]</div><div style="margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">var</span> suitName: <span style="color: #703daa" class="">String</span> { <span style="color: #ba2da2" class="">return</span> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span>.<span style="color: #4f8187" class="">description</span> }</div><div style="margin: 0px; line-height: normal;" class="">}</div></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">None of these hacks would be necessary if it were possible to simply put @objc on any property declaration.</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">DETAILED DESIGN:</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal;" class="">For read-only properties, implementation is easy; just expose the type to Objective-C as ‘id’, so:</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(186, 45, 162);" class="">@objc<span style="" class=""> </span>private(set)<span style="" class=""> </span>var<span style="" class=""> suit: </span><span style="color: #703daa" class="">Suit</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(186, 45, 162);" class=""><span style="color: #703daa" class=""><br class=""></span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(186, 45, 162);" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">becomes:</span></div><div style="margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: rgb(186, 45, 162);" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal; color: rgb(186, 45, 162);" class=""><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class="">@property<span style="" class=""> (</span>nonatomic<span style="" class="">, </span>readonly<span style="" class="">) </span>id<span style="" class=""> suit;</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">For writable properties, there is the danger that an Objective-C client could pass an object of the wrong type to the setter. For this case, we could </span><span style="font-family: Helvetica; font-size: 12px;" class="">generate a thunk for the setter that checks the type of incoming values, and simply ignores values of the wrong type:</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal;" class=""><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="color: #ba2da2" class="">func</span> setter(<span style="color: #ba2da2" class="">_</span> val: <span style="color: #ba2da2" class="">Any</span>) {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">if</span> <span style="color: #ba2da2" class="">let</span> suit = val <span style="color: #ba2da2" class="">as</span>? <span style="color: #4f8187" class="">Suit</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span> = suit</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class="">}</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">Alternatively, we could fatalError if the wrong type is sent.</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal;" class=""><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="color: #ba2da2" class="">func</span> setter(<span style="color: #ba2da2" class="">_</span> val: <span style="color: #ba2da2" class="">Any</span>) {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">if</span> <span style="color: #ba2da2" class="">let</span> suit = val <span style="color: #ba2da2" class="">as</span>? <span style="color: #4f8187" class="">Suit</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> <span style="color: #ba2da2" class="">self</span>.<span style="color: #4f8187" class="">suit</span> = suit</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> } <span style="color: #ba2da2" class="">else</span> {</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal; color: rgb(209, 47, 27);" class=""><span style="" class=""> </span><span style="color: #3e1e81" class="">fatalError</span><span style="" class="">(</span>"Passed-in value is not a Suit"<span style="" class="">)</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""> }</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class="">}</div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">For dynamic properties, we would want to reroute all sets through this thunk to ensure that the automatically-added KVO notifications will be fired. This will add a small performance cost due to the dynamic type check, but generally speaking, KVO is not a tool that one uses in performance-critical sections.</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">One special-case that would be nice to add would be to make AnyKeyPath, PartialKeyPath, KeyPath, and friends bridge to Objective-C as a string, which would allow us to get rid of the one remaining time #keyPath needed to be used in the examples for this pitch, and declare dependencies instead as:</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><div style="margin: 0px; line-height: normal;" class=""><span style="color: #ba2da2" class="">@objc</span> <span style="color: #ba2da2" class="">private</span> <span style="color: #ba2da2" class="">static</span> <span style="color: #ba2da2" class="">let</span> keyPathsForValuesAffectingSuitName: <span style="color: #703daa" class="">Set</span><<span style="color: #703daa" class="">PartialKeyPath</span><<span style="color: #703daa" class="">PlayingCard</span>>> = [\.suit]</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">which would expose itself to the Objective-C type system as: </span></div><div style="margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="margin: 0px; line-height: normal;" class=""><div style="margin: 0px; line-height: normal;" class=""><span style="color: #ba2da2" class="">@property</span> (class, <span style="color: #ba2da2" class="">readonly</span>) <span style="color: #703daa" class="">NSSet</span><<span style="color: #703daa" class="">NSString</span> *> *keyPathsForValuesAffectingSuitName;</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">allowing the KVO system to process it as normal.</span></div></div></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">IMPACT ON EXISTING CODE:</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">None; this is purely additive.</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">ALTERNATIVES CONSIDERED:</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class=""><br class=""></span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><span style="font-family: Helvetica; font-size: 12px;" class="">Keep on using the hacks described above.</span></div><div style="font-family: Menlo; font-size: 11px; margin: 0px; line-height: normal;" class=""><br class=""></div><div style="margin: 0px; line-height: normal;" class="">Charles</div><div style="margin: 0px; line-height: normal;" class=""><br class=""></div></div></div></div></div></div></div></div></div></div></div></div>_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>