                     }
                 }
             }
+            """),
+            Example("""
+            class Foo {
+                @IBInspectable var borderColor: UIColor? = nil {
+                    didSet {
+                        updateAppearance()
+                    }
+                }
+            }
             """)
         ],
         triggeringExamples: [

        ]
    )

    public func validate(file: SwiftLintFile, kind: SwiftDeclarationKind,
                         dictionary: SourceKittenDictionary) -> [StyleViolation] {
        guard kind == .varInstance else {
            return []
        }

        // Check if IBInspectable
        let isIBInspectable = dictionary.enclosedSwiftAttributes.contains(.ibinspectable)
        guard isIBInspectable else {
            return []
        }

        let shouldMakeViolation: Bool
        if !file.isMutableProperty(dictionary) {
            shouldMakeViolation = true
        } else if let type = dictionary.typeName,
            Self.supportedTypes.contains(type) {
            shouldMakeViolation = false
        } else {
            // Variable should have explicit type or IB won't recognize it
            // Variable should be of one of the supported types
            shouldMakeViolation = true
        }

        guard shouldMakeViolation else {
            return []
        }

        let location: Location
        if let offset = dictionary.offset {
            location = Location(file: file, byteOffset: offset)
        } else {
            location = Location(file: file.path)
        }

        return [
            StyleViolation(ruleDescription: Self.description,
                           severity: configuration.severity,
                           location: location)
        ]
    }

    private static func createSupportedTypes() -> [String] {
        // "You can add the IBInspectable attribute to any property in a class declaration,
        // class extension, or category of type: boolean, integer or floating point number, string,
        // localized string, rectangle, point, size, color, range, and nil."

        let expandToIncludeOptionals: (String) -> [String] = { [$0, $0 + "!", $0 + "?"] }

        // It seems that only reference types can be used as ImplicitlyUnwrappedOptional or Optional
        return Set(referenceTypes.flatMap(expandToIncludeOptionals) + types + intTypes)
    }()
}

private extension ValidIBInspectableRule {
    final class Visitor: SyntaxVisitor, ViolationsSyntaxVisitor {
        private(set) var violationPositions: [AbsolutePosition] = []

        override func visitPost(_ node: VariableDeclSyntax) {
            if node.isInstanceVariable, node.isIBInspectable, node.hasViolation {
                violationPositions.append(node.letOrVarKeyword.positionAfterSkippingLeadingTrivia)
            }
        }

        override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
            .skipChildren
        }
    }
}

private extension VariableDeclSyntax {
    var isInstanceVariable: Bool {
        guard let modifiers = modifiers else {
            return true
        }

        return !modifiers.contains { modifier in
            modifier.name.text == "static" || modifier.name.text == "class"
        }
    }

    var isIBInspectable: Bool {
        attributes?.contains { attr in
            attr.as(AttributeSyntax.self)?.attributeName.text == "IBInspectable"
        } ?? false
    }

    var hasViolation: Bool {
        isReadOnlyProperty || !isSupportedType
    }

    var isReadOnlyProperty: Bool {
        if letOrVarKeyword.tokenKind == .letKeyword {
            return true
        }

        let computedProperty = bindings.contains { binding in
            binding.accessor != nil
        }

        if !computedProperty {
            return false
        }

        return bindings.allSatisfy { binding in
            guard let accessorBlock = binding.accessor?.as(AccessorBlockSyntax.self) else {
                return true
            }

            // if it has a `get`, it needs to have a `set`, otherwise it's readonly
            if accessorBlock.containsGetAccessor {
                return !accessorBlock.containsSetAccessor
            }

            return false
        }
    }

    var isSupportedType: Bool {
        bindings.allSatisfy { binding in
            guard let type = binding.typeAnnotation else {
                return false
            }

            return ValidIBInspectableRule.supportedTypes.contains(type.type.withoutTrivia().description)
        }
    }
}

private extension AccessorBlockSyntax {
    var containsSetAccessor: Bool {
        return accessors.contains { accessor in
            accessor.accessorKind.tokenKind == .contextualKeyword("set")
        }
    }

    var containsGetAccessor: Bool {
        return accessors.contains { accessor in
            accessor.accessorKind.tokenKind == .contextualKeyword("get")
        }
    }
}
