Untuk menggabungkan dua nilai struct Swift, tulis fungsi yang memilih sumber untuk setiap field dan mengembalikan nilai baru. Contoh macOS ini menggabungkan dua gaya panel gambar sintetis, dengan aturan eksplisit untuk penggantian, pemertahanan, pengosongan opsional, dan input yang tidak valid.

Tentukan nilai yang diprioritaskan sebelum menulis fungsi penggabungan

Tugas pembaca adalah menghitung satu gaya panel gambar dari dua nilai yang sudah ada di Mac. Sebut input tersebut base dan incoming: nama-nama ini menjelaskan perannya, sementara kontrak per field menentukan nilai mana yang diprioritaskan. Perhitungan ini hanya menggunakan nilai sintetis di memori.

Swift mendokumentasikan struktur sebagai tipe nilai dan mendukung pembuatan instance melalui initializer. API merge(_:uniquingKeysWith:) yang didokumentasikan Apple adalah milik Dictionary; API tersebut menyelesaikan kunci duplikat melalui closure. Untuk struct kustom ini, definisikan fungsi penggabungan yang implementasinya menyatakan aturan tiap field yang diinginkan. Structures and Classes, Dictionary merge(_:uniquingKeysWith:).

Gunakan kontrak ilustratif berikut. Ini adalah pilihan desain untuk contoh ini, bukan perilaku yang disediakan Swift secara otomatis.

Field Aturan Contoh konsekuensi
title Ambil teks masuk, hapus spasi dan baris baru di sekelilingnya, lalu tolak hasil yang kosong " Tile Study \n" menjadi \"Tile Study\"
spacing Pertahankan nilai dasar; nilainya harus nonnegatif 12 dari nilai dasar diprioritaskan daripada 30 dari nilai masuk
showsGrid Ambil nilai Boolean masuk persis seperti adanya false dari nilai masuk menggantikan true dari nilai dasar
caption Ambil nilai opsional masuk persis seperti adanya nil dari nilai masuk mengosongkan caption dasar

Tabel ini membedakan tiga operasi yang mudah tertukar: memilih sumber, menormalkan nilai yang dipilih, dan memvalidasi hasilnya. Spasi masuk sengaja tidak digunakan. Nilai itu tidak dapat mengubah output, sekalipun berbeda dari spasi dasar.

Foundation’s trimmingCharacters(in:) mengembalikan string baru dengan karakter yang cocok dihapus dari kedua ujungnya. Apple mendokumentasikan penggunaan .whitespacesAndNewlines untuk spasi di sekeliling; string yang seluruhnya terdiri atas karakter tersebut akan menjadi kosong. Penolakan terhadap hasil kosong itu adalah aturan tambahan kita. Foundation trimmingCharacters(in:).

Jalankan contoh lokal lengkap di Mac Anda

Gunakan toolchain Swift 6 untuk contoh ini. Swift.org menyediakan petunjuk instalasi macOS melalui Swiftly dan menyebut Xcode sebagai salah satu opsi untuk pengembangan platform Apple. Buka Terminal dan jalankan swift --version untuk memastikan perintah tersebut tersedia. Jika belum ada, ikuti petunjuk instalasi sebelum melanjutkan. Install Swift on macOS, Getting Started.

Simpan program lengkap berikut sebagai MergeStyles.swift di folder kerja. Program ini mengimpor Foundation, mendefinisikan kedua nilai input, melakukan penggabungan, memeriksa hasil yang diharapkan, dan menguji dua kasus penolakan. Perhitungan ini tidak membuat permintaan jaringan dan tidak menyimpan data aplikasi.

import Foundation

struct PanelStyle: Equatable, Sendable {
    let title: String
    let spacing: Int
    let showsGrid: Bool
    let caption: String?
}

enum StyleMergeError: Error {
    case emptyTitle
    case negativeSpacing
}

func mergeStyles(
    base: PanelStyle,
    incoming: PanelStyle
) throws -> PanelStyle {
    let title = incoming.title.trimmingCharacters(
        in: .whitespacesAndNewlines
    )
    guard !title.isEmpty else {
        throw StyleMergeError.emptyTitle
    }
    guard base.spacing >= 0 else {
        throw StyleMergeError.negativeSpacing
    }

    return PanelStyle(
        title: title,
        spacing: base.spacing,
        showsGrid: incoming.showsGrid,
        caption: incoming.caption
    )
}

