We are not allowed to use circular associated type requirements.

  protocol ParentPro : class {
    associatedtype Child : ChildPro
    var children:[Child] { get set }
  }

  protocol ChildPro {
    associatedtype Parent : ParentPro
    weak var parent:Parent? { get set }
  }

In this example, the ParentPro protocol requires something conforming to the ChildPro protocol, and ChildPro something conforming to ParentPro. This kind of circular dependency is not allowed. Instead, reorganize the requirements into a hierarchy.

In this case, that means splitting the full type of the parent, from the definition of the child:

  protocol ParentPro : class {
    associatedtype Child : ChildPro
    var children:[Child] { get set }
  }

  protocol ChildPro : Leaf {
    associatedtype Parent : AnyObject
    weak var parent:Parent? { get set }
  }

The definition of the Parent associated type has only the information it needs in the immediate context: that's it's a class, so the parent property can be weak. We did this by making it conform to AnyObject.

Now, we create concrete types.

  class BookShelf : ParentPro {
    var children: [Book] = []
  }

  struct Book : ChildPro {
    weak var parent: BookShelf?
  }

  var shelf:BookShelf = BookShelf()
  var book = Book()

The concrete type BookShelf conforms the ParentPro protocol, while the Book conforms to the ChildPro.

What would really be nice, is if there was a method on the Parent, which could set the parent reference on the child when it's added to the Parent. We can't write the method on ParentPro directly, because without the circular constraint, the full type of the child's parent type is not known.

  extension ParentPro where ChildType.Parent == Self {
    func add(child:ChildType) {
      var theChild = child
      theChild.parent = self
      children.append(theChild)
    }
  }

We've added an extension on ParentPro, which adds the add(child: method, but the where clause only allows the method to exist when the child type's parent type is the type of the container it's getting added to. Thus, making the call to theChild.parent = self type-safe.

  class HappyMeal : ParentPro {
    var children:[Nugget]
  }

  struct Nugget : ChildPro {
    weak var parent:HappyMeal?
  }

Since these are protocols, and not concrete generics, we are free to create totally independent types which gain these behaviors.

  var happyMeal = HappyMeal()
  happyMeal.add(Nugget())

Merely by declaring conforming types, the type-safe extension now works on the HappyMeal as well.