From 627240ecf8ff011c552294ad18e6315eaef64ddc Mon Sep 17 00:00:00 2001
From: Guilherme Souza <grsouza@pm.me>
Date: Wed, 12 Jun 2024 16:22:21 -0300
Subject: [PATCH 1/6] feat(typegen): add swift template

---
 package.json                          |   1 +
 src/server/constants.ts               |   4 +-
 src/server/routes/generators/swift.ts |  40 ++++
 src/server/routes/index.ts            |   2 +
 src/server/server.ts                  |   7 +
 src/server/templates/swift.ts         | 283 +++++++++++++++++++++++
 test/server/typegen.ts                | 315 ++++++++++++++++++++++++++
 7 files changed, 651 insertions(+), 1 deletion(-)
 create mode 100644 src/server/routes/generators/swift.ts
 create mode 100644 src/server/templates/swift.ts

diff --git a/package.json b/package.json
index 365de941..45b3532a 100644
--- a/package.json
+++ b/package.json
@@ -24,6 +24,7 @@
     "docs:export": "PG_META_EXPORT_DOCS=true node --loader ts-node/esm src/server/server.ts > openapi.json",
     "gen:types:typescript": "PG_META_GENERATE_TYPES=typescript node --loader ts-node/esm src/server/server.ts",
     "gen:types:go": "PG_META_GENERATE_TYPES=go node --loader ts-node/esm src/server/server.ts",
+    "gen:types:swift": "PG_META_GENERATE_TYPES=swift node --loader ts-node/esm src/server/server.ts",
     "start": "node dist/server/server.js",
     "dev": "trap 'npm run db:clean' INT && run-s db:clean db:run && nodemon --exec node --loader ts-node/esm src/server/server.ts | pino-pretty --colorize",
     "test": "run-s db:clean db:run test:run db:clean",
diff --git a/src/server/constants.ts b/src/server/constants.ts
index 86415b2a..f7447c45 100644
--- a/src/server/constants.ts
+++ b/src/server/constants.ts
@@ -1,6 +1,7 @@
 import crypto from 'crypto'
 import { PoolConfig } from 'pg'
 import { getSecret } from '../lib/secrets.js'
+import { AccessControl } from './templates/swift.js'
 
 export const PG_META_HOST = process.env.PG_META_HOST || '0.0.0.0'
 export const PG_META_PORT = Number(process.env.PG_META_PORT || 1337)
@@ -40,7 +41,8 @@ export const GENERATE_TYPES_INCLUDED_SCHEMAS = GENERATE_TYPES
   : []
 export const GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS =
   process.env.PG_META_GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS === 'true'
