Files
purchases-ios/Sources/FoundationExtensions/Data+Extensions.swift
T
Jacob Rakidzich 04bc10266b Finalize video component to include a checksum, dark mode support, and optimize memory usage for large file downloads (#5631)
* Checksum and tests

* add checksum to video component models

* Update cache warming

* put the checksum in the right spot and update the view model

* File repo updates

* add missing assertion

* revert accidental trailiing comma

* Checksum from file

* Ensure cache invalidates when the checksum changes

* Use checksum as filename if available

* greedily fill space while loading occurs

* update remote image usage of the repository

* Reduce memory footprint of large file downloads by streaming the download to a temp file

* Update keyed deferred value storage key to include checksum

* remove unused argument

* remove wip comment

* Get tests to compile

* Write tests for the large item cache type

* remove test from the file repository suite

* trailing commas

* remove cache warming of videos

* remove un-needed change causing existential types

* delete the temporary file after success or failure of the code block

* log with the checksums

* optimize checksum generation to occur at the same time as the temp file storage

* starting to work on border gradients,

* Revert "starting to work on border gradients,"

This reverts commit 429e6d2fab.

* clarity regarding dark mode urls
2025-10-14 08:05:01 -05:00

146 lines
3.4 KiB
Swift

//
// Copyright RevenueCat Inc. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// Data+Extensions.swift
// Purchases
//
// Created by Josh Holtz on 6/28/21.
//
import CommonCrypto
import CryptoKit
import Foundation
extension Data {
var asString: String {
return Self.hexString(
self
.lazy
.map { $0 } // Extract byte
.makeIterator()
)
}
/// - Returns: `UUID` from the first 16 bytes of the underlying data.
var uuid: UUID? {
/// This implementation is equivalent to `return NSUUID(uuidBytes: [UInt8](self)) as UUID`
/// but ensures that the `Data` isn't unnecessarily copied in memory.
return self.dataWithMinLengthForUUID.withUnsafeBytes {
guard let baseAddress = $0.bindMemory(to: UInt8.self).baseAddress else {
return nil
}
return NSUUID(uuidBytes: baseAddress) as UUID
}
}
/// - Returns: a string representing a fetch token.
var asFetchToken: String {
return self.base64EncodedString()
}
/// - Returns: a hash representation of the underlying bytes, using SHA256.
var hashString: String {
var sha256 = SHA256()
return self.hashString(with: &sha256)
}
/// - Returns: the SHA1 hash of the underlying bytes.
var sha1: Data {
var sha1 = Insecure.SHA1()
return self.hash(with: &sha1)
}
var sha256: Data {
var sha256 = SHA256()
return self.hash(with: &sha256)
}
var sha384: Data {
var sha384 = SHA384()
return self.hash(with: &sha384)
}
var sha512: Data {
var sha512 = SHA512()
return self.hash(with: &sha512)
}
var sha256String: String {
var sha256 = SHA256()
return self.hashString(with: &sha256)
}
var sha384String: String {
var sha384 = SHA384()
return self.hashString(with: &sha384)
}
var sha512String: String {
var sha512 = SHA512()
return self.hashString(with: &sha512)
}
var md5String: String {
var md5 = Insecure.MD5()
return self.hashString(with: &md5)
}
fileprivate static func hexString(_ iterator: Array<UInt8>.Iterator) -> String {
return iterator
.lazy
.map { String(format: "%02x", $0) }
.joined()
}
private var dataWithMinLengthForUUID: Data {
let uuidMemorySize = MemoryLayout<UUID>.size
guard self.count >= uuidMemorySize else {
return self + Data(count: uuidMemorySize - self.count)
}
return self
}
}
extension Data {
static func randomNonce() -> Data {
return Data(ChaChaPoly.Nonce())
}
static let nonceLength = 12
}
// MARK: - Hashing
extension HashFunction {
func toString() -> String {
return Data.hexString(self.finalize().makeIterator())
}
}
private extension Data {
func hashString<T: HashFunction>(with digest: inout T) -> String {
digest.update(data: self)
return digest.toString()
}
func hash<T: HashFunction>(with digest: inout T) -> Data {
digest.update(data: self)
return Data(digest.finalize())
}
}