Accessing temp directory in Swift - objective-c

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
}

Related

Picture Storage as URL in SQLite DB in Swift 5

I have a swift project that uses an array of pictures. The array of pictures is kept in storage in the filmanager.default.urls path. The URLs are then kept in a SQLite database. I am able to add to the array without a problem, however, I run into an issue whenever I try to recall it from memory. I do not know if I am saving it wrong or loading it wrong. I will post the code for how I did that here.
func save(images: Array<UIImage>, identifier: String) -> Array<String> {
var picCounter = 0
var URLs: Array<String> = []
for pic in images {
let id = identifier + "makeSureThisCantAccidentallyBeAnID" + String(picCounter)
let jpgImageData = pic.jpegData(compressionQuality: 0.5)
let documentURL = fileManager.urls(for: .documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first
let path = documentURL!.appendingPathComponent(id + ".png")
do {
try jpgImageData!.write(to: path)
} catch {
print(error)
}
picCounter += 1
URLs.append(path.absoluteString)
}
return URLs
}
That is my save function. It should store the URLs and return an array of URLs in string format.
Here is my loading function, it should take an array of URLs and return the array of images.
func recover(URLarray: Array<String>, identifier: String) -> Array<UIImage> {
if URLarray[0] == "fake url" {
let pic = #imageLiteral(resourceName: "LaunchScreen")
let fakeArray = [pic]
return fakeArray
}
var picCounter = 0
var imageArray: Array<UIImage> = []
for url in URLarray {
//let fileData = FileManager.default.contents(atPath: url)
guard let image = UIImage(contentsOfFile: url) else { return imageArray }
picCounter += 1
imageArray.append(image)
}
return imageArray
}
The "#imageliteral" is a literal image that is used instead of an array if there is no array to load. This should theoretically recall every image without returning nil. Lastly, I will post my SQLite saving function
func saveNote(note: Note) {
connect()
var statement: OpaquePointer? = nil
if sqlite3_prepare_v2(
database,
"UPDATE remember SET person = ?, memories = ?, imageurl = ? WHERE rowid = ?",
-1,
&statement,
nil
) == SQLITE_OK {
sqlite3_bind_text(statement, 1, NSString(string: note.person).utf8String, -1, nil)
sqlite3_bind_text(statement, 2, NSString(string: note.memories).utf8String, -1, nil)
var imageURL: String = ""
for url in note.imageURLs {
imageURL.append(url)
imageURL.append("#")
}
sqlite3_bind_text(statement, 3, NSString(string: imageURL).utf8String, -1, nil)
sqlite3_bind_int(statement, 4, note.id)
if sqlite3_step(statement) != SQLITE_DONE {
print("Error saving note")
}
}
else {
print("Error creating note update statement")
}
sqlite3_finalize(statement)
}
This is how I tried to save it. I had to store a URL array as one string so I decided to do so by making it a string I could split with the character "#" if you think that is the problem then please tell me what character or method I could use to do this, as I have tried changing the character. If anyone helps me this far then I am extremely grateful! Thank you!

CFDictionaryGetValue throws EXC_BAD_ACCESS

I found a code snipped from Getting graphic card information in objective C in Objective-C and I am currently trying to convert it to Swift.
I am trying to read a value from a CFMutableDictionary (code is below). However when I call the function CFDictionaryGetValue I get an error:
"Thread 1: EXC_BAD_ACCESS (code=1, address=0x656d614e4f60)"
Here is my current code:
static func getGpuName() {
var iterator: io_iterator_t = 0
let errCode: kern_return_t = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOPCIDevice"), &iterator)
if errCode != kIOReturnSuccess {
fatalError("Could not retrieve the service dictionary of \"IOPCIDevice\"")
}
// iterate over the pci devices
var device = IOIteratorNext(iterator)
while device != 0 {
var unmanagedServiceDictionary: Unmanaged<CFMutableDictionary>?
if IORegistryEntryCreateCFProperties(device, &unmanagedServiceDictionary, kCFAllocatorDefault, 0) != kIOReturnSuccess {
IOObjectRelease(device)
continue
}
if let serviceDictionary: CFMutableDictionary = unmanagedServiceDictionary?.takeRetainedValue() {
let name = CFDictionaryGetValue(serviceDictionary, "IOName")
}
// release the device
IOObjectRelease(device)
// get the next device from the iterator
device = IOIteratorNext(iterator)
}
}
Does anybody have an idea how I can read the value of the CFMutableDictionary?
Thanks :)
Handling the CoreFoundation API is a real pain.
The error occurs because you cannot pass a literal String as second parameter of CFDictionaryGetValue which must be a UnsafeRawPointer.
However the solution is pretty easy. Cast the dictionary to a Swift dictionary
if let serviceDictionary = unmanagedServiceDictionary?.takeRetainedValue() as? [String:Any] {
if let name = serviceDictionary["IOName"] as? String {
print(name)
}
}
Ok so after some more research I still don't know why the error is thrown. However I found a workaround by casting the dictionary to a NSDictionary.
The following code works now:
let serviceDictionary: NSDictionary = (unmanagedServiceDictionary?.takeRetainedValue())! as NSDictionary
if let name = serviceDictionary.value(forKey: "IOName") as? String {
print(name)
}

