cat swift/laboratorio-libreria-swift-pubblicabile.md

Lezione 47 90 min

Laboratorio: libreria Swift pubblicabile

Laboratorio del Modulo 6: trasformare il dominio coupon in un package Swift con target, test, documentazione, CLI demo è CI.

Questo laboratorio chiude il modulo sul tooling professionale. L’obiettivo è creare una libreria Swift che non sia solo codice funzionante, ma un piccolo prodotto tecnico:

  • package organizzato;
  • target chiari;
  • test;
  • documentazione;
  • demo CLI;
  • comandi riproducibili;
  • CI di base.

Obiettivo

Costruire CouponKit.

Struttura finale:

CouponKit/
  Package.swift
  README.md
  Sources/
    CouponCore/
    CouponCLI/
  Tests/
    CouponCoreTests/
  .github/
    workflows/
      swift.yml

Passo 1: creare package

mkdir CouponKit
cd CouponKit
swift package init --type library

Poi riorganizza:

Sources/CouponCore
Tests/CouponCoreTests

Aggiorna i nomi nel manifest.

Passo 2: manifest

// swift-tools-version: 6.0
import PackageDescription

let package = Package(
  name: "CouponKit",
  platforms: [
    .macOS(.v14)
  ],
  products: [
    .library(name: "CouponCore", targets: ["CouponCore"]),
    .executable(name: "coupon-cli", targets: ["CouponCLI"])
  ],
  targets: [
    .target(name: "CouponCore"),
    .executableTarget(
      name: "CouponCLI",
      dependencies: ["CouponCore"]
    ),
    .testTarget(
      name: "CouponCoreTests",
      dependencies: ["CouponCore"]
    )
  ]
)

Passo 3: dominio pubblico minimo

In Sources/CouponCore:

public struct Coupon: Equatable, Sendable {
  public let codice: String
  public let sconto: Sconto
  public let regole: [RegolaCoupon]

  public init(codice: String, sconto: Sconto, regole: [RegolaCoupon]) {
    self.codice = codice
    self.sconto = sconto
    self.regole = regole
  }
}

Ricorda: in una libreria, public va dichiarato intenzionalmente. Non tutto deve essere pubblico.

Passo 4: engine

public struct CouponEngine: Sendable {
  public init() {}

  public func applica(_ coupon: Coupon, a carrello: Carrello) throws -> Carrello {
    try valida(coupon, a: carrello)
    return carrello.applicando(coupon.sconto)
  }
}

La API deve leggere bene.

Passo 5: test

Con Swift Testing:

import Testing
@testable import CouponCore

@Test
func couponPercentualeRiduceTotale() throws {
  let carrello = Carrello.fixture(totaleInCent: 10_000)
  let coupon = Coupon.percentuale10

  let aggiornato = try CouponEngine().applica(coupon, a: carrello)

  #expect(aggiornato.totaleInCent == 9_000)
}

Crea fixture leggibili:

extension Coupon {
  static let percentuale10 = Coupon(...)
}

Passo 6: CLI demo

In Sources/CouponCLI/main.swift:

import CouponCore

let engine = CouponEngine()
let carrello = Carrello.demo
let coupon = Coupon.demo

do {
  let aggiornato = try engine.applica(coupon, a: carrello)
  print("Totale: \(aggiornato.totaleInCent)")
} catch {
  print("Coupon non valido: \(error)")
}

Esegui:

swift run coupon-cli

Passo 7: README

Un README utile contiene:

# CouponKit

Typed coupon modeling and validation for Swift packages.

## Install

Add this package with Swift Package Manager.

## Usage

```swift
let updated = try CouponEngine().applica(coupon, a: carrello)

Development

swift build
swift test
swift run coupon-cli

Il README non deve essere romanzo. Deve permettere di iniziare.

## Passo 8: DocC

Crea:

```text
Sources/CouponCore/CouponCore.docc/CouponCore.md

Con:

# CouponCore

Model coupon rules with strongly typed Swift APIs.

## Overview

Use `CouponEngine` to validate and apply coupons to carts.

Poi documenta i simboli pubblici principali con ///.

Passo 9: CI

name: Swift

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: macos-15

    steps:
      - uses: actions/checkout@v4

      - name: Build
        run: swift build

      - name: Test
        run: swift test

Passo 10: checklist release

Prima di considerare il package pubblicabile:

  • swift build passa;
  • swift test passa;
  • swift run coupon-cli funziona;
  • README spiega installazione e uso;
  • DocC esiste;
  • API pubbliche hanno commenti;
  • errori pubblici sono documentati;
  • CI è configurata;
  • nessuna dipendenza inutile nel core;
  • i nomi dei target sono chiari.

Estensioni

Per rendere il package più professionale:

  • aggiungi semantic versioning;
  • aggiungi changelog;
  • aggiungi badge CI nel README;
  • aggiungi esempi DocC;
  • aggiungi test Linux in matrix;
  • aggiungi benchmark se le performance contano;
  • separa CouponHTTP come target futuro.