Int128 fail in @Model with SwiftData

Swift recently added support for Int128. However, they do need NOT seem to be supported in SwiftData. Now totally possible I'm doing something wrong too.

I have the project set to macOS 15 to use a UInt128 in @Model class as attribute. I tried using a clean Xcode project with Swift Data choosen in the macOS app wizard.

Everything compiles, but it fails at runtime in both my app and "Xcode default" SwiftData:

SwiftData/SchemaProperty.swift:380: Fatal error: Unexpected property within Persisted Struct/Enum: Builtin.Int128

with the only modification to from stock is:

@Model
final class Item {
    var timestamp: Date
    var ipv6: UInt128
    
    init(timestamp: Date) {
        self.timestamp = timestamp
        self.ipv6 = 0
    }
}

I have tried both Int128 and UInt128. Both fails exactly the same. In fact, so exactly, when using UInt128 it still show a "Int128" in error message, despite class member being UInt128 .

My underlying need is to store an IPv6 addresses with an app, so the newer UInt128 would work to persist it. Since Network Framework IPv6Address is also not compatible, it seems, with SwiftData. So not a lot of good options, other an a String. But for an IPv6 address that suffers from that same address can take a few String forms (i.e. "0000:0000:0000:0000:0000:0000:0000:0000" =="0:0:0:0:0:0:0:0" == "::") which is more annoying than having a few expand Int128 as String separator ":".

Ideas welcomed. But potentially a bug in SwiftData since Int128 is both a Builtin and conforms to Codable, so from my reading it should work.

Answered by DTS Engineer in 826650022

Ziqiao and I have been talking about this behinds the scenes (-: so I’m going to chime in on this topic from a networking perspective.

IMO it’s best to avoid integer types when storing IP addresses. An IPv4 address is not a UInt32. Rather, it’s a sequence of 4 bytes. Similarly for an IPv6 address. If you treat IP addresses as integer types, you end up creating more problems for yourself. The classic example of this is the byte ordering behaviour of BSD Sockets, where you have to remember to apply {h,n}to{n,h}{s,l} in the right places. If in_addr were defined like this:

struct in_addr {
	uint8 s_addr[4];
};

the world would be a much better place.

Coming back to your main issue, I see two ways forward here:

  • Data

  • String

The first is obvious. The second does have the ambiguity problem you’ve described, but there are two easy solutions to that:

  • Always use the canonical textual representation format, as defined in RFC 5952. It’s safe to assume that the string representation of the IPv6Address type returns that format.

  • Or, if you want sorting to work, render each byte to hex and concatenate the result. If you insert colons [1] the result will even be parseable by IPv6Address.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Like so:

let str = addr.rawValue
    .map { [$0 >> 4, $0 & 0x0f] }
    .joined()
    .map { String($0, radix: 16) }
    .chunks(ofCount: 4)
    .map { x in x.joined() }
    .joined(separator: ":")

My guess is that this is because SQLite only support 64 bit integers so it won't work "out of the box" but require some extra work to store this type

Interesting, you may be right SQLite does not have a 128-bit integer. And I'll probably figure something else out here, since Int128 also require Sequoia.

But it's a Builtin type... so the SwiftData "rules" seem to imply those are mapped by SwiftData. Now... the internet tells me not all "Codables" work, but exactly what things don't is not well cataloged. All I know is a "IPv6 address" as Swift types have caused a lot of grief. ;)

Regarding the UInt128 / Int128 in SwiftData, I agree that SwiftData should provide native support, and would hence suggest that you file a feedback report – If you do so, please share your report ID here for folks to track.

Whether it is appropriate to save an IP address as a UInt128 is arguable, and I'd leave it there for other folks to comment.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Ziqiao and I have been talking about this behinds the scenes (-: so I’m going to chime in on this topic from a networking perspective.

IMO it’s best to avoid integer types when storing IP addresses. An IPv4 address is not a UInt32. Rather, it’s a sequence of 4 bytes. Similarly for an IPv6 address. If you treat IP addresses as integer types, you end up creating more problems for yourself. The classic example of this is the byte ordering behaviour of BSD Sockets, where you have to remember to apply {h,n}to{n,h}{s,l} in the right places. If in_addr were defined like this:

struct in_addr {
	uint8 s_addr[4];
};

the world would be a much better place.

Coming back to your main issue, I see two ways forward here:

  • Data

  • String

The first is obvious. The second does have the ambiguity problem you’ve described, but there are two easy solutions to that:

  • Always use the canonical textual representation format, as defined in RFC 5952. It’s safe to assume that the string representation of the IPv6Address type returns that format.

  • Or, if you want sorting to work, render each byte to hex and concatenate the result. If you insert colons [1] the result will even be parseable by IPv6Address.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Like so:

let str = addr.rawValue
    .map { [$0 >> 4, $0 & 0x0f] }
    .joined()
    .map { String($0, radix: 16) }
    .chunks(ofCount: 4)
    .map { x in x.joined() }
    .joined(separator: ":")
Int128 fail in @Model with SwiftData
 
 
Q