swift-copying

1.3.0

A Swift Macro that generates a copying method for struct and class types, similar to Kotlin's copy function for data classes.
yysskk/swift-copying

What's New

1.3.0

2026-07-05T06:07:03Z

This release turns several previously silent or broken declarations into clear compile-time diagnostics, makes the generated method's access level match the type, and adds hosted documentation.

Added

  • A DocC documentation catalog and Swift Package Index metadata (.spi.yml), so the API reference and articles are hosted on the Swift Package Index.
  • visionOS to the list of supported platforms.

Changed

  • The generated copying method now inherits the annotated type's access level instead of always being public. An open type produces a public method and a private type produces a fileprivate one, so the method is callable exactly where the type is.
  • Declarations that would previously have produced broken or silently incorrect copies are now reported as compile-time diagnostics: a copyable stored property without an explicit type annotation, and a var bound through a tuple pattern. Every offending property is reported in a single build.
  • Widened the swift-syntax dependency range to 600.0.0 ..< 605.0.0 so the package resolves alongside other macro packages.

Fixed

  • Include every property in a combined declaration such as let x: Int, y: Int.
  • Wrap generated parameter types in parentheses so function-typed and other compound-typed properties compile.
  • Exclude let constants with an initial value and lazy properties from copying; including them previously failed to compile.

Full Changelog: 1.2.0...1.3.0

swift-copying

CI Swift Versions Platforms License

A Swift macro that generates a copying method for struct, class, and actor types, similar to Kotlin's copy function for data classes.

@Copying adds a copying method that takes one optional argument per stored property and returns a new instance with the properties you pass replaced and the rest carried over from the original.

Installation

Swift Package Manager

Add the package to your Package.swift:

dependencies: [
    .package(url: "https://github.com/yysskk/swift-copying.git", from: "1.2.0")
]

Then add Copying to your target's dependencies:

.target(
    name: "YourTarget",
    dependencies: ["Copying"]
)

Xcode

  1. Choose File → Add Package Dependencies…
  2. Enter the package URL https://github.com/yysskk/swift-copying.git
  3. Pick a dependency rule, then add the Copying library product to your target.

Usage

import Copying

Structs

@Copying
struct Person {
    let name: String
    let age: Int
    let email: String
}

let john = Person(name: "John", age: 30, email: "john@example.com")

// Change one property
let olderJohn = john.copying(age: 31)
// Person(name: "John", age: 31, email: "john@example.com")

// Change several at once
let jane = john.copying(name: "Jane", age: 25)
// Person(name: "Jane", age: 25, email: "john@example.com")

Classes

A class must declare an initializer shaped like a struct's memberwise initializer — one argument per copyable stored property, labelled with the property's name — because the generated method calls it:

@Copying
final class User {
    let id: Int
    var username: String
    var isActive: Bool

    init(id: Int, username: String, isActive: Bool) {
        self.id = id
        self.username = username
        self.isActive = isActive
    }
}

let user = User(id: 1, username: "johndoe", isActive: true)
let inactiveUser = user.copying(isActive: false)

Actors

Actors work the same way. Because copying is actor-isolated, call it with await from outside the actor:

@Copying
actor Counter {
    let id: Int
    var value: Int

    init(id: Int, value: Int) {
        self.id = id
        self.value = value
    }
}

let counter = Counter(id: 1, value: 0)
let advanced = await counter.copying(value: 10)

Optional properties

Every parameter defaults to nil, meaning "keep the current value". For an optional property, the parameter becomes a double optional so you can still reset it:

@Copying
struct Config {
    let name: String
    let timeout: Int?
}

let config = Config(name: "default", timeout: 30)

config.copying(timeout: nil)         // keeps 30 — the nil literal means "no change"
config.copying(timeout: .some(nil))  // resets to nil

let newTimeout: Int? = nil
config.copying(timeout: newTimeout)  // also resets to nil

Because the bare nil literal keeps the current value, reset an optional with .some(nil) or by passing a value of the property's own optional type.

How it works

@Copying is an attached member macro. Applying it to:

@Copying
struct Person {
    let name: String
    let age: Int
}

generates the following method inside Person:

func copying(
    name: (String)? = nil,
    age: (Int)? = nil
) -> Person {
    Person(
        name: name ?? self.name,
        age: age ?? self.age
    )
}

What gets copied

The macro generates a parameter only for stored properties that can take part in a copy.

Skipped silently:

  • static (and class) type properties
  • Computed properties (willSet/didSet-only properties are still stored and are included)
  • lazy properties
  • let constants with an initial value, e.g. let maxValue: Int = 100
  • Immutable tuple bindings, e.g. let (x, y) = (0, 0)

Rejected with a compile-time error:

  • A copyable property without an explicit type annotation, e.g. var count = 0 (a macro cannot infer the type — write var count: Int = 0)
  • A var bound through a tuple pattern, e.g. var (x, y) = (0, 0)
  • A type with no copyable stored properties
  • Applying @Copying to anything other than a struct, class, or actor

The generated method inherits the type's access level: an open type produces a public method, and a private type produces a fileprivate one, so the method is callable exactly where the type is.

Documentation

Full API documentation, including articles on the rules and optional-property semantics, is hosted on the Swift Package Index.

Requirements

  • Swift 6.2+
  • macOS 10.15+ / iOS 13+ / tvOS 13+ / watchOS 6+ / visionOS 1+ / Mac Catalyst 13+

License

swift-copying is available under the MIT license. See LICENSE for details.

Description

  • Swift Tools 6.2.0
View More Packages from this Author

Dependencies

Last updated: Tue Jul 07 2026 11:38:52 GMT-0900 (Hawaii-Aleutian Daylight Time)