set and willSet have two completely different purposes.
set is used similarly to setter methods in other languages, thus the programmer clearly knows why and when he wants/needs to use it.
willSet is comparable to a trigger. So, whenever the value will be set - and this could be outside of the programmer's influence - this function will be called (except for initializers as stated in other answers).
Imagine a public class that you have created and that you offer to other programmers as part of your framework.
Within a willSet you could trigger other functions (e. g. writing a log entry with timestamp), whenever the value will be set. In addition it allows you to take any actions before the value is set, e.g. saving the old value to somewhere else before it gets overwritten.
Of course, you could do everything I described within the set function without using willSet.
But willSet and didSet give you the opportunity to write code which is better readable:
- do whatever you you need to do prior to setting the value in willSet
- set the value in set
- do whatever you need to to after having set the value (e.g. logging or cleaning up) in didSet