-
+export const GENERATE_TYPES_SWIFT_ACCESS_CONTROL =
+  (process.env.PG_META_GENERATE_TYPES_SWIFT_ACCESS_CONTROL as AccessControl) || 'internal'
 export const DEFAULT_POOL_CONFIG: PoolConfig = {
   max: 1,
   connectionTimeoutMillis: PG_CONN_TIMEOUT_SECS * 1000,
diff --git a/src/server/routes/generators/swift.ts b/src/server/routes/generators/swift.ts
new file mode 100644
index 00000000..2c802fe2
--- /dev/null
+++ b/src/server/routes/generators/swift.ts
@@ -0,0 +1,40 @@
+import type { FastifyInstance } from 'fastify'
+import { PostgresMeta } from '../../../lib/index.js'
+import { DEFAULT_POOL_CONFIG } from '../../constants.js'
+import { extractRequestForLogging } from '../../utils.js'
+import { apply as applySwiftTemplate, AccessControl } from '../../templates/swift.js'
+import { getGeneratorMetadata } from '../../../lib/generators.js'
+
+export default async (fastify: FastifyInstance) => {
+  fastify.get<{
+    Headers: { pg: string }
+    Querystring: {
+      excluded_schemas?: string
+      included_schemas?: string
+      access_control?: AccessControl
+    }
+  }>('/', async (request, reply) => {
+    const connectionString = request.headers.pg
+    const excludedSchemas =
+      request.query.excluded_schemas?.split(',').map((schema) => schema.trim()) ?? []
+    const includedSchemas =
+      request.query.included_schemas?.split(',').map((schema) => schema.trim()) ?? []
+    const accessControl = request.query.access_control ?? 'internal'
+
+    const pgMeta: PostgresMeta = new PostgresMeta({ ...DEFAULT_POOL_CONFIG, connectionString })
+    const { data: generatorMeta, error: generatorMetaError } = await getGeneratorMetadata(pgMeta, {
+      includedSchemas,
+      excludedSchemas,
+    })
+    if (generatorMetaError) {
+      request.log.error({ error: generatorMetaError, request: extractRequestForLogging(request) })
+      reply.code(500)
+      return { error: generatorMetaError.message }
+    }
+
+    return applySwiftTemplate({
+      ...generatorMeta,
+      accessControl,
+    })
+  })
+}
diff --git a/src/server/routes/index.ts b/src/server/routes/index.ts
index 99678515..55b85618 100644
--- a/src/server/routes/index.ts
+++ b/src/server/routes/index.ts
@@ -20,6 +20,7 @@ import TypesRoute from './types.js'
 import ViewsRoute from './views.js'
 import TypeScriptTypeGenRoute from './generators/typescript.js'
 import GoTypeGenRoute from './generators/go.js'
+import SwiftTypeGenRoute from './generators/swift.js'
 import { PG_CONNECTION, CRYPTO_KEY } from '../constants.js'
 
 export default async (fastify: FastifyInstance) => {
@@ -65,4 +66,5 @@ export default async (fastify: FastifyInstance) => {
   fastify.register(ViewsRoute, { prefix: '/views' })
   fastify.register(TypeScriptTypeGenRoute, { prefix: '/generators/typescript' })
   fastify.register(GoTypeGenRoute, { prefix: '/generators/go' })
+  fastify.register(SwiftTypeGenRoute, { prefix: '/generators/swift' })
 }
diff --git a/src/server/server.ts b/src/server/server.ts
index 12371e8a..1c915f33 100644
--- a/src/server/server.ts
+++ b/src/server/server.ts
@@ -9,12 +9,14 @@ import {
   GENERATE_TYPES,
   GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS,
   GENERATE_TYPES_INCLUDED_SCHEMAS,
+  GENERATE_TYPES_SWIFT_ACCESS_CONTROL,
   PG_CONNECTION,
   PG_META_HOST,
   PG_META_PORT,
 } from './constants.js'
 import { apply as applyTypescriptTemplate } from './templates/typescript.js'
 import { apply as applyGoTemplate } from './templates/go.js'
+import { apply as applySwiftTemplate } from './templates/swift.js'
 import { getGeneratorMetadata } from '../lib/generators.js'
 
 const logger = pino({
@@ -52,6 +54,11 @@ async function getTypeOutput(): Promise<string | null> {
         detectOneToOneRelationships: GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS,
       })
       break
+    case 'swift':
+      output = await applySwiftTemplate({
+        ...generatorMetadata,
+        accessControl: GENERATE_TYPES_SWIFT_ACCESS_CONTROL,
+      })
     case 'go':
       output = applyGoTemplate(generatorMetadata)
       break
diff --git a/src/server/templates/swift.ts b/src/server/templates/swift.ts
new file mode 100644
index 00000000..a3b787ce
--- /dev/null
+++ b/src/server/templates/swift.ts
@@ -0,0 +1,283 @@
+import prettier from 'prettier'
+import type {
+  PostgresColumn,
+  PostgresFunction,
+  PostgresMaterializedView,
+  PostgresSchema,
+  PostgresTable,
+  PostgresType,
+  PostgresView,
+} from '../../lib/index.js'
+import type { GeneratorMetadata } from '../../lib/generators.js'
+
+type Operation = 'Select' | 'Insert' | 'Update'
+export type AccessControl = 'internal' | 'public' | 'private' | 'package'
+
+type SwiftGeneratorOptions = {
+  accessControl: AccessControl
+}
+
+function generateEnum(enum_: PostgresType, options: SwiftGeneratorOptions): string {
+  return `
+${options.accessControl} enum ${toUpperCamelCase(enum_.name)}: String, Codable, Hashable, Sendable {
+${enum_.enums.map((case_) => `${ident(1)}case ${toLowerCamelCase(case_)} = "${case_}"`).join('\n')}
+}
+`.trim()
+}
+
+function generateTableStructsForOperations(
+  schema: PostgresSchema,
+  schemas: PostgresSchema[],
+  table: PostgresTable | PostgresView | PostgresMaterializedView,
+  tables: PostgresTable[],
+  views: PostgresView[],
+  columns: PostgresColumn[] | undefined,
+  types: PostgresType[],
+  operations: Operation[],
+  options: SwiftGeneratorOptions
+): string[] {
+  return operations.map((operation) =>
+    generateTableStruct(schema, schemas, table, tables, views, columns, types, operation, options)
+  )
+}
+
+function generateTableStruct(
+  schema: PostgresSchema,
+  schemas: PostgresSchema[],
+  table: PostgresTable | PostgresView | PostgresMaterializedView,
+  tables: PostgresTable[],
+  views: PostgresView[],
+  columns: PostgresColumn[] | undefined,
+  types: PostgresType[],
+  operation: Operation,
+  options: SwiftGeneratorOptions
+): string {
+  const columnEntries: {
+    raw_name: string
+    formatted_name: string
+    type: string
+    nullable: boolean
+  }[] =
+    columns?.map((column) => {
+      let nullable: boolean
+
+      if (operation === 'Insert') {
+        nullable =
+          column.is_nullable || column.is_identity || column.is_generated || !!column.default_value
+      } else if (operation === 'Update') {
+        nullable = true
+      } else {
+        nullable = column.is_nullable
+      }
+
+      return {
+        raw_name: column.name,
+        formatted_name: toLowerCamelCase(column.name),
+        type: pgTypeToSwiftType(column.format, { types, schemas, tables, views }),
+        nullable,
+      }
+    }) ?? []
+
+  const identity = columns?.find((column) => column.is_identity)
+  const structName = `${toUpperCamelCase(table.name)}${operation}`
+
+  let output = `
+extension ${toUpperCamelCase(schema.name)}Schema {
+${ident(1)}${options.accessControl} struct ${structName}: Codable, Hashable, Sendable {
+${columnEntries.map(({ formatted_name, type, nullable }) => `${ident(2)}${options.accessControl} let ${formatted_name}: ${type}${nullable ? '?' : ''}`).join('\n')}
+
+${ident(2)}${options.accessControl} enum CodingKeys: String, CodingKey {
+${columnEntries.map(({ raw_name, formatted_name }) => `${ident(3)}case ${formatted_name} = "${raw_name}"`).join('\n')}
+${ident(2)}}
+}
+`
+
+  if (operation === 'Select' && identity) {
+    const identityEntry = columnEntries.find((entry) => entry.raw_name === identity.name)
+    if (identityEntry) {
+      output += `extension ${toUpperCamelCase(schema.name)}Schema.${structName}: Identifiable {
+${identityEntry.formatted_name !== 'id' ? `${ident(2)}${options.accessControl} var id: ${identityEntry.type} { ${identityEntry.formatted_name} }` : ''}
+}
+  `
+    }
+  }
+
+  return output.trim()
+}
+
+export const apply = async ({
+  schemas,
+  tables,
+  views,
+  columns,
+  types,
+  accessControl,
+}: GeneratorMetadata & SwiftGeneratorOptions): Promise<string> => {
+  const columnsByTableId = columns
+    .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+    .reduce(
+      (acc, curr) => {
+        acc[curr.table_id] ??= []
+        acc[curr.table_id].push(curr)
+        return acc
+      },
+      {} as Record<string, PostgresColumn[]>
+    )
+
+  const compositeTypes = types.filter((type) => type.attributes.length > 0)
+  const enums = types
+    .filter((type) => type.enums.length > 0)
+    .sort(({ name: a }, { name: b }) => a.localeCompare(b))
+
+  let output = `
+import Foundation
+import Supabase
+
+// MARK: - Enums
+${enums.map((enum_) => generateEnum(enum_, { accessControl })).join('\n')}
+
+// MARK: - Schemas
+${schemas.map((schema) => `${accessControl} enum ${toUpperCamelCase(schema.name)}Schema {}`).join('\n')}
+
+// MARK: - Tables
+${tables
+  .flatMap((table) =>
+    generateTableStructsForOperations(
+      schemas.find((schema) => schema.name === table.schema)!,
+      schemas,
+      table,
+      tables,
+      views,
+      columnsByTableId[table.id],
+      types,
+      ['Select', 'Insert', 'Update'],
+      { accessControl }
+    )
+  )
+  .join('\n')}
+`.trim()
+
+  return output
+}
+
+// TODO: Make this more robust. Currently doesn't handle range types - returns them as unknown.
+const pgTypeToSwiftType = (
+  pgType: string,
+  {
+    types,
+    schemas,
+    tables,
+    views,
+  }: {
+    types: PostgresType[]
+    schemas: PostgresSchema[]
+    tables: PostgresTable[]
+    views: PostgresView[]
+  }
+): string => {
+  if (pgType === 'bool') {
+    return 'Bool'
+  } else if (pgType === 'int2') {
+    return 'Int16'
+  } else if (pgType === 'int4') {
+    return 'Int32'
+  } else if (pgType === 'int8') {
+    return 'Int64'
+  } else if (pgType === 'float4') {
+    return 'Float'
+  } else if (pgType === 'float8') {
+    return 'Double'
+  } else if (pgType === 'uuid') {
+    return 'UUID'
+  } else if (
+    [
+      'bytea',
+      'bpchar',
+      'varchar',
+      'date',
+      'text',
+      'citext',
+      'time',
+      'timetz',
+      'timestamp',
+      'timestamptz',
+      'vector',
+    ].includes(pgType)
+  ) {
+    return 'String'
+  } else if (['json', 'jsonb'].includes(pgType)) {
+    return 'AnyJSON'
+  } else if (pgType === 'void') {
+    return 'Void'
+  } else if (pgType === 'record') {
+    return 'JSONObject'
+  } else if (pgType.startsWith('_')) {
+    return `[${pgTypeToSwiftType(pgType.substring(1), { types, schemas, tables, views })}]`
+  } else {
+    const enumType = types.find((type) => type.name === pgType && type.enums.length > 0)
+
+    if (enumType) {
+      return `${toUpperCamelCase(enumType.name)}`
+    }
+
+    const compositeType = types.find((type) => type.name === pgType && type.attributes.length > 0)
+    if (compositeType) {
+      return `${toUpperCamelCase(compositeType.name)}`
+    }
+
+    const tableRowType = tables.find((table) => table.name === pgType)
+    if (tableRowType) {
+      return `${toUpperCamelCase(tableRowType.name)}`
+    }
+
+    const viewRowType = views.find((view) => view.name === pgType)
+    if (viewRowType) {
+      return `${toUpperCamelCase(viewRowType.name)}`
+    }
+
+    return 'unknown'
+  }
+}
+
+function ident(level: number, options: { width: number } = { width: 2 }): string {
+  return ' '.repeat(level * options.width)
+}
+
+function toLowerCamelCase(input: string): string {
+  // Split the input string by spaces and non-alphanumeric characters
+  const words = input.split(/[\s\-_]+/)
+
+  // Map over the words array to transform each word
+  const camelCaseWords = words.map((word, index) => {
+    // Lowercase the entire word
+    const lowerCasedWord = word.toLowerCase()
+
+    // Capitalize the first letter if it's not the first word
+    if (index !== 0) {
+      return lowerCasedWord.charAt(0).toUpperCase() + lowerCasedWord.slice(1)
+    }
+
+    // Return the word as-is if it's the first word
+    return lowerCasedWord
+  })
+
+  // Join the words back together
+  return camelCaseWords.join('')
+}
+
+function toUpperCamelCase(input: string): string {
+  // Split the input string by spaces and non-alphanumeric characters
+  const words = input.split(/[\s\-_]+/)
+
+  // Map over the words array to transform each word
+  const camelCaseWords = words.map((word) => {
+    // Lowercase the entire word
+    const lowerCasedWord = word.toLowerCase()
+
+    // Capitalize the first letter of each word
+    return lowerCasedWord.charAt(0).toUpperCase() + lowerCasedWord.slice(1)
+  })
+
+  // Join the words back together
+  return camelCaseWords.join('')
+}
diff --git a/test/server/typegen.ts b/test/server/typegen.ts
index 4614d434..76af21ed 100644
--- a/test/server/typegen.ts
+++ b/test/server/typegen.ts
@@ -1701,3 +1701,318 @@ type PublicCompositeTypeWithArrayAttribute struct {
 }"
   `)
 })
+
+test('typegen: swift', async () => {
+  const { body } = await app.inject({ method: 'GET', path: '/generators/swift' })
+  expect(body).toMatchInlineSnapshot(`
+    "import Foundation
+    import Supabase
+
+    // MARK: - Enums
+    internal enum MemeStatus: String, Codable, Hashable, Sendable {
+      case new = "new"
+      case old = "old"
+      case retired = "retired"
+    }
+    internal enum UserStatus: String, Codable, Hashable, Sendable {
+      case active = "ACTIVE"
+      case inactive = "INACTIVE"
+    }
+
+    // MARK: - Schemas
+    internal enum PublicSchema {}
+
+    // MARK: - Tables
+    extension PublicSchema {
+      internal struct UsersSelect: Codable, Hashable, Sendable {
+        internal let id: Int64
+        internal let name: String?
+        internal let status: UserStatus?
+
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema.UsersSelect: Identifiable {
+
+    }
+    extension PublicSchema {
+      internal struct UsersInsert: Codable, Hashable, Sendable {
+        internal let id: Int64?
+        internal let name: String?
+        internal let status: UserStatus?
+
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      internal struct UsersUpdate: Codable, Hashable, Sendable {
+        internal let id: Int64?
+        internal let name: String?
+        internal let status: UserStatus?
+
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      internal struct TodosSelect: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let id: Int64
+        internal let userId: Int64
+
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+    extension PublicSchema.TodosSelect: Identifiable {
+
+    }
+    extension PublicSchema {
+      internal struct TodosInsert: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let id: Int64?
+        internal let userId: Int64
+
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+    extension PublicSchema {
+      internal struct TodosUpdate: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let id: Int64?
+        internal let userId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+    extension PublicSchema {
+      internal struct UsersAuditSelect: Codable, Hashable, Sendable {
+        internal let createdAt: String?
+        internal let id: Int64
+        internal let previousValue: AnyJSON?
+        internal let userId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case createdAt = "created_at"
+          case id = "id"
+          case previousValue = "previous_value"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema.UsersAuditSelect: Identifiable {
+
+    }
+    extension PublicSchema {
+      internal struct UsersAuditInsert: Codable, Hashable, Sendable {
+        internal let createdAt: String?
+        internal let id: Int64?
+        internal let previousValue: AnyJSON?
+        internal let userId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case createdAt = "created_at"
+          case id = "id"
+          case previousValue = "previous_value"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      internal struct UsersAuditUpdate: Codable, Hashable, Sendable {
+        internal let createdAt: String?
+        internal let id: Int64?
+        internal let previousValue: AnyJSON?
+        internal let userId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case createdAt = "created_at"
+          case id = "id"
+          case previousValue = "previous_value"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      internal struct UserDetailsSelect: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let userId: Int64
+
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      internal struct UserDetailsInsert: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let userId: Int64
+
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      internal struct UserDetailsUpdate: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let userId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      internal struct EmptySelect: Codable, Hashable, Sendable {
+
+
+        internal enum CodingKeys: String, CodingKey {
+
+        }
+    }
+    extension PublicSchema {
+      internal struct EmptyInsert: Codable, Hashable, Sendable {
+
+
+        internal enum CodingKeys: String, CodingKey {
+
+        }
+    }
+    extension PublicSchema {
+      internal struct EmptyUpdate: Codable, Hashable, Sendable {
+
+
+        internal enum CodingKeys: String, CodingKey {
+
+        }
+    }
+    extension PublicSchema {
+      internal struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable {
+        internal let col1: UserDetails?
+        internal let col2: AView?
+
+        internal enum CodingKeys: String, CodingKey {
+          case col1 = "col1"
+          case col2 = "col2"
+        }
+    }
+    extension PublicSchema {
+      internal struct TableWithOtherTablesRowTypeInsert: Codable, Hashable, Sendable {
+        internal let col1: UserDetails?
+        internal let col2: AView?
+
+        internal enum CodingKeys: String, CodingKey {
+          case col1 = "col1"
+          case col2 = "col2"
+        }
+    }
+    extension PublicSchema {
+      internal struct TableWithOtherTablesRowTypeUpdate: Codable, Hashable, Sendable {
+        internal let col1: UserDetails?
+        internal let col2: AView?
+
+        internal enum CodingKeys: String, CodingKey {
+          case col1 = "col1"
+          case col2 = "col2"
+        }
+    }
+    extension PublicSchema {
+      internal struct CategorySelect: Codable, Hashable, Sendable {
+        internal let id: Int32
+        internal let name: String
+
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+        }
+    }
+    extension PublicSchema {
+      internal struct CategoryInsert: Codable, Hashable, Sendable {
+        internal let id: Int32?
+        internal let name: String
+
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+        }
+    }
+    extension PublicSchema {
+      internal struct CategoryUpdate: Codable, Hashable, Sendable {
+        internal let id: Int32?
+        internal let name: String?
+
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+        }
+    }
+    extension PublicSchema {
+      internal struct MemesSelect: Codable, Hashable, Sendable {
+        internal let category: Int32?
+        internal let createdAt: String
+        internal let id: Int32
+        internal let metadata: AnyJSON?
+        internal let name: String
+        internal let status: MemeStatus?
+
+        internal enum CodingKeys: String, CodingKey {
+          case category = "category"
+          case createdAt = "created_at"
+          case id = "id"
+          case metadata = "metadata"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      internal struct MemesInsert: Codable, Hashable, Sendable {
+        internal let category: Int32?
+        internal let createdAt: String
+        internal let id: Int32?
+        internal let metadata: AnyJSON?
+        internal let name: String
+        internal let status: MemeStatus?
+
+        internal enum CodingKeys: String, CodingKey {
+          case category = "category"
+          case createdAt = "created_at"
+          case id = "id"
+          case metadata = "metadata"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      internal struct MemesUpdate: Codable, Hashable, Sendable {
+        internal let category: Int32?
+        internal let createdAt: String?
+        internal let id: Int32?
+        internal let metadata: AnyJSON?
+        internal let name: String?
+        internal let status: MemeStatus?
+
+        internal enum CodingKeys: String, CodingKey {
+          case category = "category"
+          case createdAt = "created_at"
+          case id = "id"
+          case metadata = "metadata"
+          case name = "name"
+          case status = "status"
+        }
+    }"
+  `)
+})

From f0461d4db581e38bb8bc7caddfa4ce30c30202da Mon Sep 17 00:00:00 2001
From: Guilherme Souza <grsouza@pm.me>
Date: Wed, 12 Jun 2024 16:35:52 -0300
Subject: [PATCH 2/6] test: add test case for table with identity other than id

---
 test/db/00-init.sql    |   5 +
 test/server/typegen.ts | 429 +++++++++++++++++++++++++----------------
 2 files changed, 266 insertions(+), 168 deletions(-)

diff --git a/test/db/00-init.sql b/test/db/00-init.sql
index 4a09c445..e28a0b16 100644
--- a/test/db/00-init.sql
+++ b/test/db/00-init.sql
@@ -132,3 +132,8 @@ create table table_with_other_tables_row_type (
   col1 user_details,
   col2 a_view
 );
+
+create table table_with_primary_key_other_than_id (
+  other_id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+  name text
+);
diff --git a/test/server/typegen.ts b/test/server/typegen.ts
index 76af21ed..47d8759f 100644
--- a/test/server/typegen.ts
+++ b/test/server/typegen.ts
@@ -103,6 +103,21 @@ test('typegen: typescript', async () => {
             }
             Relationships: []
           }
+          table_with_primary_key_other_than_id: {
+            Row: {
+              name: string | null
+              other_id: number
+            }
+            Insert: {
+              name?: string | null
+              other_id?: number
+            }
+            Update: {
+              name?: string | null
+              other_id?: number
+            }
+            Relationships: []
+          }
           todos: {
             Row: {
               details: string | null
@@ -605,6 +620,21 @@ test('typegen w/ one-to-one relationships', async () => {
             }
             Relationships: []
           }
+          table_with_primary_key_other_than_id: {
+            Row: {
+              name: string | null
+              other_id: number
+            }
+            Insert: {
+              name?: string | null
+              other_id?: number
+            }
+            Update: {
+              name?: string | null
+              other_id?: number
+            }
+            Relationships: []
+          }
           todos: {
             Row: {
               details: string | null
@@ -1119,6 +1149,21 @@ test('typegen: typescript w/ one-to-one relationships', async () => {
             }
             Relationships: []
           }
+          table_with_primary_key_other_than_id: {
+            Row: {
+              name: string | null
+              other_id: number
+            }
+            Insert: {
+              name?: string | null
+              other_id?: number
+            }
+            Update: {
+              name?: string | null
+              other_id?: number
+            }
+            Relationships: []
+          }
           todos: {
             Row: {
               details: string | null
@@ -1531,174 +1576,189 @@ test('typegen: go', async () => {
   expect(body).toMatchInlineSnapshot(`
     "package database
 
-import "database/sql"
-
-type PublicUsersSelect struct {
-  Id     int64          \`json:"id"\`
-  Name   sql.NullString \`json:"name"\`
-  Status sql.NullString \`json:"status"\`
-}
-
-type PublicUsersInsert struct {
-  Id     sql.NullInt64  \`json:"id"\`
-  Name   sql.NullString \`json:"name"\`
-  Status sql.NullString \`json:"status"\`
-}
-
-type PublicUsersUpdate struct {
-  Id     sql.NullInt64  \`json:"id"\`
-  Name   sql.NullString \`json:"name"\`
-  Status sql.NullString \`json:"status"\`
-}
-
-type PublicTodosSelect struct {
-  Details sql.NullString \`json:"details"\`
-  Id      int64          \`json:"id"\`
-  UserId  int64          \`json:"user-id"\`
-}
-
-type PublicTodosInsert struct {
-  Details sql.NullString \`json:"details"\`
-  Id      sql.NullInt64  \`json:"id"\`
-  UserId  int64          \`json:"user-id"\`
-}
-
-type PublicTodosUpdate struct {
-  Details sql.NullString \`json:"details"\`
-  Id      sql.NullInt64  \`json:"id"\`
-  UserId  sql.NullInt64  \`json:"user-id"\`
-}
-
-type PublicUsersAuditSelect struct {
-  CreatedAt     sql.NullString \`json:"created_at"\`
-  Id            int64          \`json:"id"\`
-  PreviousValue interface{}    \`json:"previous_value"\`
-  UserId        sql.NullInt64  \`json:"user_id"\`
-}
-
-type PublicUsersAuditInsert struct {
-  CreatedAt     sql.NullString \`json:"created_at"\`
-  Id            sql.NullInt64  \`json:"id"\`
-  PreviousValue interface{}    \`json:"previous_value"\`
-  UserId        sql.NullInt64  \`json:"user_id"\`
-}
-
-type PublicUsersAuditUpdate struct {
-  CreatedAt     sql.NullString \`json:"created_at"\`
-  Id            sql.NullInt64  \`json:"id"\`
-  PreviousValue interface{}    \`json:"previous_value"\`
-  UserId        sql.NullInt64  \`json:"user_id"\`
-}
-
-type PublicUserDetailsSelect struct {
-  Details sql.NullString \`json:"details"\`
-  UserId  int64          \`json:"user_id"\`
-}
-
-type PublicUserDetailsInsert struct {
-  Details sql.NullString \`json:"details"\`
-  UserId  int64          \`json:"user_id"\`
-}
-
-type PublicUserDetailsUpdate struct {
-  Details sql.NullString \`json:"details"\`
-  UserId  sql.NullInt64  \`json:"user_id"\`
-}
-
-type PublicEmptySelect struct {
-
-}
-
-type PublicEmptyInsert struct {
-
-}
-
-type PublicEmptyUpdate struct {
-
-}
-
-type PublicTableWithOtherTablesRowTypeSelect struct {
-  Col1 interface{} \`json:"col1"\`
-  Col2 interface{} \`json:"col2"\`
-}
-
-type PublicTableWithOtherTablesRowTypeInsert struct {
-  Col1 interface{} \`json:"col1"\`
-  Col2 interface{} \`json:"col2"\`
-}
-
-type PublicTableWithOtherTablesRowTypeUpdate struct {
-  Col1 interface{} \`json:"col1"\`
-  Col2 interface{} \`json:"col2"\`
-}
-
-type PublicCategorySelect struct {
-  Id   int32  \`json:"id"\`
-  Name string \`json:"name"\`
-}
-
-type PublicCategoryInsert struct {
-  Id   sql.NullInt32 \`json:"id"\`
-  Name string        \`json:"name"\`
-}
-
-type PublicCategoryUpdate struct {
-  Id   sql.NullInt32  \`json:"id"\`
-  Name sql.NullString \`json:"name"\`
-}
-
-type PublicMemesSelect struct {
-  Category  sql.NullInt32  \`json:"category"\`
-  CreatedAt string         \`json:"created_at"\`
-  Id        int32          \`json:"id"\`
-  Metadata  interface{}    \`json:"metadata"\`
-  Name      string         \`json:"name"\`
-  Status    sql.NullString \`json:"status"\`
-}
-
-type PublicMemesInsert struct {
-  Category  sql.NullInt32  \`json:"category"\`
-  CreatedAt string         \`json:"created_at"\`
-  Id        sql.NullInt32  \`json:"id"\`
-  Metadata  interface{}    \`json:"metadata"\`
-  Name      string         \`json:"name"\`
-  Status    sql.NullString \`json:"status"\`
-}
-
-type PublicMemesUpdate struct {
-  Category  sql.NullInt32  \`json:"category"\`
-  CreatedAt sql.NullString \`json:"created_at"\`
-  Id        sql.NullInt32  \`json:"id"\`
-  Metadata  interface{}    \`json:"metadata"\`
-  Name      sql.NullString \`json:"name"\`
-  Status    sql.NullString \`json:"status"\`
-}
-
-type PublicTodosViewSelect struct {
-  Details sql.NullString \`json:"details"\`
-  Id      sql.NullInt64  \`json:"id"\`
-  UserId  sql.NullInt64  \`json:"user-id"\`
-}
-
-type PublicUsersViewSelect struct {
-  Id     sql.NullInt64  \`json:"id"\`
-  Name   sql.NullString \`json:"name"\`
-  Status sql.NullString \`json:"status"\`
-}
-
-type PublicAViewSelect struct {
-  Id sql.NullInt64 \`json:"id"\`
-}
-
-type PublicTodosMatviewSelect struct {
-  Details sql.NullString \`json:"details"\`
-  Id      sql.NullInt64  \`json:"id"\`
-  UserId  sql.NullInt64  \`json:"user-id"\`
-}
-
-type PublicCompositeTypeWithArrayAttribute struct {
-  MyTextArray interface{} \`json:"my_text_array"\`
-}"
+    import "database/sql"
+
+    type PublicUsersSelect struct {
+      Id     int64          \`json:"id"\`
+      Name   sql.NullString \`json:"name"\`
+      Status sql.NullString \`json:"status"\`
+    }
+
+    type PublicUsersInsert struct {
+      Id     sql.NullInt64  \`json:"id"\`
+      Name   sql.NullString \`json:"name"\`
+      Status sql.NullString \`json:"status"\`
+    }
+
+    type PublicUsersUpdate struct {
+      Id     sql.NullInt64  \`json:"id"\`
+      Name   sql.NullString \`json:"name"\`
+      Status sql.NullString \`json:"status"\`
+    }
+
+    type PublicTodosSelect struct {
+      Details sql.NullString \`json:"details"\`
+      Id      int64          \`json:"id"\`
+      UserId  int64          \`json:"user-id"\`
+    }
+
+    type PublicTodosInsert struct {
+      Details sql.NullString \`json:"details"\`
+      Id      sql.NullInt64  \`json:"id"\`
+      UserId  int64          \`json:"user-id"\`
+    }
+
+    type PublicTodosUpdate struct {
+      Details sql.NullString \`json:"details"\`
+      Id      sql.NullInt64  \`json:"id"\`
+      UserId  sql.NullInt64  \`json:"user-id"\`
+    }
+
+    type PublicUsersAuditSelect struct {
+      CreatedAt     sql.NullString \`json:"created_at"\`
+      Id            int64          \`json:"id"\`
+      PreviousValue interface{}    \`json:"previous_value"\`
+      UserId        sql.NullInt64  \`json:"user_id"\`
+    }
+
+    type PublicUsersAuditInsert struct {
+      CreatedAt     sql.NullString \`json:"created_at"\`
+      Id            sql.NullInt64  \`json:"id"\`
+      PreviousValue interface{}    \`json:"previous_value"\`
+      UserId        sql.NullInt64  \`json:"user_id"\`
+    }
+
+    type PublicUsersAuditUpdate struct {
+      CreatedAt     sql.NullString \`json:"created_at"\`
+      Id            sql.NullInt64  \`json:"id"\`
+      PreviousValue interface{}    \`json:"previous_value"\`
+      UserId        sql.NullInt64  \`json:"user_id"\`
+    }
+
+    type PublicUserDetailsSelect struct {
+      Details sql.NullString \`json:"details"\`
+      UserId  int64          \`json:"user_id"\`
+    }
+
+    type PublicUserDetailsInsert struct {
+      Details sql.NullString \`json:"details"\`
+      UserId  int64          \`json:"user_id"\`
+    }
+
+    type PublicUserDetailsUpdate struct {
+      Details sql.NullString \`json:"details"\`
+      UserId  sql.NullInt64  \`json:"user_id"\`
+    }
+
+    type PublicEmptySelect struct {
+
+    }
+
+    type PublicEmptyInsert struct {
+
+    }
+
+    type PublicEmptyUpdate struct {
+
+    }
+
+    type PublicTableWithOtherTablesRowTypeSelect struct {
+      Col1 interface{} \`json:"col1"\`
+      Col2 interface{} \`json:"col2"\`
+    }
+
+    type PublicTableWithOtherTablesRowTypeInsert struct {
+      Col1 interface{} \`json:"col1"\`
+      Col2 interface{} \`json:"col2"\`
+    }
+
+    type PublicTableWithOtherTablesRowTypeUpdate struct {
+      Col1 interface{} \`json:"col1"\`
+      Col2 interface{} \`json:"col2"\`
+    }
+
+    type PublicTableWithPrimaryKeyOtherThanIdSelect struct {
+      Name    sql.NullString \`json:"name"\`
+      OtherId int64          \`json:"other_id"\`
+    }
+
+    type PublicTableWithPrimaryKeyOtherThanIdInsert struct {
+      Name    sql.NullString \`json:"name"\`
+      OtherId sql.NullInt64  \`json:"other_id"\`
+    }
+
+    type PublicTableWithPrimaryKeyOtherThanIdUpdate struct {
+      Name    sql.NullString \`json:"name"\`
+      OtherId sql.NullInt64  \`json:"other_id"\`
+    }
+
+    type PublicCategorySelect struct {
+      Id   int32  \`json:"id"\`
+      Name string \`json:"name"\`
+    }
+
+    type PublicCategoryInsert struct {
+      Id   sql.NullInt32 \`json:"id"\`
+      Name string        \`json:"name"\`
+    }
+
+    type PublicCategoryUpdate struct {
+      Id   sql.NullInt32  \`json:"id"\`
+      Name sql.NullString \`json:"name"\`
+    }
+
+    type PublicMemesSelect struct {
+      Category  sql.NullInt32  \`json:"category"\`
+      CreatedAt string         \`json:"created_at"\`
+      Id        int32          \`json:"id"\`
+      Metadata  interface{}    \`json:"metadata"\`
+      Name      string         \`json:"name"\`
+      Status    sql.NullString \`json:"status"\`
+    }
+
+    type PublicMemesInsert struct {
+      Category  sql.NullInt32  \`json:"category"\`
+      CreatedAt string         \`json:"created_at"\`
+      Id        sql.NullInt32  \`json:"id"\`
+      Metadata  interface{}    \`json:"metadata"\`
+      Name      string         \`json:"name"\`
+      Status    sql.NullString \`json:"status"\`
+    }
+
+    type PublicMemesUpdate struct {
+      Category  sql.NullInt32  \`json:"category"\`
+      CreatedAt sql.NullString \`json:"created_at"\`
+      Id        sql.NullInt32  \`json:"id"\`
+      Metadata  interface{}    \`json:"metadata"\`
+      Name      sql.NullString \`json:"name"\`
+      Status    sql.NullString \`json:"status"\`
+    }
+
+    type PublicTodosViewSelect struct {
+      Details sql.NullString \`json:"details"\`
+      Id      sql.NullInt64  \`json:"id"\`
+      UserId  sql.NullInt64  \`json:"user-id"\`
+    }
+
+    type PublicUsersViewSelect struct {
+      Id     sql.NullInt64  \`json:"id"\`
+      Name   sql.NullString \`json:"name"\`
+      Status sql.NullString \`json:"status"\`
+    }
+
+    type PublicAViewSelect struct {
+      Id sql.NullInt64 \`json:"id"\`
+    }
+
+    type PublicTodosMatviewSelect struct {
+      Details sql.NullString \`json:"details"\`
+      Id      sql.NullInt64  \`json:"id"\`
+      UserId  sql.NullInt64  \`json:"user-id"\`
+    }
+
+    type PublicCompositeTypeWithArrayAttribute struct {
+      MyTextArray interface{} \`json:"my_text_array"\`
+    }"
   `)
 })
 
@@ -1930,6 +1990,39 @@ test('typegen: swift', async () => {
           case col2 = "col2"
         }
     }
+    extension PublicSchema {
+      internal struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable {
+        internal let name: String?
+        internal let otherId: Int64
+
+        internal enum CodingKeys: String, CodingKey {
+          case name = "name"
+          case otherId = "other_id"
+        }
+    }
+    extension PublicSchema.TableWithPrimaryKeyOtherThanIdSelect: Identifiable {
+        internal var id: Int64 { otherId }
+    }
+    extension PublicSchema {
+      internal struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable {
+        internal let name: String?
+        internal let otherId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case name = "name"
+          case otherId = "other_id"
+        }
+    }
+    extension PublicSchema {
+      internal struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable {
+        internal let name: String?
+        internal let otherId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case name = "name"
+          case otherId = "other_id"
+        }
+    }
     extension PublicSchema {
       internal struct CategorySelect: Codable, Hashable, Sendable {
         internal let id: Int32

From 79f62be1699a68b952747976aa2c915bbeed1583 Mon Sep 17 00:00:00 2001
From: Guilherme Souza <grsouza@pm.me>
Date: Wed, 12 Jun 2024 16:41:11 -0300
Subject: [PATCH 3/6] update test snapshots

---
 test/lib/tables.ts | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/test/lib/tables.ts b/test/lib/tables.ts
index 6b588d24..c4c934e7 100644
--- a/test/lib/tables.ts
+++ b/test/lib/tables.ts
@@ -114,19 +114,19 @@ test('list', async () => {
       ],
       "relationships": [
         {
-          "constraint_name": "user_details_user_id_fkey",
-          "source_column_name": "user_id",
+          "constraint_name": "todos_user-id_fkey",
+          "source_column_name": "user-id",
           "source_schema": "public",
-          "source_table_name": "user_details",
+          "source_table_name": "todos",
           "target_column_name": "id",
           "target_table_name": "users",
           "target_table_schema": "public",
         },
         {
-          "constraint_name": "todos_user-id_fkey",
-          "source_column_name": "user-id",
+          "constraint_name": "user_details_user_id_fkey",
+          "source_column_name": "user_id",
           "source_schema": "public",
-          "source_table_name": "todos",
+          "source_table_name": "user_details",
           "target_column_name": "id",
           "target_table_name": "users",
           "target_table_schema": "public",
@@ -178,19 +178,19 @@ test('list without columns', async () => {
       ],
       "relationships": [
         {
-          "constraint_name": "user_details_user_id_fkey",
-          "source_column_name": "user_id",
+          "constraint_name": "todos_user-id_fkey",
+          "source_column_name": "user-id",
           "source_schema": "public",
-          "source_table_name": "user_details",
+          "source_table_name": "todos",
           "target_column_name": "id",
           "target_table_name": "users",
           "target_table_schema": "public",
         },
         {
-          "constraint_name": "todos_user-id_fkey",
-          "source_column_name": "user-id",
+          "constraint_name": "user_details_user_id_fkey",
+          "source_column_name": "user_id",
           "source_schema": "public",
-          "source_table_name": "todos",
+          "source_table_name": "user_details",
           "target_column_name": "id",
           "target_table_name": "users",
           "target_table_schema": "public",

From a75f2485345fa0ae0abd31465bb3a0bbfe72150e Mon Sep 17 00:00:00 2001
From: Guilherme Souza <grsouza@pm.me>
Date: Wed, 12 Jun 2024 16:48:58 -0300
Subject: [PATCH 4/6] test: add public access control test case for swift
 generator

---
 test/server/typegen.ts | 352 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 352 insertions(+)

diff --git a/test/server/typegen.ts b/test/server/typegen.ts
index 47d8759f..f5ee3591 100644
--- a/test/server/typegen.ts
+++ b/test/server/typegen.ts
@@ -2109,3 +2109,355 @@ test('typegen: swift', async () => {
     }"
   `)
 })
+
+test('typegen: swift w/ public access control', async () => {
+  const { body } = await app.inject({
+    method: 'GET',
+    path: '/generators/swift',
+    query: { access_control: 'public' },
+  })
+  expect(body).toMatchInlineSnapshot(`
+    "import Foundation
+    import Supabase
+
+    // MARK: - Enums
+    public enum MemeStatus: String, Codable, Hashable, Sendable {
+      case new = "new"
+      case old = "old"
+      case retired = "retired"
+    }
+    public enum UserStatus: String, Codable, Hashable, Sendable {
+      case active = "ACTIVE"
+      case inactive = "INACTIVE"
+    }
+
+    // MARK: - Schemas
+    public enum PublicSchema {}
+
+    // MARK: - Tables
+    extension PublicSchema {
+      public struct UsersSelect: Codable, Hashable, Sendable {
+        public let id: Int64
+        public let name: String?
+        public let status: UserStatus?
+
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema.UsersSelect: Identifiable {
+
+    }
+    extension PublicSchema {
+      public struct UsersInsert: Codable, Hashable, Sendable {
+        public let id: Int64?
+        public let name: String?
+        public let status: UserStatus?
+
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      public struct UsersUpdate: Codable, Hashable, Sendable {
+        public let id: Int64?
+        public let name: String?
+        public let status: UserStatus?
+
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      public struct TodosSelect: Codable, Hashable, Sendable {
+        public let details: String?
+        public let id: Int64
+        public let userId: Int64
+
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+    extension PublicSchema.TodosSelect: Identifiable {
+
+    }
+    extension PublicSchema {
+      public struct TodosInsert: Codable, Hashable, Sendable {
+        public let details: String?
+        public let id: Int64?
+        public let userId: Int64
+
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+    extension PublicSchema {
+      public struct TodosUpdate: Codable, Hashable, Sendable {
+        public let details: String?
+        public let id: Int64?
+        public let userId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+    extension PublicSchema {
+      public struct UsersAuditSelect: Codable, Hashable, Sendable {
+        public let createdAt: String?
+        public let id: Int64
+        public let previousValue: AnyJSON?
+        public let userId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case createdAt = "created_at"
+          case id = "id"
+          case previousValue = "previous_value"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema.UsersAuditSelect: Identifiable {
+
+    }
+    extension PublicSchema {
+      public struct UsersAuditInsert: Codable, Hashable, Sendable {
+        public let createdAt: String?
+        public let id: Int64?
+        public let previousValue: AnyJSON?
+        public let userId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case createdAt = "created_at"
+          case id = "id"
+          case previousValue = "previous_value"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      public struct UsersAuditUpdate: Codable, Hashable, Sendable {
+        public let createdAt: String?
+        public let id: Int64?
+        public let previousValue: AnyJSON?
+        public let userId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case createdAt = "created_at"
+          case id = "id"
+          case previousValue = "previous_value"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      public struct UserDetailsSelect: Codable, Hashable, Sendable {
+        public let details: String?
+        public let userId: Int64
+
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      public struct UserDetailsInsert: Codable, Hashable, Sendable {
+        public let details: String?
+        public let userId: Int64
+
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      public struct UserDetailsUpdate: Codable, Hashable, Sendable {
+        public let details: String?
+        public let userId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case userId = "user_id"
+        }
+    }
+    extension PublicSchema {
+      public struct EmptySelect: Codable, Hashable, Sendable {
+
+
+        public enum CodingKeys: String, CodingKey {
+
+        }
+    }
+    extension PublicSchema {
+      public struct EmptyInsert: Codable, Hashable, Sendable {
+
+
+        public enum CodingKeys: String, CodingKey {
+
+        }
+    }
+    extension PublicSchema {
+      public struct EmptyUpdate: Codable, Hashable, Sendable {
+
+
+        public enum CodingKeys: String, CodingKey {
+
+        }
+    }
+    extension PublicSchema {
+      public struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable {
+        public let col1: UserDetails?
+        public let col2: AView?
+
+        public enum CodingKeys: String, CodingKey {
+          case col1 = "col1"
+          case col2 = "col2"
+        }
+    }
+    extension PublicSchema {
+      public struct TableWithOtherTablesRowTypeInsert: Codable, Hashable, Sendable {
+        public let col1: UserDetails?
+        public let col2: AView?
+
+        public enum CodingKeys: String, CodingKey {
+          case col1 = "col1"
+          case col2 = "col2"
+        }
+    }
+    extension PublicSchema {
+      public struct TableWithOtherTablesRowTypeUpdate: Codable, Hashable, Sendable {
+        public let col1: UserDetails?
+        public let col2: AView?
+
+        public enum CodingKeys: String, CodingKey {
+          case col1 = "col1"
+          case col2 = "col2"
+        }
+    }
+    extension PublicSchema {
+      public struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable {
+        public let name: String?
+        public let otherId: Int64
+
+        public enum CodingKeys: String, CodingKey {
+          case name = "name"
+          case otherId = "other_id"
+        }
+    }
+    extension PublicSchema.TableWithPrimaryKeyOtherThanIdSelect: Identifiable {
+        public var id: Int64 { otherId }
+    }
+    extension PublicSchema {
+      public struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable {
+        public let name: String?
+        public let otherId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case name = "name"
+          case otherId = "other_id"
+        }
+    }
+    extension PublicSchema {
+      public struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable {
+        public let name: String?
+        public let otherId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case name = "name"
+          case otherId = "other_id"
+        }
+    }
+    extension PublicSchema {
+      public struct CategorySelect: Codable, Hashable, Sendable {
+        public let id: Int32
+        public let name: String
+
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+        }
+    }
+    extension PublicSchema {
+      public struct CategoryInsert: Codable, Hashable, Sendable {
+        public let id: Int32?
+        public let name: String
+
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+        }
+    }
+    extension PublicSchema {
+      public struct CategoryUpdate: Codable, Hashable, Sendable {
+        public let id: Int32?
+        public let name: String?
+
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+        }
+    }
+    extension PublicSchema {
+      public struct MemesSelect: Codable, Hashable, Sendable {
+        public let category: Int32?
+        public let createdAt: String
+        public let id: Int32
+        public let metadata: AnyJSON?
+        public let name: String
+        public let status: MemeStatus?
+
+        public enum CodingKeys: String, CodingKey {
+          case category = "category"
+          case createdAt = "created_at"
+          case id = "id"
+          case metadata = "metadata"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      public struct MemesInsert: Codable, Hashable, Sendable {
+        public let category: Int32?
+        public let createdAt: String
+        public let id: Int32?
+        public let metadata: AnyJSON?
+        public let name: String
+        public let status: MemeStatus?
+
+        public enum CodingKeys: String, CodingKey {
+          case category = "category"
+          case createdAt = "created_at"
+          case id = "id"
+          case metadata = "metadata"
+          case name = "name"
+          case status = "status"
+        }
+    }
+    extension PublicSchema {
+      public struct MemesUpdate: Codable, Hashable, Sendable {
+        public let category: Int32?
+        public let createdAt: String?
+        public let id: Int32?
+        public let metadata: AnyJSON?
+        public let name: String?
+        public let status: MemeStatus?
+
+        public enum CodingKeys: String, CodingKey {
+          case category = "category"
+          case createdAt = "created_at"
+          case id = "id"
+          case metadata = "metadata"
+          case name = "name"
+          case status = "status"
+        }
+    }"
+  `)
+})

From 93c7e4761eca235082efdc2709d8cd4587a904a0 Mon Sep 17 00:00:00 2001
From: Guilherme Souza <grsouza@pm.me>
Date: Thu, 13 Jun 2024 10:46:06 -0300
Subject: [PATCH 5/6] feat: generate materializedviews, views and composite
 types for swift

---
 src/server/templates/swift.ts | 284 +++++++++++++++++++++-------------
 test/server/typegen.ts        | 168 +++++++++++++++++---
 2 files changed, 323 insertions(+), 129 deletions(-)

diff --git a/src/server/templates/swift.ts b/src/server/templates/swift.ts
index a3b787ce..66e469fb 100644
--- a/src/server/templates/swift.ts
+++ b/src/server/templates/swift.ts
@@ -19,44 +19,45 @@ type SwiftGeneratorOptions = {
 
 function generateEnum(enum_: PostgresType, options: SwiftGeneratorOptions): string {
   return `
-${options.accessControl} enum ${toUpperCamelCase(enum_.name)}: String, Codable, Hashable, Sendable {
-${enum_.enums.map((case_) => `${ident(1)}case ${toLowerCamelCase(case_)} = "${case_}"`).join('\n')}
+${options.accessControl} enum ${formatForSwiftTypeName(enum_.name)}: String, Codable, Hashable, Sendable {
+${enum_.enums.map((case_) => `${ident(1)}case ${formatForSwiftPropertyName(case_)} = "${case_}"`).join('\n')}
 }
 `.trim()
 }
 
 function generateTableStructsForOperations(
   schema: PostgresSchema,
-  schemas: PostgresSchema[],
   table: PostgresTable | PostgresView | PostgresMaterializedView,
-  tables: PostgresTable[],
-  views: PostgresView[],
   columns: PostgresColumn[] | undefined,
-  types: PostgresType[],
   operations: Operation[],
-  options: SwiftGeneratorOptions
+  options: SwiftGeneratorOptions,
+  {
+    types,
+    views,
+    tables,
+  }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] }
 ): string[] {
   return operations.map((operation) =>
-    generateTableStruct(schema, schemas, table, tables, views, columns, types, operation, options)
+    generateTableStruct(schema, table, columns, operation, options, { types, views, tables })
   )
 }
 
 function generateTableStruct(
   schema: PostgresSchema,
-  schemas: PostgresSchema[],
   table: PostgresTable | PostgresView | PostgresMaterializedView,
-  tables: PostgresTable[],
-  views: PostgresView[],
   columns: PostgresColumn[] | undefined,
-  types: PostgresType[],
   operation: Operation,
-  options: SwiftGeneratorOptions
+  options: SwiftGeneratorOptions,
+  {
+    types,
+    views,
+    tables,
+  }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] }
 ): string {
   const columnEntries: {
     raw_name: string
     formatted_name: string
     type: string
-    nullable: boolean
   }[] =
     columns?.map((column) => {
       let nullable: boolean
@@ -72,19 +73,18 @@ function generateTableStruct(
 
       return {
         raw_name: column.name,
-        formatted_name: toLowerCamelCase(column.name),
-        type: pgTypeToSwiftType(column.format, { types, schemas, tables, views }),
-        nullable,
+        formatted_name: formatForSwiftPropertyName(column.name),
+        type: pgTypeToSwiftType(column.format, nullable, { types, views, tables }),
       }
     }) ?? []
 
   const identity = columns?.find((column) => column.is_identity)
-  const structName = `${toUpperCamelCase(table.name)}${operation}`
+  const structName = `${formatForSwiftTypeName(table.name)}${operation}`
 
   let output = `
-extension ${toUpperCamelCase(schema.name)}Schema {
+extension ${formatForSwiftTypeName(schema.name)}Schema {
 ${ident(1)}${options.accessControl} struct ${structName}: Codable, Hashable, Sendable {
-${columnEntries.map(({ formatted_name, type, nullable }) => `${ident(2)}${options.accessControl} let ${formatted_name}: ${type}${nullable ? '?' : ''}`).join('\n')}
+${columnEntries.map(({ formatted_name, type }) => `${ident(2)}${options.accessControl} let ${formatted_name}: ${type}`).join('\n')}
 
 ${ident(2)}${options.accessControl} enum CodingKeys: String, CodingKey {
 ${columnEntries.map(({ raw_name, formatted_name }) => `${ident(3)}case ${formatted_name} = "${raw_name}"`).join('\n')}
@@ -95,7 +95,7 @@ ${ident(2)}}
   if (operation === 'Select' && identity) {
     const identityEntry = columnEntries.find((entry) => entry.raw_name === identity.name)
     if (identityEntry) {
-      output += `extension ${toUpperCamelCase(schema.name)}Schema.${structName}: Identifiable {
+      output += `extension ${formatForSwiftTypeName(schema.name)}Schema.${structName}: Identifiable {
 ${identityEntry.formatted_name !== 'id' ? `${ident(2)}${options.accessControl} var id: ${identityEntry.type} { ${identityEntry.formatted_name} }` : ''}
 }
   `
@@ -105,10 +105,62 @@ ${identityEntry.formatted_name !== 'id' ? `${ident(2)}${options.accessControl} v
   return output.trim()
 }
 
+function gnerateCompositeTypeStruct(
+  schema: PostgresSchema,
+  type: PostgresType,
+  options: SwiftGeneratorOptions,
+  {
+    types,
+    views,
+    tables,
+  }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] }
+): string {
+  const typeWithRetrievedAttributes = {
+    ...type,
+    attributes: type.attributes.map((attribute) => {
+      const type = types.find((type) => type.id === attribute.type_id)
+      return {
+        ...attribute,
+        type,
+      }
+    }),
+  }
+
+  const attributeEntries: {
+    formatted_name: string
+    type: string
+    raw_name: string
+  }[] = typeWithRetrievedAttributes.attributes.map((attribute) => {
+    return {
+      formatted_name: formatForSwiftTypeName(attribute.name),
+      type: pgTypeToSwiftType(attribute.type!.format, false, { types, views, tables }),
+      raw_name: attribute.name,
+    }
+  })
+
+  let output = `extension ${formatForSwiftTypeName(schema.name)}Schema {
+${ident(1)}${options.accessControl} struct ${formatForSwiftTypeName(type.name)}: Codable, Hashable, Sendable {
+${attributeEntries
+  .map((entry) => `${ident(2)}${options.accessControl} let ${entry.formatted_name}: ${entry.type}`)
+  .join('\n')}
+
+${ident(2)}${options.accessControl} enum CodingKeys: String, CodingKey {
+${attributeEntries
+  .map((entry) => `${ident(3)}case ${entry.formatted_name} = "${entry.raw_name}"`)
+  .join('\n')}
+${ident(2)}}
+${ident(1)}}
+}
+}`
+
+  return output.trim()
+}
+
 export const apply = async ({
   schemas,
   tables,
   views,
+  materializedViews,
   columns,
   types,
   accessControl,
@@ -134,61 +186,95 @@ import Foundation
 import Supabase
 
 // MARK: - Enums
-${enums.map((enum_) => generateEnum(enum_, { accessControl })).join('\n')}
+${enums.map((enum_) => generateEnum(enum_, { accessControl })).join('\n\n')}
 
 // MARK: - Schemas
-${schemas.map((schema) => `${accessControl} enum ${toUpperCamelCase(schema.name)}Schema {}`).join('\n')}
+${schemas.map((schema) => `${accessControl} enum ${formatForSwiftTypeName(schema.name)}Schema {}`).join('\n\n')}
 
 // MARK: - Tables
 ${tables
   .flatMap((table) =>
     generateTableStructsForOperations(
       schemas.find((schema) => schema.name === table.schema)!,
-      schemas,
       table,
-      tables,
-      views,
       columnsByTableId[table.id],
-      types,
       ['Select', 'Insert', 'Update'],
-      { accessControl }
+      { accessControl },
+      { types, views, tables }
     )
   )
-  .join('\n')}
+  .join('\n\n')}
+
+// MARK: - Views
+${views
+  .flatMap((view) => {
+    generateTableStructsForOperations(
+      schemas.find((schema) => schema.name === view.schema)!,
+      view,
+      columnsByTableId[view.id],
+      ['Select'],
+      { accessControl },
+      { types, views, tables }
+    )
+  })
+  .join('\n\n')}
+
+// MARK: - Materialized Views
+${materializedViews
+  .flatMap((materializedView) =>
+    generateTableStructsForOperations(
+      schemas.find((schema) => schema.name === materializedView.schema)!,
+      materializedView,
+      columnsByTableId[materializedView.id],
+      ['Select'],
+      { accessControl },
+      { types, views, tables }
+    )
+  )
+  .join('\n\n')}
+
+// MARK: - Composite Types
+${compositeTypes
+  .map((compositeType) =>
+    gnerateCompositeTypeStruct(
+      schemas.find((schema) => schema.name === compositeType.schema)!,
+      compositeType,
+      { accessControl },
+      { types, views, tables }
+    )
+  )
+  .join('\n\n')}
 `.trim()
 
   return output
 }
 
-// TODO: Make this more robust. Currently doesn't handle range types - returns them as unknown.
+// TODO: Make this more robust. Currently doesn't handle range types - returns them as string.
 const pgTypeToSwiftType = (
   pgType: string,
+  nullable: boolean,
   {
     types,
-    schemas,
-    tables,
     views,
-  }: {
-    types: PostgresType[]
-    schemas: PostgresSchema[]
-    tables: PostgresTable[]
-    views: PostgresView[]
-  }
+    tables,
+  }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] }
 ): string => {
+  let swiftType: string
+
   if (pgType === 'bool') {
-    return 'Bool'
+    swiftType = 'Bool'
   } else if (pgType === 'int2') {
-    return 'Int16'
+    swiftType = 'Int16'
   } else if (pgType === 'int4') {
-    return 'Int32'
+    swiftType = 'Int32'
   } else if (pgType === 'int8') {
-    return 'Int64'
+    swiftType = 'Int64'
   } else if (pgType === 'float4') {
-    return 'Float'
+    swiftType = 'Float'
   } else if (pgType === 'float8') {
-    return 'Double'
+    swiftType = 'Double'
   } else if (pgType === 'uuid') {
-    return 'UUID'
+    swiftType = 'UUID'
   } else if (
     [
       'bytea',
@@ -204,80 +290,68 @@ const pgTypeToSwiftType = (
       'vector',
     ].includes(pgType)
   ) {
-    return 'String'
+    swiftType = 'String'
   } else if (['json', 'jsonb'].includes(pgType)) {
-    return 'AnyJSON'
+    swiftType = 'AnyJSON'
   } else if (pgType === 'void') {
-    return 'Void'
+    swiftType = 'Void'
   } else if (pgType === 'record') {
-    return 'JSONObject'
+    swiftType = 'JSONObject'
   } else if (pgType.startsWith('_')) {
-    return `[${pgTypeToSwiftType(pgType.substring(1), { types, schemas, tables, views })}]`
+    swiftType = `[${pgTypeToSwiftType(pgType.substring(1), false, { types, views, tables })}]`
   } else {
-    const enumType = types.find((type) => type.name === pgType && type.enums.length > 0)
-
-    if (enumType) {
-      return `${toUpperCamelCase(enumType.name)}`
-    }
+    const allTypes: { name: string; schema: string }[] = [...types, ...views, ...tables]
+    const type = allTypes.find((type) => type.name === pgType)
 
-    const compositeType = types.find((type) => type.name === pgType && type.attributes.length > 0)
-    if (compositeType) {
-      return `${toUpperCamelCase(compositeType.name)}`
+    if (type) {
+      swiftType = `${formatForSwiftTypeName(type.schema)}Schema.${formatForSwiftTypeName(type.name)}`
+    } else {
+      swiftType = 'AnyJSON'
     }
-
-    const tableRowType = tables.find((table) => table.name === pgType)
-    if (tableRowType) {
-      return `${toUpperCamelCase(tableRowType.name)}`
-    }
-
-    const viewRowType = views.find((view) => view.name === pgType)
-    if (viewRowType) {
-      return `${toUpperCamelCase(viewRowType.name)}`
-    }
-
-    return 'unknown'
   }
+
+  return `${swiftType}${nullable ? '?' : ''}`
 }
 
 function ident(level: number, options: { width: number } = { width: 2 }): string {
   return ' '.repeat(level * options.width)
 }
 
-function toLowerCamelCase(input: string): string {
-  // Split the input string by spaces and non-alphanumeric characters
-  const words = input.split(/[\s\-_]+/)
-
-  // Map over the words array to transform each word
-  const camelCaseWords = words.map((word, index) => {
-    // Lowercase the entire word
-    const lowerCasedWord = word.toLowerCase()
-
-    // Capitalize the first letter if it's not the first word
-    if (index !== 0) {
-      return lowerCasedWord.charAt(0).toUpperCase() + lowerCasedWord.slice(1)
-    }
-
-    // Return the word as-is if it's the first word
-    return lowerCasedWord
-  })
-
-  // Join the words back together
-  return camelCaseWords.join('')
+/**
+ * Converts a Postgres name to PascalCase.
+ *
+ * @example
+ * ```ts
+ * formatForSwiftTypeName('pokedex') // Pokedex
+ * formatForSwiftTypeName('pokemon_center') // PokemonCenter
+ * formatForSwiftTypeName('victory-road') // VictoryRoad
+ * formatForSwiftTypeName('pokemon league') // PokemonLeague
+ * ```
+ */
+function formatForSwiftTypeName(name: string): string {
+  return name
+    .split(/[^a-zA-Z0-9]/)
+    .map((word) => `${word[0].toUpperCase()}${word.slice(1)}`)
+    .join('')
 }
 
-function toUpperCamelCase(input: string): string {
-  // Split the input string by spaces and non-alphanumeric characters
-  const words = input.split(/[\s\-_]+/)
-
-  // Map over the words array to transform each word
-  const camelCaseWords = words.map((word) => {
-    // Lowercase the entire word
-    const lowerCasedWord = word.toLowerCase()
-
-    // Capitalize the first letter of each word
-    return lowerCasedWord.charAt(0).toUpperCase() + lowerCasedWord.slice(1)
-  })
-
-  // Join the words back together
-  return camelCaseWords.join('')
+/**
+ * Converts a Postgres name to pascalCase.
+ *
+ * @example
+ * ```ts
+ * formatForSwiftTypeName('pokedex') // pokedex
+ * formatForSwiftTypeName('pokemon_center') // pokemonCenter
+ * formatForSwiftTypeName('victory-road') // victoryRoad
+ * formatForSwiftTypeName('pokemon league') // pokemonLeague
+ * ```
+ */
+function formatForSwiftPropertyName(name: string): string {
+  return name
+    .split(/[^a-zA-Z0-9]/)
+    .map((word, index) => {
+      const lowerWord = word.toLowerCase()
+      return index !== 0 ? lowerWord.charAt(0).toUpperCase() + lowerWord.slice(1) : lowerWord
+    })
+    .join('')
 }
diff --git a/test/server/typegen.ts b/test/server/typegen.ts
index f5ee3591..717358a3 100644
--- a/test/server/typegen.ts
+++ b/test/server/typegen.ts
@@ -1774,6 +1774,7 @@ test('typegen: swift', async () => {
       case old = "old"
       case retired = "retired"
     }
+
     internal enum UserStatus: String, Codable, Hashable, Sendable {
       case active = "ACTIVE"
       case inactive = "INACTIVE"
@@ -1787,7 +1788,7 @@ test('typegen: swift', async () => {
       internal struct UsersSelect: Codable, Hashable, Sendable {
         internal let id: Int64
         internal let name: String?
-        internal let status: UserStatus?
+        internal let status: PublicSchema.UserStatus?
 
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
@@ -1798,11 +1799,12 @@ test('typegen: swift', async () => {
     extension PublicSchema.UsersSelect: Identifiable {
 
     }
+
     extension PublicSchema {
       internal struct UsersInsert: Codable, Hashable, Sendable {
         internal let id: Int64?
         internal let name: String?
-        internal let status: UserStatus?
+        internal let status: PublicSchema.UserStatus?
 
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
@@ -1810,11 +1812,12 @@ test('typegen: swift', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       internal struct UsersUpdate: Codable, Hashable, Sendable {
         internal let id: Int64?
         internal let name: String?
-        internal let status: UserStatus?
+        internal let status: PublicSchema.UserStatus?
 
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
@@ -1822,6 +1825,7 @@ test('typegen: swift', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       internal struct TodosSelect: Codable, Hashable, Sendable {
         internal let details: String?
@@ -1837,6 +1841,7 @@ test('typegen: swift', async () => {
     extension PublicSchema.TodosSelect: Identifiable {
 
     }
+
     extension PublicSchema {
       internal struct TodosInsert: Codable, Hashable, Sendable {
         internal let details: String?
@@ -1849,6 +1854,7 @@ test('typegen: swift', async () => {
           case userId = "user-id"
         }
     }
+
     extension PublicSchema {
       internal struct TodosUpdate: Codable, Hashable, Sendable {
         internal let details: String?
@@ -1861,6 +1867,7 @@ test('typegen: swift', async () => {
           case userId = "user-id"
         }
     }
+
     extension PublicSchema {
       internal struct UsersAuditSelect: Codable, Hashable, Sendable {
         internal let createdAt: String?
@@ -1878,6 +1885,7 @@ test('typegen: swift', async () => {
     extension PublicSchema.UsersAuditSelect: Identifiable {
 
     }
+
     extension PublicSchema {
       internal struct UsersAuditInsert: Codable, Hashable, Sendable {
         internal let createdAt: String?
@@ -1892,6 +1900,7 @@ test('typegen: swift', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       internal struct UsersAuditUpdate: Codable, Hashable, Sendable {
         internal let createdAt: String?
@@ -1906,6 +1915,7 @@ test('typegen: swift', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       internal struct UserDetailsSelect: Codable, Hashable, Sendable {
         internal let details: String?
@@ -1916,6 +1926,7 @@ test('typegen: swift', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       internal struct UserDetailsInsert: Codable, Hashable, Sendable {
         internal let details: String?
@@ -1926,6 +1937,7 @@ test('typegen: swift', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       internal struct UserDetailsUpdate: Codable, Hashable, Sendable {
         internal let details: String?
@@ -1936,6 +1948,7 @@ test('typegen: swift', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       internal struct EmptySelect: Codable, Hashable, Sendable {
 
@@ -1944,6 +1957,7 @@ test('typegen: swift', async () => {
 
         }
     }
+
     extension PublicSchema {
       internal struct EmptyInsert: Codable, Hashable, Sendable {
 
@@ -1952,6 +1966,7 @@ test('typegen: swift', async () => {
 
         }
     }
+
     extension PublicSchema {
       internal struct EmptyUpdate: Codable, Hashable, Sendable {
 
@@ -1960,36 +1975,40 @@ test('typegen: swift', async () => {
 
         }
     }
+
     extension PublicSchema {
       internal struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable {
-        internal let col1: UserDetails?
-        internal let col2: AView?
+        internal let col1: PublicSchema.UserDetails?
+        internal let col2: PublicSchema.AView?
 
         internal enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
     }
+
     extension PublicSchema {
       internal struct TableWithOtherTablesRowTypeInsert: Codable, Hashable, Sendable {
-        internal let col1: UserDetails?
-        internal let col2: AView?
+        internal let col1: PublicSchema.UserDetails?
+        internal let col2: PublicSchema.AView?
 
         internal enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
     }
+
     extension PublicSchema {
       internal struct TableWithOtherTablesRowTypeUpdate: Codable, Hashable, Sendable {
-        internal let col1: UserDetails?
-        internal let col2: AView?
+        internal let col1: PublicSchema.UserDetails?
+        internal let col2: PublicSchema.AView?
 
         internal enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
     }
+
     extension PublicSchema {
       internal struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable {
         internal let name: String?
@@ -2003,6 +2022,7 @@ test('typegen: swift', async () => {
     extension PublicSchema.TableWithPrimaryKeyOtherThanIdSelect: Identifiable {
         internal var id: Int64 { otherId }
     }
+
     extension PublicSchema {
       internal struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable {
         internal let name: String?
@@ -2013,6 +2033,7 @@ test('typegen: swift', async () => {
           case otherId = "other_id"
         }
     }
+
     extension PublicSchema {
       internal struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable {
         internal let name: String?
@@ -2023,6 +2044,7 @@ test('typegen: swift', async () => {
           case otherId = "other_id"
         }
     }
+
     extension PublicSchema {
       internal struct CategorySelect: Codable, Hashable, Sendable {
         internal let id: Int32
@@ -2033,6 +2055,7 @@ test('typegen: swift', async () => {
           case name = "name"
         }
     }
+
     extension PublicSchema {
       internal struct CategoryInsert: Codable, Hashable, Sendable {
         internal let id: Int32?
@@ -2043,6 +2066,7 @@ test('typegen: swift', async () => {
           case name = "name"
         }
     }
+
     extension PublicSchema {
       internal struct CategoryUpdate: Codable, Hashable, Sendable {
         internal let id: Int32?
@@ -2053,6 +2077,7 @@ test('typegen: swift', async () => {
           case name = "name"
         }
     }
+
     extension PublicSchema {
       internal struct MemesSelect: Codable, Hashable, Sendable {
         internal let category: Int32?
@@ -2060,7 +2085,7 @@ test('typegen: swift', async () => {
         internal let id: Int32
         internal let metadata: AnyJSON?
         internal let name: String
-        internal let status: MemeStatus?
+        internal let status: PublicSchema.MemeStatus?
 
         internal enum CodingKeys: String, CodingKey {
           case category = "category"
@@ -2071,6 +2096,7 @@ test('typegen: swift', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       internal struct MemesInsert: Codable, Hashable, Sendable {
         internal let category: Int32?
@@ -2078,7 +2104,7 @@ test('typegen: swift', async () => {
         internal let id: Int32?
         internal let metadata: AnyJSON?
         internal let name: String
-        internal let status: MemeStatus?
+        internal let status: PublicSchema.MemeStatus?
 
         internal enum CodingKeys: String, CodingKey {
           case category = "category"
@@ -2089,6 +2115,7 @@ test('typegen: swift', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       internal struct MemesUpdate: Codable, Hashable, Sendable {
         internal let category: Int32?
@@ -2096,7 +2123,7 @@ test('typegen: swift', async () => {
         internal let id: Int32?
         internal let metadata: AnyJSON?
         internal let name: String?
-        internal let status: MemeStatus?
+        internal let status: PublicSchema.MemeStatus?
 
         internal enum CodingKeys: String, CodingKey {
           case category = "category"
@@ -2106,6 +2133,39 @@ test('typegen: swift', async () => {
           case name = "name"
           case status = "status"
         }
+    }
+
+    // MARK: - Views
+
+
+
+
+
+
+    // MARK: - Materialized Views
+    extension PublicSchema {
+      internal struct TodosMatviewSelect: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let id: Int64?
+        internal let userId: Int64?
+
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+
+    // MARK: - Composite Types
+    extension PublicSchema {
+      internal struct CompositeTypeWithArrayAttribute: Codable, Hashable, Sendable {
+        internal let MyTextArray: AnyJSON
+
+        internal enum CodingKeys: String, CodingKey {
+          case MyTextArray = "my_text_array"
+        }
+      }
+    }
     }"
   `)
 })
@@ -2126,6 +2186,7 @@ test('typegen: swift w/ public access control', async () => {
       case old = "old"
       case retired = "retired"
     }
+
     public enum UserStatus: String, Codable, Hashable, Sendable {
       case active = "ACTIVE"
       case inactive = "INACTIVE"
@@ -2139,7 +2200,7 @@ test('typegen: swift w/ public access control', async () => {
       public struct UsersSelect: Codable, Hashable, Sendable {
         public let id: Int64
         public let name: String?
-        public let status: UserStatus?
+        public let status: PublicSchema.UserStatus?
 
         public enum CodingKeys: String, CodingKey {
           case id = "id"
@@ -2150,11 +2211,12 @@ test('typegen: swift w/ public access control', async () => {
     extension PublicSchema.UsersSelect: Identifiable {
 
     }
+
     extension PublicSchema {
       public struct UsersInsert: Codable, Hashable, Sendable {
         public let id: Int64?
         public let name: String?
-        public let status: UserStatus?
+        public let status: PublicSchema.UserStatus?
 
         public enum CodingKeys: String, CodingKey {
           case id = "id"
@@ -2162,11 +2224,12 @@ test('typegen: swift w/ public access control', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       public struct UsersUpdate: Codable, Hashable, Sendable {
         public let id: Int64?
         public let name: String?
-        public let status: UserStatus?
+        public let status: PublicSchema.UserStatus?
 
         public enum CodingKeys: String, CodingKey {
           case id = "id"
@@ -2174,6 +2237,7 @@ test('typegen: swift w/ public access control', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       public struct TodosSelect: Codable, Hashable, Sendable {
         public let details: String?
@@ -2189,6 +2253,7 @@ test('typegen: swift w/ public access control', async () => {
     extension PublicSchema.TodosSelect: Identifiable {
 
     }
+
     extension PublicSchema {
       public struct TodosInsert: Codable, Hashable, Sendable {
         public let details: String?
@@ -2201,6 +2266,7 @@ test('typegen: swift w/ public access control', async () => {
           case userId = "user-id"
         }
     }
+
     extension PublicSchema {
       public struct TodosUpdate: Codable, Hashable, Sendable {
         public let details: String?
@@ -2213,6 +2279,7 @@ test('typegen: swift w/ public access control', async () => {
           case userId = "user-id"
         }
     }
+
     extension PublicSchema {
       public struct UsersAuditSelect: Codable, Hashable, Sendable {
         public let createdAt: String?
@@ -2230,6 +2297,7 @@ test('typegen: swift w/ public access control', async () => {
     extension PublicSchema.UsersAuditSelect: Identifiable {
 
     }
+
     extension PublicSchema {
       public struct UsersAuditInsert: Codable, Hashable, Sendable {
         public let createdAt: String?
@@ -2244,6 +2312,7 @@ test('typegen: swift w/ public access control', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       public struct UsersAuditUpdate: Codable, Hashable, Sendable {
         public let createdAt: String?
@@ -2258,6 +2327,7 @@ test('typegen: swift w/ public access control', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       public struct UserDetailsSelect: Codable, Hashable, Sendable {
         public let details: String?
@@ -2268,6 +2338,7 @@ test('typegen: swift w/ public access control', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       public struct UserDetailsInsert: Codable, Hashable, Sendable {
         public let details: String?
@@ -2278,6 +2349,7 @@ test('typegen: swift w/ public access control', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       public struct UserDetailsUpdate: Codable, Hashable, Sendable {
         public let details: String?
@@ -2288,6 +2360,7 @@ test('typegen: swift w/ public access control', async () => {
           case userId = "user_id"
         }
     }
+
     extension PublicSchema {
       public struct EmptySelect: Codable, Hashable, Sendable {
 
@@ -2296,6 +2369,7 @@ test('typegen: swift w/ public access control', async () => {
 
         }
     }
+
     extension PublicSchema {
       public struct EmptyInsert: Codable, Hashable, Sendable {
 
@@ -2304,6 +2378,7 @@ test('typegen: swift w/ public access control', async () => {
 
         }
     }
+
     extension PublicSchema {
       public struct EmptyUpdate: Codable, Hashable, Sendable {
 
@@ -2312,36 +2387,40 @@ test('typegen: swift w/ public access control', async () => {
 
         }
     }
+
     extension PublicSchema {
       public struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable {
-        public let col1: UserDetails?
-        public let col2: AView?
+        public let col1: PublicSchema.UserDetails?
+        public let col2: PublicSchema.AView?
 
         public enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
     }
+
     extension PublicSchema {
       public struct TableWithOtherTablesRowTypeInsert: Codable, Hashable, Sendable {
-        public let col1: UserDetails?
-        public let col2: AView?
+        public let col1: PublicSchema.UserDetails?
+        public let col2: PublicSchema.AView?
 
         public enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
     }
+
     extension PublicSchema {
       public struct TableWithOtherTablesRowTypeUpdate: Codable, Hashable, Sendable {
-        public let col1: UserDetails?
-        public let col2: AView?
+        public let col1: PublicSchema.UserDetails?
+        public let col2: PublicSchema.AView?
 
         public enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
     }
+
     extension PublicSchema {
       public struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable {
         public let name: String?
@@ -2355,6 +2434,7 @@ test('typegen: swift w/ public access control', async () => {
     extension PublicSchema.TableWithPrimaryKeyOtherThanIdSelect: Identifiable {
         public var id: Int64 { otherId }
     }
+
     extension PublicSchema {
       public struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable {
         public let name: String?
@@ -2365,6 +2445,7 @@ test('typegen: swift w/ public access control', async () => {
           case otherId = "other_id"
         }
     }
+
     extension PublicSchema {
       public struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable {
         public let name: String?
@@ -2375,6 +2456,7 @@ test('typegen: swift w/ public access control', async () => {
           case otherId = "other_id"
         }
     }
+
     extension PublicSchema {
       public struct CategorySelect: Codable, Hashable, Sendable {
         public let id: Int32
@@ -2385,6 +2467,7 @@ test('typegen: swift w/ public access control', async () => {
           case name = "name"
         }
     }
+
     extension PublicSchema {
       public struct CategoryInsert: Codable, Hashable, Sendable {
         public let id: Int32?
@@ -2395,6 +2478,7 @@ test('typegen: swift w/ public access control', async () => {
           case name = "name"
         }
     }
+
     extension PublicSchema {
       public struct CategoryUpdate: Codable, Hashable, Sendable {
         public let id: Int32?
@@ -2405,6 +2489,7 @@ test('typegen: swift w/ public access control', async () => {
           case name = "name"
         }
     }
+
     extension PublicSchema {
       public struct MemesSelect: Codable, Hashable, Sendable {
         public let category: Int32?
@@ -2412,7 +2497,7 @@ test('typegen: swift w/ public access control', async () => {
         public let id: Int32
         public let metadata: AnyJSON?
         public let name: String
-        public let status: MemeStatus?
+        public let status: PublicSchema.MemeStatus?
 
         public enum CodingKeys: String, CodingKey {
           case category = "category"
@@ -2423,6 +2508,7 @@ test('typegen: swift w/ public access control', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       public struct MemesInsert: Codable, Hashable, Sendable {
         public let category: Int32?
@@ -2430,7 +2516,7 @@ test('typegen: swift w/ public access control', async () => {
         public let id: Int32?
         public let metadata: AnyJSON?
         public let name: String
-        public let status: MemeStatus?
+        public let status: PublicSchema.MemeStatus?
 
         public enum CodingKeys: String, CodingKey {
           case category = "category"
@@ -2441,6 +2527,7 @@ test('typegen: swift w/ public access control', async () => {
           case status = "status"
         }
     }
+
     extension PublicSchema {
       public struct MemesUpdate: Codable, Hashable, Sendable {
         public let category: Int32?
@@ -2448,7 +2535,7 @@ test('typegen: swift w/ public access control', async () => {
         public let id: Int32?
         public let metadata: AnyJSON?
         public let name: String?
-        public let status: MemeStatus?
+        public let status: PublicSchema.MemeStatus?
 
         public enum CodingKeys: String, CodingKey {
           case category = "category"
@@ -2458,6 +2545,39 @@ test('typegen: swift w/ public access control', async () => {
           case name = "name"
           case status = "status"
         }
+    }
+
+    // MARK: - Views
+
+
+
+
+
+
+    // MARK: - Materialized Views
+    extension PublicSchema {
+      public struct TodosMatviewSelect: Codable, Hashable, Sendable {
+        public let details: String?
+        public let id: Int64?
+        public let userId: Int64?
+
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+    }
+
+    // MARK: - Composite Types
+    extension PublicSchema {
+      public struct CompositeTypeWithArrayAttribute: Codable, Hashable, Sendable {
+        public let MyTextArray: AnyJSON
+
+        public enum CodingKeys: String, CodingKey {
+          case MyTextArray = "my_text_array"
+        }
+      }
+    }
     }"
   `)
 })

From d54fa21642f6ebecec8953d61778dac94e88184b Mon Sep 17 00:00:00 2001
From: Guilherme Souza <grsouza@pm.me>
Date: Fri, 14 Jun 2024 09:59:43 -0300
Subject: [PATCH 6/6] refactor swift typegen

---
 src/server/templates/swift.ts | 330 +++++++++++---------
 test/server/typegen.ts        | 558 +++++++++++-----------------------
 2 files changed, 364 insertions(+), 524 deletions(-)

diff --git a/src/server/templates/swift.ts b/src/server/templates/swift.ts
index 66e469fb..a10972e4 100644
--- a/src/server/templates/swift.ts
+++ b/src/server/templates/swift.ts
@@ -17,48 +17,56 @@ type SwiftGeneratorOptions = {
   accessControl: AccessControl
 }
 
-function generateEnum(enum_: PostgresType, options: SwiftGeneratorOptions): string {
-  return `
-${options.accessControl} enum ${formatForSwiftTypeName(enum_.name)}: String, Codable, Hashable, Sendable {
-${enum_.enums.map((case_) => `${ident(1)}case ${formatForSwiftPropertyName(case_)} = "${case_}"`).join('\n')}
+type SwiftEnumCase = {
+  formattedName: string
+  rawValue: string
 }
-`.trim()
+
+type SwiftEnum = {
+  formattedEnumName: string
+  protocolConformances: string[]
+  cases: SwiftEnumCase[]
 }
 
-function generateTableStructsForOperations(
-  schema: PostgresSchema,
-  table: PostgresTable | PostgresView | PostgresMaterializedView,
-  columns: PostgresColumn[] | undefined,
-  operations: Operation[],
-  options: SwiftGeneratorOptions,
-  {
-    types,
-    views,
-    tables,
-  }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] }
-): string[] {
-  return operations.map((operation) =>
-    generateTableStruct(schema, table, columns, operation, options, { types, views, tables })
-  )
+type SwiftAttribute = {
+  formattedAttributeName: string
+  formattedType: string
+  rawName: string
+  isIdentity: boolean
+}
+
+type SwiftStruct = {
+  formattedStructName: string
+  protocolConformances: string[]
+  attributes: SwiftAttribute[]
+  codingKeysEnum: SwiftEnum | undefined
+}
+
+function formatForSwiftSchemaName(schema: string): string {
+  return `${formatForSwiftTypeName(schema)}Schema`
+}
+
+function pgEnumToSwiftEnum(pgEnum: PostgresType): SwiftEnum {
+  return {
+    formattedEnumName: formatForSwiftTypeName(pgEnum.name),
+    protocolConformances: ['String', 'Codable', 'Hashable', 'Sendable'],
+    cases: pgEnum.enums.map((case_) => {
+      return { formattedName: formatForSwiftPropertyName(case_), rawValue: case_ }
+    }),
+  }
 }
 
-function generateTableStruct(
-  schema: PostgresSchema,
+function pgTypeToSwiftStruct(
   table: PostgresTable | PostgresView | PostgresMaterializedView,
   columns: PostgresColumn[] | undefined,
   operation: Operation,
-  options: SwiftGeneratorOptions,
   {
     types,
     views,
     tables,
   }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] }
-): string {
-  const columnEntries: {
-    raw_name: string
-    formatted_name: string
-    type: string
-  }[] =
+): SwiftStruct {
+  const columnEntries: SwiftAttribute[] =
     columns?.map((column) => {
       let nullable: boolean
 
@@ -72,49 +80,44 @@ function generateTableStruct(
       }
 
       return {
-        raw_name: column.name,
-        formatted_name: formatForSwiftPropertyName(column.name),
-        type: pgTypeToSwiftType(column.format, nullable, { types, views, tables }),
+        rawName: column.name,
+        formattedAttributeName: formatForSwiftPropertyName(column.name),
+        formattedType: pgTypeToSwiftType(column.format, nullable, { types, views, tables }),
+        isIdentity: column.is_identity,
       }
     }) ?? []
 
-  const identity = columns?.find((column) => column.is_identity)
-  const structName = `${formatForSwiftTypeName(table.name)}${operation}`
-
-  let output = `
-extension ${formatForSwiftTypeName(schema.name)}Schema {
-${ident(1)}${options.accessControl} struct ${structName}: Codable, Hashable, Sendable {
-${columnEntries.map(({ formatted_name, type }) => `${ident(2)}${options.accessControl} let ${formatted_name}: ${type}`).join('\n')}
-
-${ident(2)}${options.accessControl} enum CodingKeys: String, CodingKey {
-${columnEntries.map(({ raw_name, formatted_name }) => `${ident(3)}case ${formatted_name} = "${raw_name}"`).join('\n')}
-${ident(2)}}
-}
-`
-
-  if (operation === 'Select' && identity) {
-    const identityEntry = columnEntries.find((entry) => entry.raw_name === identity.name)
-    if (identityEntry) {
-      output += `extension ${formatForSwiftTypeName(schema.name)}Schema.${structName}: Identifiable {
-${identityEntry.formatted_name !== 'id' ? `${ident(2)}${options.accessControl} var id: ${identityEntry.type} { ${identityEntry.formatted_name} }` : ''}
-}
-  `
-    }
+  return {
+    formattedStructName: `${formatForSwiftTypeName(table.name)}${operation}`,
+    attributes: columnEntries,
+    protocolConformances: ['Codable', 'Hashable', 'Sendable'],
+    codingKeysEnum: generateCodingKeysEnumFromAttributes(columnEntries),
   }
+}
 
-  return output.trim()
+function generateCodingKeysEnumFromAttributes(attributes: SwiftAttribute[]): SwiftEnum | undefined {
+  return attributes.length > 0
+    ? {
+        formattedEnumName: 'CodingKeys',
+        protocolConformances: ['String', 'CodingKey'],
+        cases: attributes.map((attribute) => {
+          return {
+            formattedName: attribute.formattedAttributeName,
+            rawValue: attribute.rawName,
+          }
+        }),
+      }
+    : undefined
 }
 
-function gnerateCompositeTypeStruct(
-  schema: PostgresSchema,
+function pgCompositeTypeToSwiftStruct(
   type: PostgresType,
-  options: SwiftGeneratorOptions,
   {
     types,
     views,
     tables,
   }: { types: PostgresType[]; views: PostgresView[]; tables: PostgresTable[] }
-): string {
+): SwiftStruct {
   const typeWithRetrievedAttributes = {
     ...type,
     attributes: type.attributes.map((attribute) => {
@@ -126,34 +129,77 @@ function gnerateCompositeTypeStruct(
     }),
   }
 
-  const attributeEntries: {
-    formatted_name: string
-    type: string
-    raw_name: string
-  }[] = typeWithRetrievedAttributes.attributes.map((attribute) => {
-    return {
-      formatted_name: formatForSwiftTypeName(attribute.name),
-      type: pgTypeToSwiftType(attribute.type!.format, false, { types, views, tables }),
-      raw_name: attribute.name,
+  const attributeEntries: SwiftAttribute[] = typeWithRetrievedAttributes.attributes.map(
+    (attribute) => {
+      return {
+        formattedAttributeName: formatForSwiftTypeName(attribute.name),
+        formattedType: pgTypeToSwiftType(attribute.type!.format, false, { types, views, tables }),
+        rawName: attribute.name,
+        isIdentity: false,
+      }
     }
-  })
+  )
+
+  return {
+    formattedStructName: formatForSwiftTypeName(type.name),
+    attributes: attributeEntries,
+    protocolConformances: ['Codable', 'Hashable', 'Sendable'],
+    codingKeysEnum: generateCodingKeysEnumFromAttributes(attributeEntries),
+  }
+}
+
+function generateProtocolConformances(protocols: string[]): string {
+  return protocols.length === 0 ? '' : `: ${protocols.join(', ')}`
+}
 
-  let output = `extension ${formatForSwiftTypeName(schema.name)}Schema {
-${ident(1)}${options.accessControl} struct ${formatForSwiftTypeName(type.name)}: Codable, Hashable, Sendable {
-${attributeEntries
-  .map((entry) => `${ident(2)}${options.accessControl} let ${entry.formatted_name}: ${entry.type}`)
-  .join('\n')}
-
-${ident(2)}${options.accessControl} enum CodingKeys: String, CodingKey {
-${attributeEntries
-  .map((entry) => `${ident(3)}case ${entry.formatted_name} = "${entry.raw_name}"`)
-  .join('\n')}
-${ident(2)}}
-${ident(1)}}
+function generateEnum(
+  enum_: SwiftEnum,
+  { accessControl, level }: SwiftGeneratorOptions & { level: number }
+): string[] {
+  return [
+    `${ident(level)}${accessControl} enum ${enum_.formattedEnumName}${generateProtocolConformances(enum_.protocolConformances)} {`,
+    ...enum_.cases.map(
+      (case_) => `${ident(level + 1)}case ${case_.formattedName} = "${case_.rawValue}"`
+    ),
+    `${ident(level)}}`,
+  ]
 }
-}`
 
-  return output.trim()
+function generateStruct(
+  struct: SwiftStruct,
+  { accessControl, level }: SwiftGeneratorOptions & { level: number }
+): string[] {
+  const identity = struct.attributes.find((column) => column.isIdentity)
+
+  let protocolConformances = struct.protocolConformances
+  if (identity) {
+    protocolConformances.push('Identifiable')
+  }
+
+  let output = [
+    `${ident(level)}${accessControl} struct ${struct.formattedStructName}${generateProtocolConformances(struct.protocolConformances)} {`,
+  ]
+
+  if (identity && identity.formattedAttributeName !== 'id') {
+    output.push(
+      `${ident(level + 1)}${accessControl} var id: ${identity.formattedType} { ${identity.formattedAttributeName} }`
+    )
+  }
+
+  output.push(
+    ...struct.attributes.map(
+      (attribute) =>
+        `${ident(level + 1)}${accessControl} let ${attribute.formattedAttributeName}: ${attribute.formattedType}`
+    )
+  )
+
+  if (struct.codingKeysEnum) {
+    output.push(...generateEnum(struct.codingKeysEnum, { accessControl, level: level + 1 }))
+  }
+
+  output.push(`${ident(level)}}`)
+
+  return output
 }
 
 export const apply = async ({
@@ -181,72 +227,56 @@ export const apply = async ({
     .filter((type) => type.enums.length > 0)
     .sort(({ name: a }, { name: b }) => a.localeCompare(b))
 
-  let output = `
-import Foundation
-import Supabase
-
-// MARK: - Enums
-${enums.map((enum_) => generateEnum(enum_, { accessControl })).join('\n\n')}
-
-// MARK: - Schemas
-${schemas.map((schema) => `${accessControl} enum ${formatForSwiftTypeName(schema.name)}Schema {}`).join('\n\n')}
-
-// MARK: - Tables
-${tables
-  .flatMap((table) =>
-    generateTableStructsForOperations(
-      schemas.find((schema) => schema.name === table.schema)!,
-      table,
-      columnsByTableId[table.id],
-      ['Select', 'Insert', 'Update'],
-      { accessControl },
-      { types, views, tables }
-    )
-  )
-  .join('\n\n')}
-
-// MARK: - Views
-${views
-  .flatMap((view) => {
-    generateTableStructsForOperations(
-      schemas.find((schema) => schema.name === view.schema)!,
-      view,
-      columnsByTableId[view.id],
-      ['Select'],
-      { accessControl },
-      { types, views, tables }
-    )
+  const swiftEnums = enums.map((enum_) => {
+    return { schema: enum_.schema, enum_: pgEnumToSwiftEnum(enum_) }
   })
-  .join('\n\n')}
-
-// MARK: - Materialized Views
-${materializedViews
-  .flatMap((materializedView) =>
-    generateTableStructsForOperations(
-      schemas.find((schema) => schema.name === materializedView.schema)!,
-      materializedView,
-      columnsByTableId[materializedView.id],
-      ['Select'],
-      { accessControl },
-      { types, views, tables }
+
+  const swiftStructForTables = tables.flatMap((table) =>
+    (['Select', 'Insert', 'Update'] as Operation[]).map((operation) =>
+      pgTypeToSwiftStruct(table, columnsByTableId[table.id], operation, { types, views, tables })
     )
   )
-  .join('\n\n')}
-
-// MARK: - Composite Types
-${compositeTypes
-  .map((compositeType) =>
-    gnerateCompositeTypeStruct(
-      schemas.find((schema) => schema.name === compositeType.schema)!,
-      compositeType,
-      { accessControl },
-      { types, views, tables }
-    )
+
+  const swiftStructForViews = views.map((view) =>
+    pgTypeToSwiftStruct(view, columnsByTableId[view.id], 'Select', { types, views, tables })
   )
-  .join('\n\n')}
-`.trim()
 
-  return output
+  const swiftStructForMaterializedViews = materializedViews.map((materializedView) =>
+    pgTypeToSwiftStruct(materializedView, columnsByTableId[materializedView.id], 'Select', {
+      types,
+      views,
+      tables,
+    })
+  )
+
+  const swiftStructForCompositeTypes = compositeTypes.map((type) =>
+    pgCompositeTypeToSwiftStruct(type, { types, views, tables })
+  )
+
+  let output = [
+    'import Foundation',
+    'import Supabase',
+    '',
+    ...schemas.flatMap((schema) => [
+      `${accessControl} enum ${formatForSwiftSchemaName(schema.name)} {`,
+      ...swiftEnums.flatMap(({ enum_ }) => generateEnum(enum_, { accessControl, level: 1 })),
+      ...swiftStructForTables.flatMap((struct) =>
+        generateStruct(struct, { accessControl, level: 1 })
+      ),
+      ...swiftStructForViews.flatMap((struct) =>
+        generateStruct(struct, { accessControl, level: 1 })
+      ),
+      ...swiftStructForMaterializedViews.flatMap((struct) =>
+        generateStruct(struct, { accessControl, level: 1 })
+      ),
+      ...swiftStructForCompositeTypes.flatMap((struct) =>
+        generateStruct(struct, { accessControl, level: 1 })
+      ),
+      '}',
+    ]),
+  ]
+
+  return output.join('\n')
 }
 
 // TODO: Make this more robust. Currently doesn't handle range types - returns them as string.
@@ -300,11 +330,15 @@ const pgTypeToSwiftType = (
   } else if (pgType.startsWith('_')) {
     swiftType = `[${pgTypeToSwiftType(pgType.substring(1), false, { types, views, tables })}]`
   } else {
-    const allTypes: { name: string; schema: string }[] = [...types, ...views, ...tables]
-    const type = allTypes.find((type) => type.name === pgType)
+    const enumType = types.find((type) => type.name === pgType && type.enums.length > 0)
+
+    const compositeTypes = [...types, ...views, ...tables].find((type) => type.name === pgType)
 
-    if (type) {
-      swiftType = `${formatForSwiftTypeName(type.schema)}Schema.${formatForSwiftTypeName(type.name)}`
+    if (enumType) {
+      swiftType = `${formatForSwiftTypeName(enumType.name)}`
+    } else if (compositeTypes) {
+      // Append a `Select` to the composite type, as that is how is named in the generated struct.
+      swiftType = `${formatForSwiftTypeName(compositeTypes.name)}Select`
     } else {
       swiftType = 'AnyJSON'
     }
diff --git a/test/server/typegen.ts b/test/server/typegen.ts
index 717358a3..19744628 100644
--- a/test/server/typegen.ts
+++ b/test/server/typegen.ts
@@ -1768,325 +1768,224 @@ test('typegen: swift', async () => {
     "import Foundation
     import Supabase
 
-    // MARK: - Enums
-    internal enum MemeStatus: String, Codable, Hashable, Sendable {
-      case new = "new"
-      case old = "old"
-      case retired = "retired"
-    }
-
-    internal enum UserStatus: String, Codable, Hashable, Sendable {
-      case active = "ACTIVE"
-      case inactive = "INACTIVE"
-    }
-
-    // MARK: - Schemas
-    internal enum PublicSchema {}
-
-    // MARK: - Tables
-    extension PublicSchema {
-      internal struct UsersSelect: Codable, Hashable, Sendable {
+    internal enum PublicSchema {
+      internal enum MemeStatus: String, Codable, Hashable, Sendable {
+        case new = "new"
+        case old = "old"
+        case retired = "retired"
+      }
+      internal enum UserStatus: String, Codable, Hashable, Sendable {
+        case active = "ACTIVE"
+        case inactive = "INACTIVE"
+      }
+      internal struct UsersSelect: Codable, Hashable, Sendable, Identifiable {
         internal let id: Int64
         internal let name: String?
-        internal let status: PublicSchema.UserStatus?
-
+        internal let status: UserStatus?
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
           case status = "status"
         }
-    }
-    extension PublicSchema.UsersSelect: Identifiable {
-
-    }
-
-    extension PublicSchema {
-      internal struct UsersInsert: Codable, Hashable, Sendable {
+      }
+      internal struct UsersInsert: Codable, Hashable, Sendable, Identifiable {
         internal let id: Int64?
         internal let name: String?
-        internal let status: PublicSchema.UserStatus?
-
+        internal let status: UserStatus?
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
-      internal struct UsersUpdate: Codable, Hashable, Sendable {
+      }
+      internal struct UsersUpdate: Codable, Hashable, Sendable, Identifiable {
         internal let id: Int64?
         internal let name: String?
-        internal let status: PublicSchema.UserStatus?
-
+        internal let status: UserStatus?
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
-      internal struct TodosSelect: Codable, Hashable, Sendable {
+      }
+      internal struct TodosSelect: Codable, Hashable, Sendable, Identifiable {
         internal let details: String?
         internal let id: Int64
         internal let userId: Int64
-
         internal enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-    extension PublicSchema.TodosSelect: Identifiable {
-
-    }
-
-    extension PublicSchema {
-      internal struct TodosInsert: Codable, Hashable, Sendable {
+      }
+      internal struct TodosInsert: Codable, Hashable, Sendable, Identifiable {
         internal let details: String?
         internal let id: Int64?
         internal let userId: Int64
-
         internal enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-
-    extension PublicSchema {
-      internal struct TodosUpdate: Codable, Hashable, Sendable {
+      }
+      internal struct TodosUpdate: Codable, Hashable, Sendable, Identifiable {
         internal let details: String?
         internal let id: Int64?
         internal let userId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-
-    extension PublicSchema {
-      internal struct UsersAuditSelect: Codable, Hashable, Sendable {
+      }
+      internal struct UsersAuditSelect: Codable, Hashable, Sendable, Identifiable {
         internal let createdAt: String?
         internal let id: Int64
         internal let previousValue: AnyJSON?
         internal let userId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case createdAt = "created_at"
           case id = "id"
           case previousValue = "previous_value"
           case userId = "user_id"
         }
-    }
-    extension PublicSchema.UsersAuditSelect: Identifiable {
-
-    }
-
-    extension PublicSchema {
-      internal struct UsersAuditInsert: Codable, Hashable, Sendable {
+      }
+      internal struct UsersAuditInsert: Codable, Hashable, Sendable, Identifiable {
         internal let createdAt: String?
         internal let id: Int64?
         internal let previousValue: AnyJSON?
         internal let userId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case createdAt = "created_at"
           case id = "id"
           case previousValue = "previous_value"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
-      internal struct UsersAuditUpdate: Codable, Hashable, Sendable {
+      }
+      internal struct UsersAuditUpdate: Codable, Hashable, Sendable, Identifiable {
         internal let createdAt: String?
         internal let id: Int64?
         internal let previousValue: AnyJSON?
         internal let userId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case createdAt = "created_at"
           case id = "id"
           case previousValue = "previous_value"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct UserDetailsSelect: Codable, Hashable, Sendable {
         internal let details: String?
         internal let userId: Int64
-
         internal enum CodingKeys: String, CodingKey {
           case details = "details"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct UserDetailsInsert: Codable, Hashable, Sendable {
         internal let details: String?
         internal let userId: Int64
-
         internal enum CodingKeys: String, CodingKey {
           case details = "details"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct UserDetailsUpdate: Codable, Hashable, Sendable {
         internal let details: String?
         internal let userId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case details = "details"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct EmptySelect: Codable, Hashable, Sendable {
-
-
-        internal enum CodingKeys: String, CodingKey {
-
-        }
-    }
-
-    extension PublicSchema {
+      }
       internal struct EmptyInsert: Codable, Hashable, Sendable {
-
-
-        internal enum CodingKeys: String, CodingKey {
-
-        }
-    }
-
-    extension PublicSchema {
+      }
       internal struct EmptyUpdate: Codable, Hashable, Sendable {
-
-
-        internal enum CodingKeys: String, CodingKey {
-
-        }
-    }
-
-    extension PublicSchema {
+      }
       internal struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable {
-        internal let col1: PublicSchema.UserDetails?
-        internal let col2: PublicSchema.AView?
-
+        internal let col1: UserDetailsSelect?
+        internal let col2: AViewSelect?
         internal enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct TableWithOtherTablesRowTypeInsert: Codable, Hashable, Sendable {
-        internal let col1: PublicSchema.UserDetails?
-        internal let col2: PublicSchema.AView?
-
+        internal let col1: UserDetailsSelect?
+        internal let col2: AViewSelect?
         internal enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct TableWithOtherTablesRowTypeUpdate: Codable, Hashable, Sendable {
-        internal let col1: PublicSchema.UserDetails?
-        internal let col2: PublicSchema.AView?
-
+        internal let col1: UserDetailsSelect?
+        internal let col2: AViewSelect?
         internal enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
-    }
-
-    extension PublicSchema {
-      internal struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable {
+      }
+      internal struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable, Identifiable {
+        internal var id: Int64 { otherId }
         internal let name: String?
         internal let otherId: Int64
-
         internal enum CodingKeys: String, CodingKey {
           case name = "name"
           case otherId = "other_id"
         }
-    }
-    extension PublicSchema.TableWithPrimaryKeyOtherThanIdSelect: Identifiable {
-        internal var id: Int64 { otherId }
-    }
-
-    extension PublicSchema {
-      internal struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable {
+      }
+      internal struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable, Identifiable {
+        internal var id: Int64? { otherId }
         internal let name: String?
         internal let otherId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case name = "name"
           case otherId = "other_id"
         }
-    }
-
-    extension PublicSchema {
-      internal struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable {
+      }
+      internal struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable, Identifiable {
+        internal var id: Int64? { otherId }
         internal let name: String?
         internal let otherId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case name = "name"
           case otherId = "other_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct CategorySelect: Codable, Hashable, Sendable {
         internal let id: Int32
         internal let name: String
-
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct CategoryInsert: Codable, Hashable, Sendable {
         internal let id: Int32?
         internal let name: String
-
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct CategoryUpdate: Codable, Hashable, Sendable {
         internal let id: Int32?
         internal let name: String?
-
         internal enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct MemesSelect: Codable, Hashable, Sendable {
         internal let category: Int32?
         internal let createdAt: String
         internal let id: Int32
         internal let metadata: AnyJSON?
         internal let name: String
-        internal let status: PublicSchema.MemeStatus?
-
+        internal let status: MemeStatus?
         internal enum CodingKeys: String, CodingKey {
           case category = "category"
           case createdAt = "created_at"
@@ -2095,17 +1994,14 @@ test('typegen: swift', async () => {
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct MemesInsert: Codable, Hashable, Sendable {
         internal let category: Int32?
         internal let createdAt: String
         internal let id: Int32?
         internal let metadata: AnyJSON?
         internal let name: String
-        internal let status: PublicSchema.MemeStatus?
-
+        internal let status: MemeStatus?
         internal enum CodingKeys: String, CodingKey {
           case category = "category"
           case createdAt = "created_at"
@@ -2114,17 +2010,14 @@ test('typegen: swift', async () => {
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
+      }
       internal struct MemesUpdate: Codable, Hashable, Sendable {
         internal let category: Int32?
         internal let createdAt: String?
         internal let id: Int32?
         internal let metadata: AnyJSON?
         internal let name: String?
-        internal let status: PublicSchema.MemeStatus?
-
+        internal let status: MemeStatus?
         internal enum CodingKeys: String, CodingKey {
           case category = "category"
           case createdAt = "created_at"
@@ -2133,39 +2026,49 @@ test('typegen: swift', async () => {
           case name = "name"
           case status = "status"
         }
-    }
-
-    // MARK: - Views
-
-
-
-
-
-
-    // MARK: - Materialized Views
-    extension PublicSchema {
+      }
+      internal struct TodosViewSelect: Codable, Hashable, Sendable {
+        internal let details: String?
+        internal let id: Int64?
+        internal let userId: Int64?
+        internal enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+      }
+      internal struct UsersViewSelect: Codable, Hashable, Sendable {
+        internal let id: Int64?
+        internal let name: String?
+        internal let status: UserStatus?
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+      }
+      internal struct AViewSelect: Codable, Hashable, Sendable {
+        internal let id: Int64?
+        internal enum CodingKeys: String, CodingKey {
+          case id = "id"
+        }
+      }
       internal struct TodosMatviewSelect: Codable, Hashable, Sendable {
         internal let details: String?
         internal let id: Int64?
         internal let userId: Int64?
-
         internal enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-
-    // MARK: - Composite Types
-    extension PublicSchema {
+      }
       internal struct CompositeTypeWithArrayAttribute: Codable, Hashable, Sendable {
         internal let MyTextArray: AnyJSON
-
         internal enum CodingKeys: String, CodingKey {
           case MyTextArray = "my_text_array"
         }
       }
-    }
     }"
   `)
 })
@@ -2180,325 +2083,224 @@ test('typegen: swift w/ public access control', async () => {
     "import Foundation
     import Supabase
 
-    // MARK: - Enums
-    public enum MemeStatus: String, Codable, Hashable, Sendable {
-      case new = "new"
-      case old = "old"
-      case retired = "retired"
-    }
-
-    public enum UserStatus: String, Codable, Hashable, Sendable {
-      case active = "ACTIVE"
-      case inactive = "INACTIVE"
-    }
-
-    // MARK: - Schemas
-    public enum PublicSchema {}
-
-    // MARK: - Tables
-    extension PublicSchema {
-      public struct UsersSelect: Codable, Hashable, Sendable {
+    public enum PublicSchema {
+      public enum MemeStatus: String, Codable, Hashable, Sendable {
+        case new = "new"
+        case old = "old"
+        case retired = "retired"
+      }
+      public enum UserStatus: String, Codable, Hashable, Sendable {
+        case active = "ACTIVE"
+        case inactive = "INACTIVE"
+      }
+      public struct UsersSelect: Codable, Hashable, Sendable, Identifiable {
         public let id: Int64
         public let name: String?
-        public let status: PublicSchema.UserStatus?
-
+        public let status: UserStatus?
         public enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
           case status = "status"
         }
-    }
-    extension PublicSchema.UsersSelect: Identifiable {
-
-    }
-
-    extension PublicSchema {
-      public struct UsersInsert: Codable, Hashable, Sendable {
+      }
+      public struct UsersInsert: Codable, Hashable, Sendable, Identifiable {
         public let id: Int64?
         public let name: String?
-        public let status: PublicSchema.UserStatus?
-
+        public let status: UserStatus?
         public enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
-      public struct UsersUpdate: Codable, Hashable, Sendable {
+      }
+      public struct UsersUpdate: Codable, Hashable, Sendable, Identifiable {
         public let id: Int64?
         public let name: String?
-        public let status: PublicSchema.UserStatus?
-
+        public let status: UserStatus?
         public enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
-      public struct TodosSelect: Codable, Hashable, Sendable {
+      }
+      public struct TodosSelect: Codable, Hashable, Sendable, Identifiable {
         public let details: String?
         public let id: Int64
         public let userId: Int64
-
         public enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-    extension PublicSchema.TodosSelect: Identifiable {
-
-    }
-
-    extension PublicSchema {
-      public struct TodosInsert: Codable, Hashable, Sendable {
+      }
+      public struct TodosInsert: Codable, Hashable, Sendable, Identifiable {
         public let details: String?
         public let id: Int64?
         public let userId: Int64
-
         public enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-
-    extension PublicSchema {
-      public struct TodosUpdate: Codable, Hashable, Sendable {
+      }
+      public struct TodosUpdate: Codable, Hashable, Sendable, Identifiable {
         public let details: String?
         public let id: Int64?
         public let userId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-
-    extension PublicSchema {
-      public struct UsersAuditSelect: Codable, Hashable, Sendable {
+      }
+      public struct UsersAuditSelect: Codable, Hashable, Sendable, Identifiable {
         public let createdAt: String?
         public let id: Int64
         public let previousValue: AnyJSON?
         public let userId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case createdAt = "created_at"
           case id = "id"
           case previousValue = "previous_value"
           case userId = "user_id"
         }
-    }
-    extension PublicSchema.UsersAuditSelect: Identifiable {
-
-    }
-
-    extension PublicSchema {
-      public struct UsersAuditInsert: Codable, Hashable, Sendable {
+      }
+      public struct UsersAuditInsert: Codable, Hashable, Sendable, Identifiable {
         public let createdAt: String?
         public let id: Int64?
         public let previousValue: AnyJSON?
         public let userId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case createdAt = "created_at"
           case id = "id"
           case previousValue = "previous_value"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
-      public struct UsersAuditUpdate: Codable, Hashable, Sendable {
+      }
+      public struct UsersAuditUpdate: Codable, Hashable, Sendable, Identifiable {
         public let createdAt: String?
         public let id: Int64?
         public let previousValue: AnyJSON?
         public let userId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case createdAt = "created_at"
           case id = "id"
           case previousValue = "previous_value"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct UserDetailsSelect: Codable, Hashable, Sendable {
         public let details: String?
         public let userId: Int64
-
         public enum CodingKeys: String, CodingKey {
           case details = "details"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct UserDetailsInsert: Codable, Hashable, Sendable {
         public let details: String?
         public let userId: Int64
-
         public enum CodingKeys: String, CodingKey {
           case details = "details"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct UserDetailsUpdate: Codable, Hashable, Sendable {
         public let details: String?
         public let userId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case details = "details"
           case userId = "user_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct EmptySelect: Codable, Hashable, Sendable {
-
-
-        public enum CodingKeys: String, CodingKey {
-
-        }
-    }
-
-    extension PublicSchema {
+      }
       public struct EmptyInsert: Codable, Hashable, Sendable {
-
-
-        public enum CodingKeys: String, CodingKey {
-
-        }
-    }
-
-    extension PublicSchema {
+      }
       public struct EmptyUpdate: Codable, Hashable, Sendable {
-
-
-        public enum CodingKeys: String, CodingKey {
-
-        }
-    }
-
-    extension PublicSchema {
+      }
       public struct TableWithOtherTablesRowTypeSelect: Codable, Hashable, Sendable {
-        public let col1: PublicSchema.UserDetails?
-        public let col2: PublicSchema.AView?
-
+        public let col1: UserDetailsSelect?
+        public let col2: AViewSelect?
         public enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct TableWithOtherTablesRowTypeInsert: Codable, Hashable, Sendable {
-        public let col1: PublicSchema.UserDetails?
-        public let col2: PublicSchema.AView?
-
+        public let col1: UserDetailsSelect?
+        public let col2: AViewSelect?
         public enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct TableWithOtherTablesRowTypeUpdate: Codable, Hashable, Sendable {
-        public let col1: PublicSchema.UserDetails?
-        public let col2: PublicSchema.AView?
-
+        public let col1: UserDetailsSelect?
+        public let col2: AViewSelect?
         public enum CodingKeys: String, CodingKey {
           case col1 = "col1"
           case col2 = "col2"
         }
-    }
-
-    extension PublicSchema {
-      public struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable {
+      }
+      public struct TableWithPrimaryKeyOtherThanIdSelect: Codable, Hashable, Sendable, Identifiable {
+        public var id: Int64 { otherId }
         public let name: String?
         public let otherId: Int64
-
         public enum CodingKeys: String, CodingKey {
           case name = "name"
           case otherId = "other_id"
         }
-    }
-    extension PublicSchema.TableWithPrimaryKeyOtherThanIdSelect: Identifiable {
-        public var id: Int64 { otherId }
-    }
-
-    extension PublicSchema {
-      public struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable {
+      }
+      public struct TableWithPrimaryKeyOtherThanIdInsert: Codable, Hashable, Sendable, Identifiable {
+        public var id: Int64? { otherId }
         public let name: String?
         public let otherId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case name = "name"
           case otherId = "other_id"
         }
-    }
-
-    extension PublicSchema {
-      public struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable {
+      }
+      public struct TableWithPrimaryKeyOtherThanIdUpdate: Codable, Hashable, Sendable, Identifiable {
+        public var id: Int64? { otherId }
         public let name: String?
         public let otherId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case name = "name"
           case otherId = "other_id"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct CategorySelect: Codable, Hashable, Sendable {
         public let id: Int32
         public let name: String
-
         public enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct CategoryInsert: Codable, Hashable, Sendable {
         public let id: Int32?
         public let name: String
-
         public enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct CategoryUpdate: Codable, Hashable, Sendable {
         public let id: Int32?
         public let name: String?
-
         public enum CodingKeys: String, CodingKey {
           case id = "id"
           case name = "name"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct MemesSelect: Codable, Hashable, Sendable {
         public let category: Int32?
         public let createdAt: String
         public let id: Int32
         public let metadata: AnyJSON?
         public let name: String
-        public let status: PublicSchema.MemeStatus?
-
+        public let status: MemeStatus?
         public enum CodingKeys: String, CodingKey {
           case category = "category"
           case createdAt = "created_at"
@@ -2507,17 +2309,14 @@ test('typegen: swift w/ public access control', async () => {
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct MemesInsert: Codable, Hashable, Sendable {
         public let category: Int32?
         public let createdAt: String
         public let id: Int32?
         public let metadata: AnyJSON?
         public let name: String
-        public let status: PublicSchema.MemeStatus?
-
+        public let status: MemeStatus?
         public enum CodingKeys: String, CodingKey {
           case category = "category"
           case createdAt = "created_at"
@@ -2526,17 +2325,14 @@ test('typegen: swift w/ public access control', async () => {
           case name = "name"
           case status = "status"
         }
-    }
-
-    extension PublicSchema {
+      }
       public struct MemesUpdate: Codable, Hashable, Sendable {
         public let category: Int32?
         public let createdAt: String?
         public let id: Int32?
         public let metadata: AnyJSON?
         public let name: String?
-        public let status: PublicSchema.MemeStatus?
-
+        public let status: MemeStatus?
         public enum CodingKeys: String, CodingKey {
           case category = "category"
           case createdAt = "created_at"
@@ -2545,39 +2341,49 @@ test('typegen: swift w/ public access control', async () => {
           case name = "name"
           case status = "status"
         }
-    }
-
-    // MARK: - Views
-
-
-
-
-
-
-    // MARK: - Materialized Views
-    extension PublicSchema {
+      }
+      public struct TodosViewSelect: Codable, Hashable, Sendable {
+        public let details: String?
+        public let id: Int64?
+        public let userId: Int64?
+        public enum CodingKeys: String, CodingKey {
+          case details = "details"
+          case id = "id"
+          case userId = "user-id"
+        }
+      }
+      public struct UsersViewSelect: Codable, Hashable, Sendable {
+        public let id: Int64?
+        public let name: String?
+        public let status: UserStatus?
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+          case name = "name"
+          case status = "status"
+        }
+      }
+      public struct AViewSelect: Codable, Hashable, Sendable {
+        public let id: Int64?
+        public enum CodingKeys: String, CodingKey {
+          case id = "id"
+        }
+      }
       public struct TodosMatviewSelect: Codable, Hashable, Sendable {
         public let details: String?
         public let id: Int64?
         public let userId: Int64?
-
         public enum CodingKeys: String, CodingKey {
           case details = "details"
           case id = "id"
           case userId = "user-id"
         }
-    }
-
-    // MARK: - Composite Types
-    extension PublicSchema {
+      }
       public struct CompositeTypeWithArrayAttribute: Codable, Hashable, Sendable {
         public let MyTextArray: AnyJSON
-
         public enum CodingKeys: String, CodingKey {
           case MyTextArray = "my_text_array"
         }
       }
-    }
     }"
   `)
 })