How can I use the Objective-C code below in Swift, I tried but something is wrong.
Objective-C:
NSUInteger index = [theArray indexOfObjectPassingTest:
^BOOL(NSDictionary *dict, NSUInteger idx, BOOL *stop)
{
return [[dict objectForKey:#"name"] isEqual:theValue];
}
];
Swift (Doesn't work):
let index = theArray.indexOfObjectPassingTest { (var dict: NSDictionary, var ind: Int, var bool: Bool) -> Bool in
return dict.objectForKey("name")?.isEqual("theValue")
}
I played with it and got this to work:
let theArray: NSArray = [["name": "theName"], ["name": "theStreet"], ["name": "theValue"]]
let index = theArray.indexOfObjectPassingTest { (dict, ind, bool) in return dict["name"] as? String == "theValue" }
if index == NSNotFound {
print("not found")
} else {
print(index) // prints "2"
}
This can be further reduced. As #newacct mentioned in the comment, the return can be dropped since the closure is only a single line. Also, _ can be used in place of the parameters that aren't being used:
let index = theArray.indexOfObjectPassingTest { (dict, _, _) in dict["name"] as? String == "theValue" }
You can get rid of the parameter list in the closure entirely and use the default $0 value. Note in that case, the three parameters are combined as a tuple, so the first value of the tuple dict is referenced as $0.0:
let index = theArray.indexOfObjectPassingTest { $0.0["name"] as? String == "theValue" }
Swift 3:
Consider this method:
public func index(where predicate: (Element) throws -> Bool) rethrows -> Int?
The following gives you an example how to use it:
let dict1 = ["name": "Foo"]
let dict2 = ["name": "Doh"]
let array = [dict1, dict2]
let index = array.index { (dictionary) -> Bool in
return dictionary["name"] == "Doh"
}
This returns the value 1.
Hope that helps
Guess you need this:
var index: UInt = theArray.indexOfObjectPassingTest({(dict: [NSObject: AnyObject], idx: UInt, stop: Bool) -> BOOL in return dict.objectForKey("name").isEqual(theValue)
})
I ended up using this for a Swift5 project:
let index = self.info.indexOfObject(passingTest: { (obj:Any, ind:Int, stop:UnsafeMutablePointer<ObjCBool>) -> Bool in
if let details = obj as? NSDictionary, let id = details["id"] as? Int
{
if (orderId == id)
{
stop.pointee = true
return true
}
}
return false;
})
Where orderId is id value of the object I wanted to find.
Related
I trying to make a SwiftUI app where after entering one letter in a TextField the cursor automatically moves to the next TextField. The UI is pretty much like this.
In Swift/IB, it looks like this was done with delegates and adding a target like in this post:
How to move to the next UITextField automatically in Swift
But can't find any documentation for using delegates/targets in SwiftUI.
I tried following this post:
SwiftUI TextField max length
But this has not worked for me. Setting the .prefix(1) does not seem to make a difference. The TextField still accepts any amount of characters and when moved to the next TextField does not reduce the characters entered to only the first character.
In SwiftUI's current state, is it possible to automatically move to the next TextField after 1 character is entered?
Thanks for any help!
It can be done in iOS 15 with FocusState
import SwiftUI
///Sample usage
#available(iOS 15.0, *)
struct PinParentView: View {
#State var pin: Int = 12356
var body: some View {
VStack{
Text(pin.description)
PinView(pin: $pin)
}
}
}
#available(iOS 15.0, *)
struct PinView: View {
#Binding var pin: Int
#State var pinDict: [UniqueCharacter] = []
#FocusState private var focusedField: UniqueCharacter?
var body: some View{
HStack{
ForEach($pinDict, id: \.id, content: { $char in
TextField("pin digit", text:
Binding(get: {
char.char.description
}, set: { newValue in
let newest: Character = newValue.last ?? "0"
//This check is only needed if you only want numbers
if Int(newest.description) != nil{
char.char = newest
}
//Set the new focus
DispatchQueue.main.async {
setFocus()
}
})
).textFieldStyle(.roundedBorder)
.focused($focusedField, equals: char)
})
}.onAppear(perform: {
//Set the initial value of the text fields
//By using unique characters you can keep the order
pinDict = pin.description.uniqueCharacters()
})
}
func setFocus(){
//Default to the first box when focus is not set or the user reaches the last box
if focusedField == nil || focusedField == pinDict.last{
focusedField = pinDict.first
}else{
//find the index of the current character
let idx = pinDict.firstIndex(of: focusedField!)
//Another safety check for the index
if idx == nil || pinDict.last == pinDict[idx!]{
focusedField = pinDict.first
}else{
focusedField = pinDict[idx! + 1]
}
}
//Update the Binding that came from the parent
setPinBinding()
}
///Updates the binding from the parent
func setPinBinding(){
var newPinInt = 0
for n in pinDict{
if n == pinDict.first{
newPinInt = Int(n.char.description) ?? 0
}else{
newPinInt = Int(String(newPinInt) + n.char.description) ?? 0
}
}
pin = newPinInt
}
}
//Convert String to Unique characers
extension String{
func uniqueCharacters() -> [UniqueCharacter]{
let array: [Character] = Array(self)
return array.uniqueCharacters()
}
func numberOnly() -> String {
self.trimmingCharacters(in: CharacterSet(charactersIn: "-0123456789.").inverted)
}
}
extension Array where Element == Character {
func uniqueCharacters() -> [UniqueCharacter]{
var array: [UniqueCharacter] = []
for char in self{
array.append(UniqueCharacter(char: char))
}
return array
}
}
//String/Characters can be repeating so yu have to make them a unique value
struct UniqueCharacter: Identifiable, Equatable, Hashable{
var char: Character
var id: UUID = UUID()
}
#available(iOS 15.0, *)
struct PinView_Previews: PreviewProvider {
static var previews: some View {
PinParentView()
}
}
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
}
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
}
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
}
How do I stop a block enumeration?
myArray.enumerateObjectsUsingBlock( { object, index, stop in
//how do I stop the enumeration in here??
})
I know in obj-c you do this:
[myArray enumerateObjectsUsingBlock:^(id *myObject, NSUInteger idx, BOOL *stop) {
*stop = YES;
}];
In Swift 1:
stop.withUnsafePointer { p in p.memory = true }
In Swift 2:
stop.memory = true
In Swift 3 - 4:
stop.pointee = true
This has unfortunately changed every major version of Swift. Here's a breakdown:
Swift 1
stop.withUnsafePointer { p in p.memory = true }
Swift 2
stop.memory = true
Swift 3
stop.pointee = true
since XCode6 Beta4, the following way can be used instead:
let array: NSArray = // the array with some elements...
array.enumerateObjectsUsingBlock( { (object: AnyObject!, idx: Int, stop: UnsafePointer<ObjCBool>) -> Void in
// do something with the current element...
var shouldStop: ObjCBool = // true or false ...
stop.initialize(shouldStop)
})
The accepted answer is correct but will work for NSArrays only. Not for the Swift datatype Array. If you like you can recreate it with an extension.
extension Array{
func enumerateObjectsUsingBlock(enumerator:(obj:Any, idx:Int, inout stop:Bool)->Void){
for (i,v) in enumerate(self){
var stop:Bool = false
enumerator(obj: v, idx: i, stop: &stop)
if stop{
break
}
}
}
}
call it like
[1,2,3,4,5].enumerateObjectsUsingBlock({
obj, idx, stop in
let x = (obj as Int) * (obj as Int)
println("\(x)")
if obj as Int == 3{
stop = true
}
})
or for function with a block as the last parameter you can do
[1,2,3,4,5].enumerateObjectsUsingBlock(){
obj, idx, stop in
let x = (obj as Int) * (obj as Int)
println("\(x)")
if obj as Int == 3{
stop = true
}
}
Just stop = true
Since stop is declared as inout, swift will take care of mapping the indirection for you.