Related
I a codable serialization extension which I use to turn my Codable struct to dictionaries, the problem I am facing is strings. I get string value from my UITextField at at times this value could be empty and as a result an empty string is decoded. How can I return nil if the value is an empty string.
extension Encodable {
var requestDictionary: [String: Any]? {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
guard let data = try? encoder.encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
}
if I have a Struct
let example = Exa(age: 10, name: "")
let dict = example.requestDictionary
print(dict)
I want it to just print ["age": 10] and return nil for the empty string
You can implement your own String encoding method extending KeyedEncodingContainer:
extension KeyedEncodingContainer {
mutating func encode(_ value: String, forKey key: K) throws {
guard !value.isEmpty else { return }
try encodeIfPresent(value, forKey: key)
}
}
Btw your request dictionary can be simplified as:
extension Encodable {
var dictionary: [String: Any]? {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
return try? JSONSerialization.jsonObject(with: encoder.encode(self)) as? [String: Any]
}
}
Playground testing:
struct Exa: Encodable {
let age: Int
let name: String
}
let example = Exa(age: 10, name: "")
let dict = example.dictionary!
print(dict) // "["age": 10]\n"
I'll just another approach using a property wrapper to mark which properties could be skipped.
#propertyWrapper
struct SkipEmpty {
var wrappedValue: String
}
extension SkipEmpty: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.wrappedValue = try container.decode(String.self)
}
func encode(to encoder: Encoder) throws {
// nothing to do here, see below
}
}
But to actually skip, you'd also need to create a overload for the KeyedEncodingContainer.encode method for the SkipEmpty type:
extension KeyedEncodingContainer {
mutating func encode(_ value: SkipEmpty, forKey key: K) throws {
if !value.wrappedValue.isEmpty {
try encode(value.wrappedValue, forKey: key) // encode the value here
}
}
}
You could possibly try to make it more generic, e.g. SkipEmpty<T: Codable> and provide another argument for the value to skip or a predicate, etc...
The usage is:
struct Exa: Encodable {
var age: Int
#SkipEmpty var name: String
}
Issue: "Type HASH256 has no member hash"
Background: Trying to implement Apple sign in with Firebase on Swift
Tried to resolve the issue with the following:
-all pods update
-import CommonCrypto + import CryptoKit
-clean build folder / build
The error is still present
// Adapted from https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce
private func randomNonceString(length: Int = 32) -> String {
precondition(length > 0)
let charset: Array<Character> =
Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._")
var result = ""
var remainingLength = length
while remainingLength > 0 {
let randoms: [UInt8] = (0 ..< 16).map { _ in
var random: UInt8 = 0
let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random)
if errorCode != errSecSuccess {
fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)")
}
return random
}
randoms.forEach { random in
if length == 0 {
return
}
if random < charset.count {
result.append(charset[Int(random)])
remainingLength -= 1
}
}
}
return result
}
//Start Apple's sign-in flow
// Unhashed nonce.
fileprivate var currentNonce: String?
#available(iOS 13, *)
func startSignInWithAppleFlow() {
let nonce = randomNonceString()
currentNonce = nonce
let appleIDProvider = ASAuthorizationAppleIDProvider()
let request = appleIDProvider.createRequest()
request.requestedScopes = [.fullName, .email]
request.nonce = sha256(nonce)
let authorizationController = ASAuthorizationController(authorizationRequests: [request])
authorizationController.delegate = self as! ASAuthorizationControllerDelegate
authorizationController.presentationContextProvider = self as! ASAuthorizationControllerPresentationContextProviding
authorizationController.performRequests()
}
#available(iOS 13, *)
private func sha256(_ input: String) -> String {
let inputData = Data(input.utf8)
let hashedData = SHA256.hash(data: inputData)
let hashString = hashedData.compactMap {
return String(format: "%02x", $0)
}.joined()
return hashString
}
// func SHA256() -> String {
//
// let data = self.data(using: String.Encoding.utf8)
// let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
// CC_SHA256(((data! as NSData)).bytes, CC_LONG(data!.count), res?.mutableBytes.assumingMemoryBound(to: UInt8.self))
// let hashedString = "\(res!)".replacingOccurrences(of: "", with: "").replacingOccurrences(of: " ", with: "")
// let badchar: CharacterSet = CharacterSet(charactersIn: "\"<\",\">\"")
// let cleanedstring: String = (hashedString.components(separatedBy: badchar) as NSArray).componentsJoined(by: "")
// return cleanedstring
//
// }
}
//Apple extension
#available(iOS 13.0, *)
extension AuthViewController: ASAuthorizationControllerDelegate {
func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
guard let nonce = currentNonce else {
fatalError("Invalid state: A login callback was received, but no login request was sent.")
}
guard let appleIDToken = appleIDCredential.identityToken else {
print("Unable to fetch identity token")
return
}
guard let idTokenString = String(data: appleIDToken, encoding: .utf8) else {
print("Unable to serialize token string from data: \(appleIDToken.debugDescription)")
return
}
// Initialize a Firebase credential.
let credential = OAuthProvider.credential(withProviderID: "apple.com",
idToken: idTokenString,
accessToken: nonce)
// Sign in with Firebase.
Auth.auth().signIn(with: credential) { (authResult, error) in
if (error != nil) {
// Error. If error.code == .MissingOrInvalidNonce, make sure
// you're sending the SHA256-hashed nonce as a hex string with
// your request to Apple.
print(error?.localizedDescription)
return
}
// User is signed in to Firebase with Apple.
// ...
}
}
}
func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) {
// Handle error.
print("Sign in with Apple errored: \(error)")
}
}
Image of error
I encountered the same problem, I spend two days figured it out!
The reason is we mistaken installed 'CryptoKit' in our Podfile. which apple also has a build-in 'CryptoKit' for iOS version 13+.
Solution :
1.deleted pod ''CryptoKit' in our pod file.
2. pod install
after that, we will use apple build in 'CryptoKit' which has the build-in method hash.
This should work: add this outside of your class and then instead of request.nonce = sha256(nonce), type request.nonce = nonce.sha256()
extension String {
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return hexStringFromData(input: digest(input: stringData as NSData))
}
return ""
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
credit
I want to generate random bytes using SecRandomCopyBytes in Swift 3.0. Here is how I did it in Swift 2.2
private static func generateRandomBytes() -> String? {
let data = NSMutableData(length: Int(32))
let result = SecRandomCopyBytes(kSecRandomDefault, 32, UnsafeMutablePointer<UInt8>(data!.mutableBytes))
if result == errSecSuccess {
return data!.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
} else {
print("Problem generating random bytes")
return nil
}
}
In Swift 3, I tried to do it like this, since I know the concept of unsafemutablebytes is different now, but it doesn't allow me to return. If I comment out the return part, it still says Generic Parameter ResultType could not be inferred
fileprivate static func generateRandomBytes() -> String? {
var keyData = Data(count: 32)
_ = keyData.withUnsafeMutableBytes {mutableBytes in
let result = SecRandomCopyBytes(kSecRandomDefault, keyData.count, mutableBytes)
if result == errSecSuccess {
return keyData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
} else {
print("Problem generating random bytes")
return nil
}
}
return nil
}
Does anyone know how to fix this?
Thanks
You were close, but return inside the closure returns
from the closure, not from the outer function.
Therefore only SecRandomCopyBytes() should be called in the
closure, and the result passed back.
func generateRandomBytes() -> String? {
var keyData = Data(count: 32)
let result = keyData.withUnsafeMutableBytes {
(mutableBytes: UnsafeMutablePointer<UInt8>) -> Int32 in
SecRandomCopyBytes(kSecRandomDefault, 32, mutableBytes)
}
if result == errSecSuccess {
return keyData.base64EncodedString()
} else {
print("Problem generating random bytes")
return nil
}
}
For a "single-expression closure" the closure type can inferred
automatically, so this can be shortened to
func generateRandomBytes() -> String? {
var keyData = Data(count: 32)
let result = keyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, 32, $0)
}
if result == errSecSuccess {
return keyData.base64EncodedString()
} else {
print("Problem generating random bytes")
return nil
}
}
Swift 5 update:
func generateRandomBytes() -> String? {
var keyData = Data(count: 32)
let result = keyData.withUnsafeMutableBytes {
SecRandomCopyBytes(kSecRandomDefault, 32, $0.baseAddress!)
}
if result == errSecSuccess {
return keyData.base64EncodedString()
} else {
print("Problem generating random bytes")
return nil
}
}
This is the simplest and "Swiftiest" way to implement your function using Swift 5:
func generateRandomBytes() -> String? {
var bytes = [UInt8](repeating: 0, count: 32)
let result = SecRandomCopyBytes(kSecRandomDefault, bytes.count, &bytes)
guard result == errSecSuccess else {
print("Problem generating random bytes")
return nil
}
return Data(bytes).base64EncodedString()
}
Generally it is best practice in Swift to use guard statements as opposed to if/else statements when the control flow of a function depends on the success or failure of an expression or the presence of a non-nil value.
According to Apple Documentation it looks similar to this:
public func randomData(ofLength length: Int) throws -> Data {
var bytes = [UInt8](repeating: 0, count: length)
let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)
if status == errSecSuccess {
return Data(bytes: bytes)
}
// throw an error
}
or as an additional initializer:
public extension Data {
public init(randomOfLength length: Int) throws {
var bytes = [UInt8](repeating: 0, count: length)
let status = SecRandomCopyBytes(kSecRandomDefault, length, &bytes)
if status == errSecSuccess {
self.init(bytes: bytes)
} else {
// throw an error
}
}
}
In objective-c it looks like this:
#include <sys/xattr.h>
#implementation NSString (reverse)
-(NSString*)sha1
{
NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
uint8_t digest[CC_SHA1_DIGEST_LENGTH];
CC_SHA1(data.bytes, (int)data.length, digest);
NSMutableString *output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
[output appendFormat:#"%02x", digest[i]];
return output;
}
#end
I need something like this with Swift, is it possible?
Please, show work example.
Your Objective-C code (using a NSString category) can be directly translated to Swift
(using a String extension).
First you have to create a "bridging header" and add
#import <CommonCrypto/CommonCrypto.h>
Then:
extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let output = NSMutableString(capacity: Int(CC_SHA1_DIGEST_LENGTH))
for byte in digest {
output.appendFormat("%02x", byte)
}
return output as String
}
}
println("Hello World".sha1())
This can be written slightly shorter and Swifter as
extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = map(digest) { String(format: "%02hhx", $0) }
return "".join(hexBytes)
}
}
Update for Swift 2:
extension String {
func sha1() -> String {
let data = self.dataUsingEncoding(NSUTF8StringEncoding)!
var digest = [UInt8](count:Int(CC_SHA1_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA1(data.bytes, CC_LONG(data.length), &digest)
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")
}
}
To return a Base-64 encoded string instead of a hex encoded string,
just replace
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joinWithSeparator("")
with
return NSData(bytes: digest, length: digest.count).base64EncodedStringWithOptions([])
Update for Swift 3:
extension String {
func sha1() -> String {
let data = self.data(using: String.Encoding.utf8)!
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}
To return a Base-64 encoded string instead of a hex encoded string,
just replace
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
by
return Data(bytes: digest).base64EncodedString()
Update for Swift 4:
The bridging header file is no longer needed, one can import CommonCrypto instead:
import CommonCrypto
extension String {
func sha1() -> String {
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}
Update for Swift 5:
The Data.withUnsafeBytes() method now calls the closure with an UnsafeRawBufferPointer to, and baseAddress is used to pass the initial address to the C function:
import CommonCrypto
extension String {
func sha1() -> String {
let data = Data(self.utf8)
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA1($0.baseAddress, CC_LONG(data.count), &digest)
}
let hexBytes = digest.map { String(format: "%02hhx", $0) }
return hexBytes.joined()
}
}
With CryptoKit added in iOS13, we now have native Swift API:
import Foundation
import CryptoKit
// CryptoKit.Digest utils
extension Digest {
var bytes: [UInt8] { Array(makeIterator()) }
var data: Data { Data(bytes) }
var hexStr: String {
bytes.map { String(format: "%02X", $0) }.joined()
}
}
func example() {
guard let data = "hello world".data(using: .utf8) else { return }
let digest = Insecure.SHA1.hash(data: data)
print(digest.data) // 20 bytes
print(digest.hexStr) // 2AAE6C35C94FCFB415DBE95F408B9CE91EE846ED
}
A version for Swift 5 that uses CryptoKit on iOS 13 and falls back to CommonCrypto otherwise:
import CommonCrypto
import CryptoKit
import Foundation
private func hexString(_ iterator: Array<UInt8>.Iterator) -> String {
return iterator.map { String(format: "%02x", $0) }.joined()
}
extension Data {
public var sha1: String {
if #available(iOS 13.0, *) {
return hexString(Insecure.SHA1.hash(data: self).makeIterator())
} else {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
self.withUnsafeBytes { bytes in
_ = CC_SHA1(bytes.baseAddress, CC_LONG(self.count), &digest)
}
return hexString(digest.makeIterator())
}
}
}
Usage:
let string = "The quick brown fox jumps over the lazy dog"
let hexDigest = string.data(using: .ascii)!.sha1
assert(hexDigest == "2fd4e1c67a2d28fced849ee1bb76e7391b93eb12")
Also available via Swift package manager:
https://github.com/ralfebert/TinyHashes
Yes, it's possible, copy this class into your project.
https://github.com/idrougge/sha1-swift
And it will be easy like:
SHA1.hexString(from: "myPhrase" )!
Tested for swift 3 and swift 4.
To get the result as NSData, provided that you included <CommonCrypto/CommonCrypto.h> in your bridging header:
extension NSData {
func sha1() -> NSData? {
let len = Int(CC_SHA1_DIGEST_LENGTH)
let digest = UnsafeMutablePointer<UInt8>.alloc(len)
CC_SHA1(bytes, CC_LONG(length), digest)
return NSData(bytesNoCopy: UnsafeMutablePointer<Void>(digest), length: len)
}
}
Also uses proper pointer allocation. Invoke it like this:
myString.dataUsingEncoding(NSUTF8StringEncoding)?.sha1()
If you need a hex representation of NSData have a look at my other answer.
We can extract logic for encrypting string using sha1 for three steps:
Convert string to Data object
Encrypt data using SHA1 function to Data
Convert data object to hex string
IMHO it's much more readable and this version doesn't require NSData.
extension String {
var sha1: String {
guard let data = data(using: .utf8, allowLossyConversion: false) else {
// Here you can just return empty string or execute fatalError with some description that this specific string can not be converted to data
}
return data.digestSHA1.hexString
}
}
fileprivate extension Data {
var digestSHA1: Data {
var bytes: [UInt8] = Array(repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH))
withUnsafeBytes {
_ = CC_SHA1($0, CC_LONG(count), &bytes)
}
return Data(bytes: bytes)
}
var hexString: String {
return map { String(format: "%02x", UInt8($0)) }.joined()
}
}
Yes, it's possible: make that objective-c code accessible from swift
See documentation.
I would avoid rewriting it in swift if you won't get any benefit (such as using swift-specific features).
Also, in a project I am working on I used some objective-c code similar to yours to handle hashes. At beginning I started writing it in swift, then I realized that it was just easier and better to reuse old good obj-c.
I want to use sha256 in my project, but I had some troubles rewriting objC code to swift code. Help me please. I used this answer: How can I compute a SHA-2 (ideally SHA 256 or SHA 512) hash in iOS?
Here's my code
var hash : [CUnsignedChar]
CC_SHA256(data.bytes, data.length, hash)
var res : NSData = NSData.dataWithBytes(hash, length: CC_SHA256_DIGEST_LENGTH)
it gives me error everything because swift cannot convert Int to CC_LONG, for example.
You have to convert explicitly between Int and CC_LONG, because Swift does not
do implicit conversions, as in (Objective-)C.
You also have to define hash as an array of the required size.
func sha256(data : NSData) -> NSData {
var hash = [UInt8](count: Int(CC_SHA256_DIGEST_LENGTH), repeatedValue: 0)
CC_SHA256(data.bytes, CC_LONG(data.length), &hash)
let res = NSData(bytes: hash, length: Int(CC_SHA256_DIGEST_LENGTH))
return res
}
Alternatively, you can use NSMutableData to allocate the needed buffer:
func sha256(data : NSData) -> NSData {
let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256(data.bytes, CC_LONG(data.length), UnsafeMutablePointer(res.mutableBytes))
return res
}
Update for Swift 3 and 4:
func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0, CC_LONG(data.count), &hash)
}
return Data(bytes: hash)
}
Update for Swift 5:
func sha256(data : Data) -> Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &hash)
}
return Data(hash)
}
Updated for Swift 5.
Put this extension somewhere in your project and use it on a string like this: mystring.sha256(), or on data with data.sha256()
import Foundation
import CommonCrypto
extension Data{
public func sha256() -> String{
return hexStringFromData(input: digest(input: self as NSData))
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
private func hexStringFromData(input: NSData) -> String {
var bytes = [UInt8](repeating: 0, count: input.length)
input.getBytes(&bytes, length: input.length)
var hexString = ""
for byte in bytes {
hexString += String(format:"%02x", UInt8(byte))
}
return hexString
}
}
public extension String {
func sha256() -> String{
if let stringData = self.data(using: String.Encoding.utf8) {
return stringData.sha256()
}
return ""
}
}
With CryptoKit added in iOS13, we now have native Swift API:
import Foundation
import CryptoKit
// CryptoKit.Digest utils
extension Digest {
var bytes: [UInt8] { Array(makeIterator()) }
var data: Data { Data(bytes) }
var hexStr: String {
bytes.map { String(format: "%02X", $0) }.joined()
}
}
func example() {
guard let data = "hello world".data(using: .utf8) else { return }
let digest = SHA256.hash(data: data)
print(digest.data) // 32 bytes
print(digest.hexStr) // B94D27B9934D3E08A52E52D7DA7DABFAC484EFE37A5380EE9088F7ACE2EFCDE9
}
Because utils are defined for protocol Digest, you can use it for all digest type in CryptoKit, like SHA384Digest, SHA512Digest, SHA1Digest, MD5Digest...
Functions giving the SHA from NSData & String (Swift 3):
func sha256(_ data: Data) -> Data? {
guard let res = NSMutableData(length: Int(CC_SHA256_DIGEST_LENGTH)) else { return nil }
CC_SHA256((data as NSData).bytes, CC_LONG(data.count), res.mutableBytes.assumingMemoryBound(to: UInt8.self))
return res as Data
}
func sha256(_ str: String) -> String? {
guard
let data = str.data(using: String.Encoding.utf8),
let shaData = sha256(data)
else { return nil }
let rc = shaData.base64EncodedString(options: [])
return rc
}
Include in your bridging header:
#import "CommonCrypto/CommonCrypto.h"
A version for Swift 5 that uses CryptoKit on iOS 13 and falls back to CommonCrypto otherwise:
import CommonCrypto
import CryptoKit
import Foundation
private func hexString(_ iterator: Array<UInt8>.Iterator) -> String {
return iterator.map { String(format: "%02x", $0) }.joined()
}
extension Data {
public var sha256: String {
if #available(iOS 13.0, *) {
return hexString(SHA256.hash(data: self).makeIterator())
} else {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
self.withUnsafeBytes { bytes in
_ = CC_SHA256(bytes.baseAddress, CC_LONG(self.count), &digest)
}
return hexString(digest.makeIterator())
}
}
}
Usage:
let string = "The quick brown fox jumps over the lazy dog"
let hexDigest = string.data(using: .ascii)!.sha256
assert(hexDigest == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592")
Also available via Swift package manager:
https://github.com/ralfebert/TinyHashes
I researched many answers and I summarized it:
import CryptoKit
import CommonCrypto
extension String {
func hash256() -> String {
let inputData = Data(utf8)
if #available(iOS 13.0, *) {
let hashed = SHA256.hash(data: inputData)
return hashed.compactMap { String(format: "%02x", $0) }.joined()
} else {
var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
inputData.withUnsafeBytes { bytes in
_ = CC_SHA256(bytes.baseAddress, UInt32(inputData.count), &digest)
}
return digest.makeIterator().compactMap { String(format: "%02x", $0) }.joined()
}
}
}
import CommonCrypto
public extension String {
var sha256: String {
let data = Data(utf8)
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
data.withUnsafeBytes { buffer in
_ = CC_SHA256(buffer.baseAddress, CC_LONG(buffer.count), &hash)
}
return hash.map { String(format: "%02hhx", $0) }.joined()
}
}
Here's my simple 3-line Swift 4 function for this using the Security Transforms API, which is part of Foundation on macOS. (Unfortunately iOS programmers cannot use this technique.)
import Foundation
extension Data {
public func sha256Hash() -> Data {
let transform = SecDigestTransformCreate(kSecDigestSHA2, 256, nil)
SecTransformSetAttribute(transform, kSecTransformInputAttributeName, self as CFTypeRef, nil)
return SecTransformExecute(transform, nil) as! Data
}
}
Here's a method that uses the CoreFoundation Security Transforms API, so you don't even need to link to CommonCrypto. For some reason in 10.10/Xcode 7 linking to CommmonCrypto with Swift is drama so I used this instead.
This method reads from an NSInputStream, which you can either get from a file, or you can make one that reads an NSData, or you can make bound reader/writer streams for a buffered process.
// digestType is from SecDigestTransform and would be kSecDigestSHA2, etc
func digestForStream(stream : NSInputStream,
digestType type : CFStringRef, length : Int) throws -> NSData {
let transform = SecTransformCreateGroupTransform().takeRetainedValue()
let readXform = SecTransformCreateReadTransformWithReadStream(stream as CFReadStreamRef).takeRetainedValue()
var error : Unmanaged<CFErrorRef>? = nil
let digestXform : SecTransformRef = try {
let d = SecDigestTransformCreate(type, length, &error)
if d == nil {
throw error!.takeUnretainedValue()
} else {
return d.takeRetainedValue()
}
}()
SecTransformConnectTransforms(readXform, kSecTransformOutputAttributeName,
digestXform, kSecTransformInputAttributeName,
transform, &error)
if let e = error { throw e.takeUnretainedValue() }
if let output = SecTransformExecute(transform, &error) as? NSData {
return output
} else {
throw error!.takeUnretainedValue()
}
}
Tested in Swift5.
In case you want to get the hash in String,
this is how I did.
private func getHash(_ phrase:String) -> String{
let data = phrase.data(using: String.Encoding.utf8)!
let length = Int(CC_SHA256_DIGEST_LENGTH)
var digest = [UInt8](repeating: 0, count: length)
data.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(data.count), &digest)
}
return digest.map { String(format: "%02x", $0) }.joined(separator: "")
}
For Swift 5:
guard let data = self.data(using: .utf8) else { return nil }
var sha256 = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
sha256.withUnsafeMutableBytes { sha256Buffer in
data.withUnsafeBytes { buffer in
let _ = CC_SHA256(buffer.baseAddress!, CC_LONG(buffer.count), sha256Buffer.bindMemory(to: UInt8.self).baseAddress)
}
}
return sha256
The other answers will have performance problems for calculating digests from large amounts of data (e.g. large files). You will not want to load all data into memory at once. Consider the following approach using update/finalize:
final class SHA256Digest {
enum InputStreamError: Error {
case createFailed(URL)
case readFailed
}
private lazy var context: CC_SHA256_CTX = {
var shaContext = CC_SHA256_CTX()
CC_SHA256_Init(&shaContext)
return shaContext
}()
private var result: Data? = nil
init() {
}
func update(url: URL) throws {
guard let inputStream = InputStream(url: url) else {
throw InputStreamError.createFailed(url)
}
return try update(inputStream: inputStream)
}
func update(inputStream: InputStream) throws {
guard result == nil else {
return
}
inputStream.open()
defer {
inputStream.close()
}
let bufferSize = 4096
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
defer {
buffer.deallocate()
}
while true {
let bytesRead = inputStream.read(buffer, maxLength: bufferSize)
if bytesRead < 0 {
//Stream error occured
throw (inputStream.streamError ?? InputStreamError.readFailed)
} else if bytesRead == 0 {
//EOF
break
}
self.update(bytes: buffer, length: bytesRead)
}
}
func update(data: Data) {
guard result == nil else {
return
}
data.withUnsafeBytes {
self.update(bytes: $0, length: data.count)
}
}
func update(bytes: UnsafeRawPointer, length: Int) {
guard result == nil else {
return
}
_ = CC_SHA256_Update(&self.context, bytes, CC_LONG(length))
}
func finalize() -> Data {
if let calculatedResult = result {
return calculatedResult
}
var resultBuffer = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
CC_SHA256_Final(&resultBuffer, &self.context)
let theResult = Data(bytes: resultBuffer)
result = theResult
return theResult
}
}
extension Data {
private static let hexCharacterLookupTable: [Character] = [
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"a",
"b",
"c",
"d",
"e",
"f"
]
var hexString: String {
return self.reduce(into: String(), { (result, byte) in
let c1: Character = Data.hexCharacterLookupTable[Int(byte >> 4)]
let c2: Character = Data.hexCharacterLookupTable[Int(byte & 0x0F)]
result.append(c1)
result.append(c2)
})
}
}
You could use it as follows:
let digest = SHA256Digest()
try digest.update(url: fileURL)
let result = digest.finalize().hexString
print(result)
I prefer to use:
extension String {
var sha256:String? {
guard let stringData = self.data(using: String.Encoding.utf8) else { return nil }
return digest(input: stringData as NSData).base64EncodedString(options: [])
}
private func digest(input : NSData) -> NSData {
let digestLength = Int(CC_SHA256_DIGEST_LENGTH)
var hash = [UInt8](repeating: 0, count: digestLength)
CC_SHA256(input.bytes, UInt32(input.length), &hash)
return NSData(bytes: hash, length: digestLength)
}
}
The hasded String is base64 encoded.