objecthub / swift-numberkit

Advanced numeric data types for Swift 5, including BigInt, Rational, and Complex numbers.
Apache License 2.0
54 stars 15 forks source link

Type 'UInt64' does not conform to protocol 'IntegerNumber' #22

Open nirmalpatidar123 opened 2 weeks ago

nirmalpatidar123 commented 2 weeks ago

I'm getting error in my xcode project when I have used the latest version of Hedera SDK Swift so I checked it uses the latest version of NumberKit library which causing this issue.

Here is the link for issue created on Hedera SDK Swift

objecthub commented 2 weeks ago

This is related to a bug fix I did recently, which apparently has lead to a similar bug being introduced in the Hedera SDK (probably long ago). NumberKit used to contain the following code:

extension UInt64: IntegerNumber {
   public var doubleValue: Double {
     return Double(self)
   }
}

This is semantically incorrect, because IntegerNumber is representing signed integers (and uses the corresponding Swift protocols). Unfortunately, the Swift compiler wasn't able to detect this broken protocol compliance so far and the erroneous code was not noticed. With Swift 6 (which is the default soon), this broken compliance will be reported as a warning and my guess is that eventually, this will become an error.

Unfortunately, the Swift Hedera API contains a similar erroneous definition when extending Rational (these are signed rational numbers — see the comment in the NumberKit implementation of Rational):

extension Rational: ProtobufCodable where T == UInt64 {
    internal typealias Protobuf = Proto_Fraction

    internal init(protobuf proto: Protobuf) {
        self.init(UInt64(proto.numerator), UInt64(proto.denominator))
    }

    internal func toProtobuf() -> Protobuf {
        .with { proto in
            proto.numerator = Int64(self.numerator)
            proto.denominator = Int64(self.denominator)
        }
    }
}

And the fact that the code needs to cast the values multiple times between UInt64 and Int64 shows that negative rationals are guaranteed to crash the application. This is a bug that needs to be fixed in the Hedera API. My guess is that the following change will do the trick, but I have no idea if this is backward compatible:

extension Rational: ProtobufCodable where T == Int64 {
    internal typealias Protobuf = Proto_Fraction

    internal init(protobuf proto: Protobuf) {
        self.init(proto.numerator, proto.denominator)
    }

    internal func toProtobuf() -> Protobuf {
        .with { proto in
            proto.numerator = self.numerator
            proto.denominator = self.denominator
        }
    }
}

To silence the error you get for now, there are two options:

  1. You add the first code snippet of this post to your code/the Swift Hedera API: this is bringing back the broken compliance, but your code compiles. You will get the warning I talked about when switching to Swift 6.
  2. You replace this line in Package.swift of the Swift Hedera API: .package(url: "https://github.com/objecthub/swift-numberkit.git", from: "2.4.1") with .package(url: "https://github.com/objecthub/swift-numberkit.git", exact: "2.5.1"). Everything will work as before, but you won't automatically include further changes to NumberKit.

I'd also be willing to consider changes to NumberKit, e.g. by bringing back the broken protocol compliance if compiled with Swift 5.X. Let me know if this is preferred.