Put simply I am trying to convert a #define
macro into a native Swift data structure of some sort. Just not sure how or what kind.
Details
I would like to try and replicate the following #define
from Objective-C to Swift. Source: JoeKun/FileMD5Hash
#define FileHashComputationContextInitialize(context, hashAlgorithmName) \
CC_##hashAlgorithmName##_CTX hashObjectFor##hashAlgorithmName; \
context.initFunction = (FileHashInitFunction)&CC_##hashAlgorithmName##_Init; \
context.updateFunction = (FileHashUpdateFunction)&CC_##hashAlgorithmName##_Update; \
context.finalFunction = (FileHashFinalFunction)&CC_##hashAlgorithmName##_Final; \
context.digestLength = CC_##hashAlgorithmName##_DIGEST_LENGTH; \
context.hashObjectPointer = (uint8_t **)&hashObjectFor##hashAlgorithmName
Obviously #define
does not exist in Swift; therefore I'm not looking for a 1:1 port. More generally just the spirit of it.
To start, I made an enum
called CryptoAlgorithm
. I only care to support two crypto algorithms for the sake of this question; but there should be nothing stopping me from extending it further.
enum CryptoAlgorithm {
case MD5, SHA1
}
So far so good. Now to implement the digestLength
.
enum CryptoAlgorithm {
case MD5, SHA1
var digestLength: Int {
switch self {
case .MD5:
return Int(CC_MD5_DIGEST_LENGTH)
case .SHA1:
return Int(CC_SHA1_DIGEST_LENGTH)
}
}
Again, so far so good. Now to implement the initFunction
.
enum CryptoAlgorithm {
case MD5, SHA1
var digestLength: Int {
switch self {
case .MD5:
return Int(CC_MD5_DIGEST_LENGTH)
case .SHA1:
return Int(CC_SHA1_DIGEST_LENGTH)
}
var initFunction: UnsafeMutablePointer<CC_MD5_CTX> -> Int32 {
switch self {
case .MD5:
return CC_MD5_Init
case .SHA1:
return CC_SHA1_Init
}
}
}
Crash and burn. 'CC_MD5_CTX' is not identical to 'CC_SHA1_CTX'
. The problem is that CC_SHA1_Init
is a UnsafeMutablePointer<CC_SHA1_CTX> -> Int32
. Therefore, the two return types are not the same.
Is an enum
the wrong approach? Should I be using generics? If so, how should the generic be made? Should I provide a protocol that both CC_MD5_CTX
and CC_SHA1_CTX
and then are extended by and return that?
All suggestions are welcome (except to use an Objc bridge).
I don't know if I love where this is going in the original ObjC code, because it's pretty type-unsafe. In Swift you just need to make all the type unsafety more explicit:
The more "Swift" way of approaching this would be with protocols, such as:
Then you'd have something like (untested):
But I'd be strongly tempted to hide the context, and just make it:
Why do you need to expose the fact that it's CommonCrypto under the covers? And why would you want to rely on the caller to hold onto the context for you? If it goes out of scope, then later calls will crash. I'd hold onto the context inside.
Getting more closely to your original question, consider this (compiles, but not tested):