Now that we've reached Swift 2.0, I've decided to convert my, as yet unfinished, OS X app to Swift. Making progress but I've run into some issues with using termios and could use some clarification and advice.
The termios struct is treated as a struct in Swift, no surprise there, but what is surprising is that the array of control characters in the struct is now a tuple. I was expecting it to just be an array. As you might imagine it took me a while to figure this out. Working in a Playground if I do:
var settings:termios = termios()
print(settings)
then I get the correct details printed for the struct.
In Obj-C to set the control characters you would use, say,
cfmakeraw(&settings);
settings.c_cc[VMIN] = 1;
where VMIN is a #define equal to 16 in termios.h. In Swift I have to do
cfmakeraw(&settings)
settings.c_cc.16 = 1
which works, but is a bit more opaque. I would prefer to use something along the lines of
settings.c_cc.vim = 1
instead, but can't seem to find any documentation describing the Swift "version" of termios. Does anyone know if the tuple has pre-assigned names for it's elements, or if not, is there a way to assign names after the fact? Should I just create my own tuple with named elements and then assign it to settings.c_cc?
Interestingly, despite the fact that pre-processor directives are not supposed to work in Swift, if I do
print(VMIN)
print(VTIME)
then the correct values are printed and no compiler errors are produced. I'd be interested in any clarification or comments on that. Is it a bug?
The remaining issues have to do with further configuration of the termios.
The definition of cfsetspeed is given as
func cfsetspeed(_: UnsafeMutablePointer<termios>, _: speed_t) -> Int32
and speed_t is typedef'ed as an unsigned long. In Obj-C we'd do
cfsetspeed(&settings, B38400);
but since B38400 is a #define in termios.h we can no longer do that. Has Apple set up replacement global constants for things like this in Swift, and if so, can anyone tell me where they are documented. The alternative seems to be to just plug in the raw values and lose readability, or to create my own versions of the constants previously defined in termios.h. I'm happy to go that route if there isn't a better choice.
Let's start with your second problem, which is easier to solve.
B38400 is available in Swift, it just has the wrong type.
So you have to convert it explicitly:
var settings = termios()
cfsetspeed(&settings, speed_t(B38400))
Your first problem has no "nice" solution that I know of.
Fixed sized arrays are imported to Swift as tuples, and – as far as I know – you cannot address a tuple element with a variable.
However,Swift preserves the memory layout of structures imported from C, as
confirmed by Apple engineer Joe Groff:. Therefore you can take the address of the tuple and “rebind” it to a pointer to the element type:
var settings = termios()
withUnsafeMutablePointer(to: &settings.c_cc) { (tuplePtr) -> Void in
tuplePtr.withMemoryRebound(to: cc_t.self, capacity: MemoryLayout.size(ofValue: settings.c_cc)) {
$0[Int(VMIN)] = 1
}
}
(Code updated for Swift 4+.)
Related
I'm struggling to figure out a reason for this behavior, or maybe this is suppose to happen and I just wasn't aware.
For background, I'm using proto3, and am doing this in Go1.15, and I do know that packed is the default in proto3, and I'm relatively new to protobufs.
I defined the following message in a proto file:
message Response {
repeated uint32 points = 1 [packed=true];
}
Which will generate the following code using protoc-gen-go v1.25.0.
type Response struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Points []uint32 `protobuf:"varint,3,rep,packed,name=points,json=points,proto3" json:"points,omitempty"`
}
I go to use the new struct, and it doesn't behave like I would normally expect a struct to behave. Here's some things I wrote, along with what was printed out.
newResponse := pb.Response{Points: []uint32{2,4,6,8}}
fmt.Println(newResponse)
//{{{} [] [] <nil>} 0 [] [2 4 6 8] --> I expect this
refToNewResponse := &newResponse
fmt.Println(refToNewResponse)
// points:2 points:4 points:6 points:8 --> not what I expected
Now you might be thinking, it's just formatting big deal.
But I expect a list... not numbers that each individually have a label. I've seen and used other protobufs... and when I see the response that they return, it doesn't look like this, it's one label to a list like:
points: [2 4 6 8]
I do need to use the reference version of this because I eventually want to expand and use a list of Responses which the generated code will spit out a slice of pointer Responses, but I can't understand why it's separating and labeling each element in the slice.
I'm hoping someone can point out something I'm doing or not doing that is causing this... thank you in advance.
This is indeed just formatting. Nothing has changed in the underlying data structure. You requested a repeated uint32 Points and it's literally printing them that way.
The marshaler in the protobuf implementation can really output whatever it likes, there is no reference version of the human-readable representation of a protobuf.
If you really must have a custom format for the .String() output, you can try a different proto library such as gogoprotobuf, or try various extensions. But ultimately, it's just human-readable output.
Note:
this has nothing to do with packed=true (which is indeed the default).
if you're confused about printing the pointer vs the basic type, it's because the String() method has a pointer receiver. See this question
Apple has changed the Swift reflection in XCode 7 beta 5. The global reflect() function is gone, and you'll have to do this:
let mirror = Mirror(reflecting: object)
It gives more or less the same information in a nicer way (no more .1 og .2 for propertyname and value). But I can't find a way to explore if the mirrored item is an instance of a class.
The older implementation you could check the following:
reflectedProperty.1.objectIdentifier != nil || reflectedProperty.1.count > 0
But objectIdentifier seems to be gone and the count is always 2 regardless of type.
Help anyone?
Ok so I found a workaround. I was iterating over mirror.children.enumerate() which seemed to make all the properties of type String. Instead I dug into Apple preliminary documentation, and read that it could be a good idea to "upgrade" children to e.g. AnyRandomAccessCollection. Which made it possible to rely on the count of an objects children to determine if it's an object (after testing whether it's an array)
Currently I have a functioning Swift class to JSON Serializer working here on this gist if you are interested in the code:
https://gist.github.com/peheje/cc3618253d4f38ea4885
I am not sure if you are looking for this, but the output for the following is "Class"
mirror.displayStyle
I'd like to programmatically set the width of some NSTableColumns in code (so that I can restore the widths on startup), but I don't really know how to apply what's written in the docs
for column in table.tableColumns {
var w: CGFloat = 125
column.setWidth(w)
println("\(column.identifier!)") // this prints my identifiers, so I know these are my columns and not something else I'm not interested in
}
The error I get is as follows: '(#lvalue CGFloat) -> $T3' is not identical to 'CGFloat'
With just 125 as the argument to setWidth the error says '(IntegerLiteralConvertable) -> etc...'
Code completion in XCode shows four versions of setWidth() each of which take at least two arguments, and none with just the width which is all I care about. My guess is that the docs don't match XCode 6.1.1, perhaps? It suggests there's just a setWidth() method, but in real life I have to choose between four equally confusing versions.
One Quincey_Morris gave me this answer in the Apple developer forums (I hope this isn't a breach of Apple's terms and conditions). I had to cast column "as [NSTableColumn]" before the opening brace of my for loop before I could just call "column.width = 125".
According to my understanding of the documentation, this should be correct:
var cookies: [NSHTTPCookie] = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies as [NSHTTPCookie]
where I'm creating an array of NSHTTPCookie objects. The interpreter does not like this syntax, however, giving me "Expected type after 'as'" and putting a little pointer at the opening bracket of the [NSHTTPCookie] at the end.
However, this works:
var cookies:NSHTTPCookie[] = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies as NSHTTPCookie[]
From the documentation, it seems like the first version is more correct, however.
Here's another example, this time with someone else's code. No one else using this code has reported the same behavior I get. (This is just a snippet; if the context is relevant let me know and I'll post more)
func asDict(x: AnyObject) -> [String:AnyObject]? {
return x as? [String:AnyObject]
}
In this case the playground interpreter objects in both places [String:AnyObject] is used. It just doesn't seem to be recognizing it as a type.
I double-checked to make sure I have the most recent beta of Xcode 6, but it seems much more likely to me that the problem is in my understanding rather than in the tool, since this would be a mighty big bug for only me to experience.
You must be using an old beta, this works in Beta 5 playground:
import Foundation
println("hello")
var cookies:[NSHTTPCookie] = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies as [NSHTTPCookie]
println("goodbye")
The key UIKeyboardAnimationCurveUserInfoKey in the userInfo dictionary of a UIKeyboardWillShowNotification contains an Int with the value 7.
Now I need to pass this Int into UIView.setAnimationCurve(<here>). I tried to create the required UIViewAnimationCurve enum like this UIViewAnimationCurve(rawValue: 7). Because the raw value 7 is undocumented, the result is always nil.
It works fine this way in Objective-C. Any idea how to get this animation curve from the notification into a UIView animation using Swift?
Update:
As pointed out by Martin, this is no longer a problem since Xcode 6.3.
From the Xcode 6.3 Release Notes:
Imported NS_ENUM types with undocumented values, such as UIViewAnimationCurve, can now be converted from their raw integer values using the init(rawValue:) initializer without being reset to nil. Code that used unsafeBitCast as a workaround for this issue can be written to use the raw value initializer.
I think I figured it out, but I'm not sure if this is the way it's supposed to be done.
let animationCurve = unsafeBitCast(7, UIViewAnimationCurve.self)
UIView.setAnimationCurve(animationCurve)
Update: The solution contained in this question works as well.
var animationCurve = UIViewAnimationCurve.EaseInOut
NSNumber(integer: 7).getValue(&animationCurve)