Trouble converting NSData Objective-C code to Swift

I've been having issues converting an Objective-C snippet to Swift that uses NSData and CoreBluetooth. I have looked at this question and a couple others dealing with NSData in Swift but haven't had any success.
Objective-C Snippet:
- (CGFloat) minTemperature
{
CGFloat result = NAN;
int16_t value = 0;
// characteristic is a CBCharacteristic
if (characteristic) {
[[characteristic value] getBytes:&value length:sizeof (value)];
result = (CGFloat)value / 10.0f;
}
return result;
}
What I have so far in Swift (not working):
func minTemperature() -> CGFloat {
let bytes = [UInt8](characteristic?.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
value = Int16(fPointer.pointee)
result = CGFloat(value / 10) // not correct value
return result
}
Does the logic look wrong here? Thanks!
One error is in
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
because the rebound pointer $0 is only valid inside the closure and must
not be passed to the outside. Also the capacity should be 1 for a
single Int16 value. Another problem is the integer division in
result = CGFloat(value / 10)
which truncates the result (as already observed by the4kman).
Creating an [UInt8] array from the data is not necessary, the
withUnsafeBytes() method of Data can be used instead.
Finally you could return nil (instead of "not a number") if no
characteristic value is given:
func minTemperature() -> CGFloat? {
guard let value = characteristic?.value else {
return nil
}
let i16val = value.withUnsafeBytes { (ptr: UnsafePointer<Int16>) in
ptr.pointee
}
return CGFloat(i16val) / 10.0
}
You should make the return value optional and check if characteristic is nil in the beginning with a guard. You should also explicitly convert the value to CGFloat, then divide it by 10.
func minTemperature() -> CGFloat? {
guard characteristic != nil else {
return nil
  }
let bytes = [UInt8](characteristic!.value)
let pointer = UnsafePointer<UInt8>(bytes)
let fPointer = pointer.withMemoryRebound(to: Int16.self, capacity: 2) { return $0 }
let value = Int16(fPointer.pointee)
result = CGFloat(value) / 10
return result
}

Swift Cannot convert value of type CBPeripheral to expected argument type

The code I have:
func didDiscoverBLE(_ peripheral: CBPeripheral!, address: String!, rssi: Int32) {
DispatchQueue.main.async(execute: {() -> Void in
// Handle Discovery
self.arrayPeripehral.contains(where:peripheral)
return
})
self.arrayPeripehral.append(peripheral)
let title: String = "\(peripheral.name) \(address) (RSSI:\(rssi))"
self.arrayPeripheralName.append(title)
In this line i have a problem:
self.arrayPeripehral.contains(where:peripheral)
return
})
Has anyone an idea?
Here is the code I copied from the obective c to the swift and got stuck on this error
- (void)didDiscoverBLE:(CBPeripheral *)peripheral address:(NSString *)address rssi:(int)rssi
{
dispatch_async(dispatch_get_main_queue(), ^{
// Handle Discovery
if([arrayPeripehral containsObject:peripheral])
return;
[arrayPeripehral addObject:peripheral];
NSString * title = [NSString stringWithFormat:#"%# %# (RSSI:%d)", peripheral.name, address, rssi];
[arrayPeripheralName addObject:title];
Change the type of arrayPeripehral to [CBPeripheral] from [Any] that will give compiler more idea about its type then use contains(where:) like this to check array contains object or not.
var arrayPeripehral = [CBPeripheral]()
Now use contains(where:) this way to check array contains object or not.
if self.arrayPeripehral.contains(where: { $0.name == peripheral.name }) {
return
}
Also change type declaration of arrayPeripheralName to [String] from [Any] as of you are appending only String object in it.
var arrayPeripheralName = [String]()

Swift: Alternatives to super class methods to generate objects

I have the following Objective-C code I'm trying to convert to swift:
-(id)initWithBook:(NSString*)bookTitle author:(NSString*)author description:(NSString*)description{
self = [super init];
if (self) {
self.bookTitle = [bookTitle copy];
self.author = [author copy];
self.description = [uri description];
}
return self;
}
+(NSArray*)listOfBooks:(NSArray*)jsonWithBooks{
NSMutableArray *elements = [NSMutableArray new];
for (NSDictionary *dictElment in jsonRespnse){
Books *booksData = [[Books alloc] initWithBook:[dictElment objectForKey:#"bookTitle"]
title:[dictElment objectForKey:#"author"]
description:[dictElment objectForKey:#"description"]];
[elements addObject:booksData];
}
return [NSArray arrayWithArray:elements];
}
In my Objective-C code I'm calling super class "+(NSArray*)listOfBooks:(NSArray*)jsonWithBooks" to generate NSArray of objects. But I haven't found an equivalente on Swift. Any of you knows what would be the best alternative to do something like this?
I'm trying to use #Alexander example but my project crash in the following line:
let inventoryBooks = Book.books(fromDictArray: json .object(forKey: "books") as! [[String : String]] )
I check the type for this:
json .object(forKey: "books")
As follow:
let arrayOfBooks = json .object(forKey: "books")
if arrayOfBooks is NSArray {
print("nsarray")
}
if arrayOfBooks is [[String:String]] {
print("string:string")
}
if arrayOfBooks is NSDictionary {
print("NSDic")
}
And is printing nsarray
My question. What I'm doing wrong or do I need to change the signature on this function:
static func books(fromDictArray array: [[String: String]]) -> [Book?] {
return array.map(Book.init)
}
This sample of the json response:
{
books = (
{
caption = "";
"display_sizes" =(
{
name = thumb;
uri = "https://someUrl.com/img.jpg";
}
);
id = 123;
"max_dimensions" = {
height = 4912;
width = 7360;
};
title = "Learning Swift";
author = "Some guy"
}
{
caption = "";
"display_sizes" =(
{
name = thumb;
uri = "https://someUrl.com/img.jpg";
}
);
id = 123;
"max_dimensions" = {
height = 4912;
width = 7360;
};
title = "Swift";
author = "me meme"
}
)
}
Here is how I would write this code in idiomatic Swift:
struct Book {
let title: String
let author: String
let description: String
/* an implicit member wise initializer is generated,
which would otherwise look something like this:
init(title: String, author: String, description: String) {
self.title = title
self.author = author
self.description = description
} */
}
// Initialization from Dictionaries
extension Book {
init?(fromDict dict: [String: Any]) {
guard
let title = dict["bookTitle"] as? String,
let author = dict["author"] as? String,
let description = dict["description"] as? String
else { return nil }
self.init(
title: title,
author: author,
description: description
)
}
static func books(fromDictArray array: [[String: Any]]) -> [Book?] {
return array.map(Book.init)
}
}
Here are some notable points:
Book is a struct. Such a broad description of a book doesn't need to support the notion of identity. I.e., your book named "Harry Potter", by "J.K. Rowling" with the description "Some description" can be considered to be the same as my book with the same values. There's no apparent need (yet) to distinguish the identity of your book vs the identity of mine.
Book has an implicit memberwise initializer init(title:author:description:) which simply initializes its fields to the given parameters.
An extension is made which compartmentalizes all dictionary related tasks into a single unit.
A failable initializer, init?(fromDictArray:) is made, which returns a new book based off the given dict (presumably created from your JSON). This initializer is fault tolerant. If the dict provided is invalid, then the initializer will simply return nil, without crashing your program.
A static method is made on the Book struct, books(fromDictArray:), which will create an array of optional books ([Book?], a.k.a Array<Optional<Book>> out of the given dict. It is then the job of the consumer of this method to deal with the nil values, those resulting from invalid dicts, as they please.
They could ignore the nil books:
let books = Book.books(fromDictArray: myDictArray).flatMap{$0}
They could crash if a nil book is found:
let books = Book.books(fromDictArray: myDictArray) as! [Book]
Or they can handle the nil cases in some unique way:
let books = Book.books(fromDictArray: myDictArray).map{ book in
if book == nil {
print("A nil book was found")
}
}
As already mentioed by Dan, the Swift equivalent would be a class func:
class func listOfBooks(jsonWithBooks: [NSDictionary]) -> [Book] {
var books = [Book]()
for json in jsonWithBooks {
let book = Book(
book: json["bookTitle"]!,
author: json["author"]!,
description: json["description"]!
)
books.append(book)
}
return books
}