I've done some looking around but i've only been able to find examples that use Objective-C to create SHA256 hashes. Is there a way to do this with only Swift4?
You can use like this :
func ccSha256(data: Data) -> Data {
var digest = Data(count: Int(CC_SHA256_DIGEST_LENGTH))
_ = digest.withUnsafeMutableBytes { (digestBytes) in
data.withUnsafeBytes { (stringBytes) in
CC_SHA256(stringBytes, CC_LONG(data.count), digestBytes)
}
}
return digest
}
You can call like this :
let str = "givesomestringtoencode"
let data = ccSha256(data: str.data(using: .utf8)!)
print("sha256 String: \(data.map { String(format: "%02hhx", $0) }.joined())")
Add the below in bridging header file:
#import <CommonCrypto/CommonHMAC.h>
Related
I'm trying to convert this example here:
https://github.com/NilStack/NKWatchChart
to swift2-code. I'm having problems with exactly this part:
data01.getData = ^(NSUInteger index) {
CGFloat yValue = [data01Array[index] floatValue];
return [NKLineChartDataItem dataItemWithY:yValue];
};
getData is defined as follows:
public class NKLineChartData : NSObject {
public var color: UIColor!
public var alpha: CGFloat
public var itemCount: UInt
public var getData: LCLineChartDataGetter!
The LCLineChartDataGetter is a type alias:
public typealias LCLineChartDataGetter = (UInt) -> NKLineChartDataItem!
I'm pretty new to objective-c and swift, so maybe someone can point me to how the assignment can be done using swift!
I found the solution myself. The equivalent in Swift is:
data01.getData = {(index : UInt) -> NKLineChartDataItem in
let yValue : CGFloat = CGFloat(data01Array[Int(index)] as! NSNumber)
return NKLineChartDataItem.init(y: yValue)
}
Maybe someone has a better/shorter solution but the above seems to work ;)
The thing being assigned to getData in Obj-C is a block that takes an integer parameter and returns a NKLineChartDataItem. (Check something like http://goshdarnblocksyntax.com if you need help remembering these.)
In Swift, the formal syntax for the corresponding closure looks like this:
data01.getData = { (index: UInt) -> NKLineChartDataItem! in
// body
}
But you can also use type inference to shorten it:
data01.getData = { index in
// body
}
Looking at more of the example from the NKWatchChart readme you linked to:
NSArray * data01Array = #[#60.1, #160.1, #126.4, #0.0, #186.2, #127.2, #176.2];
NKLineChartData *data01 = [NKLineChartData new];
data01.color = NKGreen;
data01.alpha = 0.9f;
data01.itemCount = data01Array.count;
data01.inflexionPointStyle = NKLineChartPointStyleTriangle;
data01.getData = ^(NSUInteger index) {
CGFloat yValue = [data01Array[index] floatValue];
return [NKLineChartDataItem dataItemWithY:yValue];
};
You can probably shorten this even more through native Swift types:
let data01Array: [CGFloat] = [60.1, 160.1, 126.4, 0.0, 186.2, 127.2, 176.2]
let data01 = NKLineChartData()
data01.color = NKGreen
data01.alpha = 0.9
data01.itemCount = data01Array.count
data01.inflexionPointStyle = .Triangle;
data01.getData = { NKLineChartDataItem(y: data01Array[$0]) }
You don't need to convert swift to objective-c to make it run in your project. Just drag the swift into your project and Xcode will automatically generate a bridging header for you
I am trying replicate some Objective C cocoa in Swift. All is good until I come across the following:
// Set a new type and creator:
unsigned long type = 'TEXT';
unsigned long creator = 'pdos';
How can I create Int64s (or the correct Swift equivalent) from single quote character literals like this?
Types:
public typealias AEKeyword = FourCharCode
public typealias OSType = FourCharCode
public typealias FourCharCode = UInt32
I'm using this in my Cocoa Scripting apps, it considers characters > 0x80 correctly
func OSTypeFrom(string : String) -> UInt {
var result : UInt = 0
if let data = string.dataUsingEncoding(NSMacOSRomanStringEncoding) {
let bytes = UnsafePointer<UInt8>(data.bytes)
for i in 0..<data.length {
result = result << 8 + UInt(bytes[i])
}
}
return result
}
Edit:
Alternatively
func fourCharCodeFrom(string : String) -> FourCharCode
{
assert(string.count == 4, "String length must be 4")
var result : FourCharCode = 0
for char in string.utf16 {
result = (result << 8) + FourCharCode(char)
}
return result
}
or still swiftier
func fourCharCode(from string : String) -> FourCharCode
{
return string.utf16.reduce(0, {$0 << 8 + FourCharCode($1)})
}
I found the following typealiases from the Swift API:
typealias FourCharCode = UInt32
typealias OSType = FourCharCode
And the following functions:
func NSFileTypeForHFSTypeCode(hfsFileTypeCode: OSType) -> String!
func NSHFSTypeCodeFromFileType(fileTypeString: String!) -> OSType
This should allow me to create the equivalent code:
let type : UInt32 = UInt32(NSHFSTypeCodeFromFileType("TEXT"))
let creator : UInt32 = UInt32(NSHFSTypeCodeFromFileType("pdos"))
But those 4-character strings doesn't work and return 0.
If you wrap each string in ' single quotes ' and call the same functions, you will get the correct return values:
let type : UInt32 = UInt32(NSHFSTypeCodeFromFileType("'TEXT'"))
let creator : UInt32 = UInt32(NSHFSTypeCodeFromFileType("'pdos'"))
Adopt the ExpressibleByStringLiteral protocol to use four-character string literals directly:
extension FourCharCode: ExpressibleByStringLiteral {
public init(stringLiteral value: StringLiteralType) {
if let data = value.data(using: .macOSRoman), data.count == 4 {
self = data.reduce(0, {$0 << 8 + Self($1)})
} else {
self = 0
}
}
}
Now you can just pass a string literal as the FourCharCode / OSType / UInt32 parameter:
let record = NSAppleEventDescriptor.record()
record.setDescriptor(NSAppleEventDescriptor(boolean: true), forKeyword: "test")
In Swift 4 or later, I use this code - if the string is not 4 characters in size, it will return an OSType(0):
extension String {
public func osType() -> OSType {
var result:UInt = 0
if let data = self.data(using: .macOSRoman), data.count == 4
{
data.withUnsafeBytes { (ptr:UnsafePointer<UInt8>) in
for i in 0..<data.count {
result = result << 8 + UInt(ptr[i])
}
}
}
return OSType(result)
}
}
let type = "APPL".osType() // 1095782476
// check if this is OK in a playground
let hexStr = String(format: "0x%lx", type) // 0x4150504c -> "APPL" in ASCII
Swift 5 Update:
extension String {
func osType() -> OSType {
return OSType(
data(using: .macOSRoman)?
.withUnsafeBytes {
$0.reduce(into: UInt(0)) { $0 = $0 << 8 + UInt($1) }
} ?? 0
)
}
}
Here's a simple function
func mbcc(foo: String) -> Int
{
let chars = foo.utf8
var result: Int = 0
for aChar in chars
{
result = result << 8 + Int(aChar)
}
return result
}
let a = mbcc("TEXT")
print(String(format: "0x%lx", a)) // Prints 0x54455854
It will work for strings that will fit in an Int. Once they get longer it starts losing digits from the top.
If you use
result = result * 256 + Int(aChar)
you should get a crash when the string gets too big instead.
Using NSHFSTypeCodeFromFileType does work, but only for 4-character strings wrapped with single quotes, aka 6-character strings. It returns 0 for unquoted 4-character strings.
So wrap your 4-character string in ' ' before passing it to the function:
extension FourCharCode: ExpressibleByStringLiteral {
public init(stringLiteral value: StringLiteralType) {
switch (value.count, value.first, value.last) {
case (6, "'", "'"):
self = NSHFSTypeCodeFromFileType(value)
case (4, _, _):
self = NSHFSTypeCodeFromFileType("'\(value)'")
default:
self = 0
}
}
}
Using the above extension, you can use 4-character or single-quoted 6-character string literals:
let record = NSAppleEventDescriptor.record()
record.setDescriptor(NSAppleEventDescriptor(boolean: true), forKeyword: "4444")
record.setDescriptor(NSAppleEventDescriptor(boolean: true), forKeyword: "'6666'")
It would be even better to limit the string literal to 4-character strings at compile time. That does not seem to currently be possible, but is being discussed for Swift here:
Allow for Compile-Time Checked Intervals for Parameters Expecting Literal Values
I have a question about saving Arrays in Apple's new programming language Swift. In Objective-C I saved data with NSFileManager... but this doesn't work anymore in Swift. So I wanted to ask how I should save an array WITHOUT using NSUserDefaults which isn't really suited for storing a big amount of data. I would really much appreciate any help :]
First (if your array is not of string type) change it to String:
var notStringArray = [1, 2, 3, 4]
var array: [String] = []
for value in notStringArray{
array.append(String(value))
}
Then reduce the array to one string:
var array = ["1", "2", "3", "4", "5"] //Ignore this line if your array wasn't of type String and you did the step above
var stringFromArray = reduce(array, "") { $0.isEmpty ? $1 : "\($0)\n\($1)" }
This create an string that looks like this:
"1
2
3
4
5"
And then to write and read a file add this class at the top of your file:
class File {
class func open (path: String, utf8: NSStringEncoding = NSUTF8StringEncoding) -> String? {
var error: NSError? //
return NSFileManager().fileExistsAtPath(path) ? String(contentsOfFile: path, encoding: utf8, error: &error)! : nil
}
class func save (path: String, fileContent: String, utf8: NSStringEncoding = NSUTF8StringEncoding) -> Bool {
var error: NSError? //
return fileContent.writeToFile(path, atomically: true, encoding: utf8, error: &error)
}
}
(Don't forget to import UIKit)
To save to a file:
let didSave = File.save("DirectoryOfFile", content: stringFromArray)
if didSave {
println("file saved")
} else {
println("error saving file")
}
To get it back:
var stringFromFile = ""
if let loadData = File.open("DirectoryOfFile") {
stringFromFile = loadData
} else {
println("error reading file")
}
To put it back in an array:
var newArray: [String] = [] //Creates empty string array
newArray = stringFromFile.componentsSeparatedByString("\n")
And there you have it
I was trying to access temp directory in Swift. In Objective-C, I could use the following code to do so:
- (NSString *)tempDirectory {
NSString *tempDirectoryTemplate =
[NSTemporaryDirectory() stringByAppendingPathComponent:#"XXXXX"];
const char *tempDirectoryTemplateCString = [tempDirectoryTemplate fileSystemRepresentation];
char *tempDirectoryNameCString = (char *)malloc(strlen(tempDirectoryTemplateCString) + 1);
strcpy(tempDirectoryNameCString, tempDirectoryTemplateCString);
char *result = mkdtemp(tempDirectoryNameCString);
if (!result) {
return nil;
}
NSString *tempDirectoryPath = [[NSFileManager defaultManager] stringWithFileSystemRepresentation:tempDirectoryNameCString length:strlen(result)];
free(tempDirectoryNameCString);
return tempDirectoryPath;
}
However, I'm a bit confuse about the type conversion and casting from Objective-C to Swift, such as const char * or CMutablePointer<CChar>. Is there any documents that I should look into?
Thanks.
How about something like :
public extension FileManager {
func createTempDirectory() throws -> String {
let tempDirectory = (NSTemporaryDirectory() as NSString).appendingPathComponent(UUID().uuidString)
try FileManager.default.createDirectory(atPath: tempDirectory,
withIntermediateDirectories: true,
attributes: nil)
return tempDirectory
}
}
It doesn't answer your question about char* but it's cleaner...
NSFileManager reference here.
Also check out this SO question regarding unique names.
According to Apple, use of NSTemporaryDirectory is discouraged:
See the FileManager method url(for:in:appropriateFor:create:) for the
preferred means of finding the correct temporary directory. For more
information about temporary files, see File System Programming Guide.
So instead, you should use FileManager.default.temporaryDirectory
or if you want an unique path:
let extractionPath = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString, isDirectory: true)
Swift 2.1 version:
func createTempDirectory() -> String? {
let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("XXXXXX")
do {
try NSFileManager.defaultManager().createDirectoryAtURL(tempDirURL, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
return tempDirURL.absoluteString
}
Swift 3 and up
I think a good way to do this in swift is with an extension on FileManager. This should create a unique temporary folder and return the URL to you.
extension FileManager{
func createTemporaryDirectory() throws -> URL {
let url = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString)
try createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
return url
}
}
Swift 3 version
func createTempDirectory() -> String? {
guard let tempDirURL = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("myTempFile.xxx") else {
return nil
}
do {
try FileManager.default.createDirectory(at: tempDirURL, withIntermediateDirectories: true, attributes: nil)
} catch {
return nil
}
return tempDirURL.absoluteString
}
A direct translation of your Objective-C code to Swift would be:
func tempDirectory()->String! {
let tempDirectoryTemplate = NSTemporaryDirectory() + "XXXXX"
var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation().copy()
let result : CString = reinterpretCast(mkdtemp(&tempDirectoryTemplateCString))
if !result {
return nil
}
let fm = NSFileManager.defaultManager()
let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
return tempDirectoryPath
}
It uses the same mkdtemp() BSD method as your original code. This method creates
a directory name from the template which is guaranteed not to exist at the time where
the method is called.
Thanks to Nate Cook who figured out that reinterpretCast() can be used to treat the UnsafePointer<CChar> returned by mkdtemp() as a CString, so that it can be passed to stringWithFileSystemRepresentation(), see Working with C strings in Swift, or: How to convert UnsafePointer<CChar> to CString.
As of Xcode 6 beta 6, the reinterpretCast() is not necessary anymore and the
above code can be simplified to
func tempDirectory()->String! {
let tempDirectoryTemplate = NSTemporaryDirectory() + "XXXXX"
var tempDirectoryTemplateCString = tempDirectoryTemplate.fileSystemRepresentation()
let result = mkdtemp(&tempDirectoryTemplateCString)
if result == nil {
return nil
}
let fm = NSFileManager.defaultManager()
let tempDirectoryPath = fm.stringWithFileSystemRepresentation(result, length: Int(strlen(result)))
return tempDirectoryPath
}
I'm going through Jastor's documentation:
There's an Objective-C implementation for returning arrays:
+ (Class)categories_class {
return [ProductCategory class];
}
This is my attempt at converting it to Swift, however it ends up not returning anything so I don't think it's implemented correctly:
#<_TtC4TestApp4Room: id = (null) {
resultCount = 50; // 50 is returning fine
results = ( // results is not
);
}>
NSDictionary response:
{
"resultCount" : 50,
"results" : [
{
"collectionExplicitness" : "notExplicit",
"discCount" : 1,
"artworkUrl60" : "http:\/\/a4.mzstatic.com\/us\/r30\/Features\/2a\/b7\/da\/dj.kkirmfzh.60x60-50.jpg",
"collectionCensoredName" : "Changes in Latitudes, Changes in Attitudes (Ultmate Master Disk Gold CD Reissue)"
}
]
}
Music.swift (not quite sure how to implement the results_class() method)
class Music : Jastor {
var resultCount: NSNumber = 0
var results: NSArray = []
class func results_class() -> AnyClass {
return Author.self
}
}
Author.swift
class Author {
var collectionExplicitness: NSString = ""
var discCount: NSNumber = 0
var artworkUrl60: NSString = ""
var collectionCensoredName: NSString = ""
}
I'm using the following syntax (adapted to your example):
static let results_class = Author.self
and everything works for me.
Other differences that may or may not have an effect:
I'm using Int instead of NSNumber and String instead of NSString (except for arrays).
I'm using implicitly wrapped optionals rather than assigning a default value to each field