Why this code capturing self inside this block compile in swift?

The compiler has never complained about this. But it should (and I have filed a bug on this point), because the code you've written won't work: the button will not actually do anything when tapped. (The app might even crash, but then again it might not.)

The reason (for both phenomena) is that the compiler misinterprets the term self here to mean the class — which does exist before initialization of the instance.

The solution is to replace let by lazy var. That does work, because now the code will not actually be called until some later, when the instance does exist.