Есть ли в Swift аналог оператора with из VBA и Delphi?

В VBA возможна такая запись обращения к свойствам и методам классов/структур:

    With theCustomer
        .Name = "Coho Vineyard"
        .URL  = "http://www.cohovineyard.com/"
        .City = "Redmond"
    End With

В Delphi/Object Pascal, в свою очередь, это делалось так:

    with theCustomer do
        Name := 'Coho Vineyard';
        URL  := 'http://www.cohovineyard.com/';
        City := 'Redmond';
    end;

В Swift ни оператор do { ... } не обеспечивает сокращения записи, ни в замыканиях я такого не нашёл. Я подозреваю, что что-то упустил.

Подскажите, плиз, чем можно заменить записи из приведённых двух примеров в этом языке. Какие есть варианты?


Ответы (2 шт):

Автор решения: VAndrJ

К сожалению, в Swift нет каскадных записей. Можно самому написать некое подобие. Для классов все просто, особенно которые наследуются от NSObject, один раз прописал и можно пользоваться:

protocol Applyable {}

extension Applyable where Self: AnyObject {
    
    @inlinable
    @discardableResult
    func apply(_ block: (Self) throws -> Void) rethrows -> Self {
        try block(self)
        return self
    }
}

extension NSObject: Applyable {}

// Применение
let someView = UIView().apply {
    $0.backgroundColor = .black
}

Так как структуры – value type, соответственно, с ними не получится так же. Чтобы их "завести" нужна промежуточная mutable переменная:

extension Applyable where Self: Any {
    
    @inlinable
    @discardableResult
    func applied(_ block: (inout Self) throws -> Void) rethrows -> Self {
        var mutableCopy = self
        try block(&mutableCopy)
        return mutableCopy
    }
}

struct SomeTestStruct: Applyable {
    var someTestValue: String?
}

// Применение
let someTestStruct = SomeTestStruct().applied {
    $0.someTestValue = "Test"
}
→ Ссылка
Автор решения: Антон Войтов

Объединив два решения – перегрузку операторов и замыкания – из предлагаемых языком Swift и предлагаемых коллегами вариантов, удалось решить свою задачу.

Имеем для примера объявленную структуру и её расширение с искомой функцией (оператором):

struct Cell {
    
    var point: CGPoint
    
}

extension Cell {
    
    @inline(__always)
    static func >> (left: inout Cell, right: (inout Cell) -> Void) {
        right(&left)
    }
    
}

В любом месте, где требуется сокращённая запись работает такой вызов:

        data[x][y] >> {
            $0.point.x = xPart + offsetX
            $0.point.y = yPart + offsetY
        }

В моём решении не требуется пропуск результата через атрибут @discardableResult.

Вторым вариантом решения будет создание нового протокола или расширение стандартного (скажем, Equatable, но я не уверен, что это «законно») с функцией-оператором. Пример создания своего протокола:

protocol Applyable {}

extension Applyable {
    
    @inlinable
    static func >> (left: inout Self, right: (inout Self) -> Void) {
        right(&left)
    }
    
}

extension NSObject: Applyable {
    
}

extension CGPoint: Applyable {
    
}

В таком решении равновозможны обе записи:

        data[x][y] >> {
            $0.point.x = xPart + offsetX
            $0.point.y = yPart + offsetY
        }

и

        data[x][y].point >> {
            $0.x = xPart + offsetX
            $0.y = yPart + offsetY
        }

Но «минусом» будет расширение каждого используемого типа, где требуется или направшивается сокращённая запись. И ещё надо подумать над оператором >>, что-то мне эти «стрелки» не нравятся. ))

→ Ссылка