Should Array's `append(_)` function cause `didSet`?


(Zhao Xin) #1

Should Array's `append(_)` functions cause the array's `didSet`?
In my own test, it did call `didSet` in Playground. But in .swift files, it
didn't call.

Is this a known bug or something? Which is correct?

Xcode Version 9.0 beta 2 (9M137d)​

swift --version

Apple Swift version 4.0 (swiftlang-900.0.45.6 clang-900.0.26)

Target: x86_64-apple-macosx10.9

Zhao Xin


(Jordan Rose) #2

It definitely should. Can you show the code where it wasn’t being called?

Thanks!
Jordan

···

On Jul 7, 2017, at 00:31, Zhao Xin via swift-users <swift-users@swift.org> wrote:

Should Array's `append(_)` functions cause the array's `didSet`?
In my own test, it did call `didSet` in Playground. But in .swift files, it didn't call.

Is this a known bug or something? Which is correct?

Xcode Version 9.0 beta 2 (9M137d)​

swift --version
Apple Swift version 4.0 (swiftlang-900.0.45.6 clang-900.0.26)
Target: x86_64-apple-macosx10.9

Zhao Xin
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Zhao Xin) #3

import Cocoa

struct Foo {

    var keys = ["z","y","x"]

    {

        didSet {

            keys.sort()

        }

    }

    init(keysAndValues:Dictionary<String, String>) {

        self.keys.append(contentsOf: keysAndValues.keys)

    }

}

let keysAndValues:Dictionary<String,String> = ["c":"c", "b":"b", "a":"a"]

var foo = Foo(keysAndValues: keysAndValues) // `let foo` is the same result

foo.keys.forEach { print($0) }

/*

prints

z

y

x

b

c

a

*/

Above code doesn't call `didSet` in playground. My .swift file is similar
and didn't call `didSet` either. However, if without a struct, `didSet` is
called.

var keys = ["z","y","x"]

{

    didSet {

        keys.sort()

    }

}

let keysAndValues:Dictionary<String,String> = ["c":"c", "b":"b", "a":"a"]

keys.append(contentsOf: keysAndValues.keys)

keys.forEach { print($0) }

/*

prints

a

b

c

x

y

z

*/

*Xcode 9.0 beta 2 (9M137d)*

*Apple Swift version 4.0 (swiftlang-900.0.45.6 clang-900.0.26)*

*Target: x86_64-apple-macosx10.9*

*macOS Sierra 10.12.5 (16F73)*

Zhao Xin

···

On Fri, Jul 7, 2017 at 11:52 PM, Jordan Rose <jordan_rose@apple.com> wrote:

It definitely should. Can you show the code where it wasn’t being called?

Thanks!
Jordan

On Jul 7, 2017, at 00:31, Zhao Xin via swift-users <swift-users@swift.org> > wrote:

Should Array's `append(_)` functions cause the array's `didSet`?
In my own test, it did call `didSet` in Playground. But in .swift files,
it didn't call.

Is this a known bug or something? Which is correct?

Xcode Version 9.0 beta 2 (9M137d)​

swift --version
Apple Swift version 4.0 (swiftlang-900.0.45.6 clang-900.0.26)
Target: x86_64-apple-macosx10.9

Zhao Xin
_______________________________________________
swift-users mailing list
swift-users@swift.org
https://lists.swift.org/mailman/listinfo/swift-users


(Marco S Hyman) #4

    init(keysAndValues:Dictionary<String, String>) {
        self.keys.append(contentsOf: keysAndValues.keys)
    }

Above code doesn't call `didSet` in playground. My .swift file is similar and didn't call `didSet` either. However, if without a struct, `didSet` is called.

“If you don’t need to compute the property but still need to provide code that is run before and after setting a new value, use willSet and didSet. The code you provide is run any time the value changes outside of an initializer.”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3.1).” iBooks. https://itun.es/us/jEUH0.l

Note “outside of an initializer”. didSet and willSet are not called in initializers.


(Zhao Xin) #5

Thank you very much Marco. But What is “outside of an initializer” really
bothers me. **Both** `func bar(keysAndValues:Dictionary<String, String>)`
works now. **Are they really outside ?**

struct Foo {

    var keys = ["z","y","x"]

    {

        didSet {

            keys.sort()

        }

    }