let base = PanelStyle(
    title: "Shape Study",
    spacing: 12,
    showsGrid: true,
    caption: "Blue squares"
)
let incoming = PanelStyle(
    title: "  Tile Study \n",
    spacing: 30,
    showsGrid: false,
    caption: nil
)

let expected = PanelStyle(
    title: "Tile Study",
    spacing: 12,
    showsGrid: false,
    caption: nil
)

let result = try mergeStyles(base: base, incoming: incoming)
precondition(result == expected)
precondition(base.title == "Shape Study")
precondition(base.caption == "Blue squares")
precondition(incoming.title == "  Tile Study \n")

print(result.title)
print("spacing: \(result.spacing)")
print("grid: \(result.showsGrid)")
print("caption cleared: \(result.caption == nil)")

let blankTitle = PanelStyle(
    title: " \n\t",
    spacing: 30,
    showsGrid: false,
    caption: nil
)
do {
    _ = try mergeStyles(base: base, incoming: blankTitle)
    preconditionFailure("Expected emptyTitle")
} catch StyleMergeError.emptyTitle {
    print("Rejected blank title")
}

let negativeBase = PanelStyle(
    title: "Shape Study",
    spacing: -1,
    showsGrid: true,
    caption: "Blue squares"
)
do {
    _ = try mergeStyles(base: negativeBase, incoming: incoming)
    preconditionFailure("Expected negativeSpacing")
} catch StyleMergeError.negativeSpacing {
    print("Rejected negative spacing")
}

Dari folder yang berisi file tersebut, jalankan:

swift MergeStyles.swift

Output yang diharapkan ditampilkan di bawah. Swift tidak tersedia di lingkungan riset, jadi program ini diperiksa tetapi tidak dijalankan di sana.

Tile Study
spacing: 12
grid: false
caption cleared: true
Rejected blank title
Rejected negative spacing

Initializer adalah alat bantu pengambilan keputusan utama: setiap argumen menunjukkan field input yang dipilih atau nilai perantara bernama. Tidak ada input yang diubah. Semantik nilai Swift mendukung pendekatan ini; field di sini juga merupakan tipe nilai. Jika kelak Anda menambahkan field bertipe kelas, menyalin struct yang membungkusnya tidak akan membuat salinan instance kelas yang dirujuk secara terpisah. Structures and Classes.

Validasi dilakukan sebelum pembuatan nilai. Judul masuk yang kosong akan memicu error alih-alih diam-diam mengembalikan judul dasar, dan spasi dasar negatif akan memicu error alih-alih diam-diam membatasinya ke nol. Jika kedua kondisi gagal, emptyTitle dilaporkan lebih dahulu karena guard tersebut dijalankan lebih dahulu. Pemanggil harus menangani error sebelum menggunakan hasil.

Tentukan apakah nilai opsional berarti mengosongkan atau mempertahankan

Contoh utama memperlakukan incoming.caption sebagai caption yang diinginkan secara keseluruhan. Karena itu, nil berarti “hasilnya tidak memiliki caption.” Dengan demikian, pengosongan dinyatakan secara eksplisit dalam kontrak, meskipun representasinya hanya berupa opsional.

Persyaratan lain mungkin menyatakan bahwa nil yang masuk berarti “pertahankan caption dasar.” Untuk kebijakan itu, argumen initializer pengganti menjadi caption: incoming.caption ?? base.caption. Operator nil-coalescing Swift memilih operand kiri jika berisi nilai, dan jika tidak, menggunakan operand kanan. Ini adalah fragmen pengganti berlabel, bukan program lengkap kedua. Nil-coalescing operator.

Kebijakan fallback tersebut tidak dapat sekaligus menggunakan nil masuk yang sama untuk meminta pengosongan. Tentukan maknanya sebelum menggunakan ekspresi tersebut. String kosong adalah nilai lain yang berbeda: dengan kedua kebijakan, caption masuk \"\" tetap menjadi caption kosong kecuali Anda secara eksplisit menambahkan normalisasi.

Jika operasi pengeditan di masa mendatang memerlukan ketiga tindakan—mempertahankan, mengganti, dan mengosongkan—representasikan tindakannya secara terpisah. Program lengkap dan independen berikut mengilustrasikan operasi khusus caption menggunakan enum kustom:

enum CaptionChange {
    case keep
    case set(String?)
}

func applying(
    _ change: CaptionChange,
    to caption: String?
) -> String? {
    switch change {
    case .keep:
        return caption
    case .set(let replacement):
        return replacement
    }
}

let original: String? = "Blue squares"
precondition(applying(.keep, to: original) == original)
precondition(applying(.set("Green circles"), to: original)
    == "Green circles")
precondition(applying(.set(nil), to: original) == nil)
print("Caption actions checked")

Enum ini merupakan perluasan ilustratif dari kontrak. Pertahankan fungsi dua nilai awal ketika kedua input merupakan gaya lengkap; perkenalkan tipe tindakan ketika input benar-benar menjelaskan perubahan.

Periksa prioritas dengan nilai yang sengaja dibuat berbeda

Pemeriksaan penggabungan yang berguna membuat field yang bersaing memiliki nilai berbeda. Jika kedua input menggunakan spasi 12 dan sama-sama mengaktifkan grid, pilihan sumber yang salah dapat luput terdeteksi. Sebagai gantinya, contoh menggunakan spasi 12 versus 30, serta true versus false, sehingga hasil yang diharapkan memperlihatkan pilihan tersebut.

Sebelum menyesuaikan fungsi, periksa kasus batas berikut berdasarkan kontrak yang dimaksud:

  • false yang masuk harus tetap false; jangan gunakan OR Boolean untuk menyatakan penggantian.
  • Caption masuk nil harus mengosongkan caption berdasarkan kebijakan utama.
  • Spasi di sekeliling judul harus dihapus, sementara spasi di dalamnya tetap ada.
  • Judul masuk yang hanya berisi spasi harus memicu error tanpa kembali ke nilai dasar.
  • Spasi dasar 0 harus berhasil; spasi dasar negatif harus memicu error.
  • Menukar argumen harus menerapkan aturan pada peran yang baru ditetapkan.

Poin terakhir penting karena penggabungan ini bergantung pada urutan. Jika input contoh dibalik, hasilnya adalah \"Shape Study\", spasi 30, grid true, dan caption \"Blue squares\". Itulah hasil dari aturan yang dinyatakan. Beri nama pada kedua argumen di lokasi pemanggilan agar perannya tetap terlihat.

Saat menambahkan field, perluas tabel dan initializer, lalu tambahkan pemeriksaan dengan nilai yang berbeda. Jika gaya bersarang di masa depan memuat beberapa pengaturan independen, tentukan apakah seluruh nilai bersarang akan diganti atau setiap field-nya memiliki prioritas sendiri. Implementasi saat ini hanya mendefinisikan empat field yang ditampilkan.

Pisahkan konkurensi dari kebijakan penggabungan

Perhitungan ini berjalan secara sinkron: membaca argumennya, memvalidasi nilai yang dipilih, lalu mengembalikan hasil atau memicu error. Perhitungan ini tidak memerlukan task atau actor untuk menetapkan prioritas. Swift mendokumentasikan fungsi asinkron sebagai fungsi yang dapat mengalami penangguhan; menambahkan eksekusi asinkron tidak akan menentukan field mana yang diprioritaskan. Concurrency.

Contoh ini mendeklarasikan PanelStyle sebagai Sendable untuk menyatakan bahwa nilainya dapat melintasi batas konkurensi. Apple mengharuskan struct yang sendable memiliki anggota yang sendable. Konformansi tersebut berkaitan dengan pemindahan nilai; aturan pemilihan field tetap sepenuhnya berasal dari mergeStyles. Sendable.

Untuk tugas panel gambar lokal ini, letakkan kontrak di dekat fungsi dan gunakan input contoh yang nilainya tampak berbeda. Hasilnya seharusnya dapat dijelaskan satu field demi satu field: judul masuk yang dinormalisasi, spasi dasar yang dipertahankan, pengaturan grid masuk, dan caption masuk yang secara eksplisit dapat mengosongkan nilai sebelumnya.

Sumber