    init(keysAndValues:Dictionary<String, String>) {

        func bar(keysAndValues:Dictionary<String, String>) {

            keys.append(contentsOf: keysAndValues.keys)

        }

        bar(keysAndValues:keysAndValues)

    }

// private mutating func bar(keysAndValues:Dictionary<String, String>) {

// keys.append(contentsOf: keysAndValues.keys)

// }

}

let keysAndValues:Dictionary<String,String> = ["c":"c", "b":"b", "a":"a"]

var foo = Foo(keysAndValues: keysAndValues) // `let foo` is the same result

foo.keys.forEach { print($0) }

/*

prints

a

b

c

x

y

z

*/

Zhao Xin

···

On Sat, Jul 8, 2017 at 9:59 AM, Marco S Hyman <marc@snafu.org> wrote:

> init(keysAndValues:Dictionary<String, String>) {
> self.keys.append(contentsOf: keysAndValues.keys)
> }

>
> Above code doesn't call `didSet` in playground. My .swift file is
similar and didn't call `didSet` either. However, if without a struct,
`didSet` is called.

“If you don’t need to compute the property but still need to provide code
that is run before and after setting a new value, use willSet and didSet.
The code you provide is run any time the value changes outside of an
initializer.”

Excerpt From: Apple Inc. “The Swift Programming Language (Swift 3.1).”
iBooks. https://itun.es/us/jEUH0.l

Note “outside of an initializer”. didSet and willSet are not called in
initializers.


(Marco S Hyman) #6

Uhhh, that is certainly not the results I’d have expected. Perhaps one of the swift language lawyers can explain.

···

On Jul 7, 2017, at 9:48 PM, Zhao Xin <owenzx@gmail.com> wrote:

Thank you very much Marco. But What is “outside of an initializer” really bothers me. **Both** `func bar(keysAndValues:Dictionary<String, String>)` works now. **Are they really outside ?**


(Jordan Rose) #7

The goal is that once the initializer is completed all accesses will go through the setter and therefore trigger willSet/didSet behavior. Since a local function can be assigned to a property or something and get called later, it has to go through the setter as well. So the rules only apply to what's directly in the body of the initializer, not anything nested. (This includes closures, even.) It might be worth a bug against us at Apple to make this more explicit in the documentation, https://bugreport.apple.com <https://bugreport.apple.com/>.

We also have a bug where 'defer' can trigger willSet and didSet behavior as well, SR-1437 <https://bugs.swift.org/browse/SR-1437>. But that really is just a bug.

Jordan

···

On Jul 7, 2017, at 22:50, Marco S Hyman via swift-users <swift-users@swift.org> wrote:

On Jul 7, 2017, at 9:48 PM, Zhao Xin <owenzx@gmail.com> wrote:

Thank you very much Marco. But What is “outside of an initializer” really bothers me. **Both** `func bar(keysAndValues:Dictionary<String, String>)` works now. **Are they really outside ?**

Uhhh, that is certainly not the results I’d have expected. Perhaps one of the swift language lawyers can explain.


(Zhao Xin) #8

Thanks.

Zhao Xin

···

On Tue, Jul 11, 2017 at 2:29 AM, Jordan Rose <jordan_rose@apple.com> wrote:

On Jul 7, 2017, at 22:50, Marco S Hyman via swift-users < > swift-users@swift.org> wrote:

On Jul 7, 2017, at 9:48 PM, Zhao Xin <owenzx@gmail.com> wrote:

Thank you very much Marco. But What is “outside of an initializer” really
bothers me. **Both** `func bar(keysAndValues:Dictionary<String, String>)`
works now. **Are they really outside ?**

Uhhh, that is certainly not the results I’d have expected. Perhaps one of
the swift language lawyers can explain.

The goal is that once the initializer is completed all accesses will go
through the setter and therefore trigger willSet/didSet behavior. Since a
local function can be assigned to a property or something and get called
later, it has to go through the setter as well. So the rules only apply to
what's directly in the body of the initializer, not anything nested. (This
includes closures, even.) It might be worth a bug against us at Apple to
make this more explicit in the documentation, https://bugreport.apple.com.

We also have a bug where 'defer' can trigger willSet and didSet behavior
as well, SR-1437 <https://bugs.swift.org/browse/SR-1437>. But that really
is just a bug.

Jordan