I am using the google VR SDK external framework in my swift project.
In my Bridging-Header.h I am importing the framework header
#import "GVRCardboardView.h"
In GVRCardboardView.h there is this:
typedef NS_ENUM(NSInteger, GVRUserEvent) {
kGVRUserEventBackButton,
kGVRUserEventTilt,
kGVRUserEventTrigger,
};
In my swift code
#objc func cardboardView(_ cardboardView: GVRCardboardView!, didFire event: GVRUserEvent) {
switch (event) {
case kGVRUserEventBackButton:
print("User pressed back button");
break;
case GVRUserEvent.kGVRUserEventTilt:
print("User performed tilt action");
break;
case .kGVRUserEventTrigger:
print("User performed trigger action");
break;
}
}
But I get compiler errors. You can see I'm trying three different ways to access the enum values in my switch case, and they all give errors. First,
Use of unresolved identifier 'kGVRUserEventTilt'
When I searched other similar questions online, I saw the solution was to include the name of the enum, like you see in the second case of my switch statement. But that gives me this error:
Type 'GVRUserEvent' has no member 'kGVRUserEventTilt'
And the third case gives this:
Pattern cannot match values of type 'GVRUserEvent'
Anyone know why? It recognizes GVRUserEvent as something -- I don't get errors in the argument line.
Thanks for any help!
Related
Logging VkResult enum values
The VkResult enum contains a lot of values. Unfortunately though, they are C enums which aliases an integer, so I cannot easily just log their names to the console. For this purpose, I am envisioning a function which does something like this:
void graphics::log_vk_result(VkResult result)
{
switch (result)
{
case VK_SUCCESS:
log_core_debug("VK_SUCCESS"); return;
case VK_NOT_READY:
log_core_debug("VK_NOT_READY"); return;
[...]
}
But some of the enum values are only supported by certain extensions, as indicated here. An example: The extension VK_EXT_debug_report introduces the following value to the enumeration: VK_ERROR_VALIDATION_FAILED_EXT. So my idea for (potentially) more portable code would be something like this:
void graphics::log_vk_result(VkResult result)
{
switch (result)
{
[...]
#if defined(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)
case VK_ERROR_VALIDATION_FAILED_EXT:
log_core_debug("VK_ERROR_VALIDATION_FAILED_EXT");
#endif
}
I found this name by looking at the extension manual page. I cannot easily see whether or not VK_EXT_DEBUG_REPORT_EXTENSION_NAME is a macro or an enum - it is a const char* but stated under the section "New Enum Constants". So checking for this particular value, for this particular extension, was my default choice.
[I do realize this extension is marked as deprecated, and I'm not using it! I only use it as an example here.]
I have two questions:
Is this needed?
Is this the correct way of doing this?
Thanks a lot!
All of this is unnecessary, since Vulkan SDK already includes the desired functionality:
#include <vulkan/vk_enum_string_helper.h>
void graphics::log_vk_result( const VkResult result ){
log_core_debug( string_VkResult(result) );
}
I'm trying to learn Swift by converting an existing Objective-C app I wrote (a streaming audio player). However, the libraries I'm using don't have Swift equivalents, so I've created a bridging header and am referencing them successfully.
Now, I'm trying to do a switch on an enum from one of these libraries, but Swift is just complaining.
To make things even more confusing (for me, probably not for any of you), a series of if blocks works fine.
Here is the enum from the Objective-C library:
typedef enum {
kFsAudioStreamRetrievingURL,
kFsAudioStreamStopped,
kFsAudioStreamBuffering,
kFsAudioStreamPlaying,
kFsAudioStreamPaused,
kFsAudioStreamSeeking,
kFSAudioStreamEndOfFile,
kFsAudioStreamFailed,
kFsAudioStreamRetryingStarted,
kFsAudioStreamRetryingSucceeded,
kFsAudioStreamRetryingFailed,
kFsAudioStreamPlaybackCompleted,
kFsAudioStreamUnknownState
} FSAudioStreamState;
This works:
if (state == kFsAudioStreamRetrievingURL) {
}
if (state == kFsAudioStreamStopped) {
}
if (state == kFsAudioStreamBuffering) {
}
But this doesn't work:
switch state {
case kFsAudioStreamRetrievingURL:
case kFsAudioStreamStopped:
case kFsAudioStreamBuffering:
break;
}
The second one gives me the following error:
Binary operator '~=' cannot be applied to two 'FSAudioStreamState' operands
I would appreciate any assistance in helping me learn more Swift!
Try this:
switch state {
case .RetrievingURL:
case .Stopped:
case .Buffering:
...
}
---- EDITED!!! ----
I'm wrong, this is C++ enum. So try this:
switch state.value {
case kFsAudioStreamRetrievingURL.value:
case kFsAudioStreamStopped.value:
case kFsAudioStreamBuffering.value:
...
}
In obj-c
Enum definition should be
typedef NS_ENUM(NSInteger, FSAudioStreamState){
FSAudioStreamStateStopped,
...
}
So when they are imported into Swift
enum FSAudioStreamState{
case Stopped
...
}
I think it's C++ enum (Not Objective-C enum), so swift will not regard it as enum and it would be better to use if - else.
I'm using Swift to make a game in SpriteKit.
In Objective-C I could use the following method:
(void)enumerateChildNodesWithName:(NSString *)name usingBlock:(void (^)(SKNode *node, BOOL *stop))block
to perform actions on that *node, but I can't get this function working in Swift. Basically, I don't know how to reference that node in Swift.
This is the code I'm using, but I'm having trouble with the "usingBlock:" part. I've tried many things for many hours, but have not succeeded. Help please!
func spawnEnemy() -> () {
let enemy = SKSpriteNode(imageNamed: "enemy")
enemy.name = "enemy"
enemy.position = CGPointMake(100, 100)
self.addChild(enemy)
}
func checkCollisions() -> () {
self.enumerateChildNodesWithName("enemy", usingBlock: ((SKNode!, CMutablePointer<ObjCBool>) -> Void)?)
}
For now, don't trust autocomplete to insert the code you need — it drops in signatures from the "header", but a block signature is not the same as the declaration you need when inserting your own closure for a block parameter.
The formal way to write a closure would be to replicate the signature inside braces, adding local parameter names and using the in keyword to mark the start of the closure body:
self.enumerateChildNodesWithName("enemy", usingBlock: {
(node: SKNode!, stop: UnsafeMutablePointer <ObjCBool>) -> Void in
// do something with node or stop
})
But Swift's type inference means you don't have to write that much. Instead, you can just name the parameters, because their type (as well as the closure's return type) is known:
self.enumerateChildNodesWithName("enemy", usingBlock: {
node, stop in
// do something with node or stop
})
You can also use trailing closure syntax:
self.enumerateChildNodesWithName("enemy") {
node, stop in
// do something with node or stop
}
(You can even drop the local parameter names and refer to parameters by position — e.g. $0 for node — but here isn't a great place to do that because it makes your code far less readable. It's best to reserve $0 and friends for closures where it's blindingly obvious what the parameters are, like the closures you use with map and sort.)
See Closures in The Swift Programming Language for further explanation.
Also, because stop is an UnsafeMutablePointer, the syntax for using it is a bit different than in ObjC: set stop.memory = true to break out of enumeration.
After experimenting with a few little Swift programs, I decided my next step was to port a single module in an Objective-C program into Swift to see what steps were required. I had a number of issues, so I thought I'd post my process and results here in case others might find it useful.
I also created a table to help me remember the different conversions. Unfortunately, StackOverflow doesn't support tables, so I posted these conversions as a Github gist here.
Although Apple will undoubtedly provide an Xcode Refactor to convert from Objective-C to Swift, converting one manually is a great way to get familiar with the differences between the two languages. There is so much 'muscle memory' involved in a language you know well, and this is a great way to get familiar with the new syntax. As promised by Apple, it turns out the languages share so many common ideas, that it's mostly a mechanical process (as opposed to porting from, say C++ or even traditional C).
Note that this process uses none of the exciting new features of Swift, it only gets the code straight across. I should mention that moving to Swift will restrict any backwards compatability to iOS 7 or OS X 10.9. I also ran into a couple of issues (with workarounds below) that I'm sure are just due to the first beta release status of the project, so may not be required in future versions.
I chose iPhoneCoreDataRecipes and picked a module that didn’t rely on a lot of others: IngredientDetailViewController. If you'd like to follow along, check out my "answer" below.
Hope this is of use.
0) Download a copy of the project here and open Recipes.xcodeproj in Xcode version 6.
1) Choose File>New File…>iOS Source>Swift File> IngredientDetailViewController (Folder: Classes, Group: Recipe View Controllers)
2) Reply Yes to “Would you like to configure an Objective-C bridging header?”
3) Copy the first three lines below from Recipes_Prefix.pch and the next three from IngredientDetailViewController.m into Recipes-Bridging-Header.h. If you do further files, obviously don't duplicate lines, and remove any files that you've converted to Swift. I haven't found any where that documents the need for the Cocoa lines, given that they're imported in the swift file, but ...
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>
#import "Recipe.h"
#import "Ingredient.h"
#import "EditingTableViewCell.h"
4) Copy/paste the text from both the IngredientDetailViewController.h file and the IngredientDetailViewController.m files into IngredientDetailViewController.swift.
5) Delete both IngredientDetailViewController.h and .m files from project.
6) Do a global Find-and-Replace from #import "IngredientDetailViewController.h" to #import "Recipes-Swift.h" (Only one conversion in this case, and again for further files, don't duplicate this line in your Objective-C modules.)
7) Check the Project>Targets>Recipes>Build Settings Runpath Search Paths. If it shows $(inherited), remove this line or you'll get an error on launch about "no image found"
8) Convert Objective-C syntax in IngredientDetailViewController.swift to Swift. See the GitHub Gist mentioned above the substitutions required, or below for my converted version.
9) You may need to update the IB links. Do a Find>Find in Files on IngredientDetailViewController and select the one in Interface Builder. Open the Identity Inspector in the right-hand column. Select IngredientDetailViewController in the Class field, type xxx or something and tab.
10) Build and Run. Note that after going into a recipe, you must tap Edit and then the info button of an ingredient to activate IngredientDetailViewController
12) Congrats on building your first mixed Swift/Objective-C program!
Here's my cut at this particular module:
``
class IngredientDetailViewController: UITableViewController {
var recipe: Recipe!
var ingredient: Ingredient! {
willSet {
if let newIngredient = newValue {
self.ingredientStr = newIngredient.name
self.amountStr = newIngredient.amount
} else {
self.ingredientStr = ""
self.amountStr = ""
}
}
}
init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!) {
super.init(nibName:nibNameOrNil, bundle: nibBundleOrNil?)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
init(style: UITableViewStyle) {
super.init(style: style)
}
// MARK: table's data source
var ingredientStr: String?
var amountStr: String?
// view tags for each UITextField
let kIngredientFieldTag = 1
let kAmountFieldTag = 2
override func viewDidLoad () {
super.viewDidLoad()
self.title = "Ingredient"
self.tableView.allowsSelection = false
self.tableView.allowsSelectionDuringEditing = false
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
let IngredientsCellIdentifier = "IngredientsCell"
let cell = tableView.dequeueReusableCellWithIdentifier(IngredientsCellIdentifier, forIndexPath: indexPath ) as EditingTableViewCell
if (indexPath.row == 0) {
// cell ingredient name
cell.label.text = "Ingredient"
cell.textField.text = self.ingredientStr
cell.textField.placeholder = "Name"
cell.textField.tag = kIngredientFieldTag
}
else if (indexPath.row == 1) {
// cell ingredient amount
cell.label.text = "Amount"
cell.textField.text = self.amountStr
cell.textField.placeholder = "Amount"
cell.textField.tag = kAmountFieldTag
}
return cell
}
#IBAction func save (sender: AnyObject!) {
if let context = self.recipe.managedObjectContext {
if (!self.ingredient) {
self.ingredient = NSEntityDescription.insertNewObjectForEntityForName("Ingredient",
inManagedObjectContext:context) as Ingredient
self.recipe.addIngredientsObject(self.ingredient)
self.ingredient.displayOrder = self.recipe.ingredients.count
}
// update the ingredient from the values in the text fields
let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow:0, inSection:0)) as EditingTableViewCell
self.ingredient.name = cell.textField.text
// save the managed object context
var error: NSError? = nil
if !context.save( &error) {
/*
Replace this implementation with code to handle the error appropriately.
abort() causes the application to generate a crash log and terminate.
You should not use this function in a shipping application, although it may be
useful during development. If it is not possible to recover from the error, display
an alert panel that instructs the user to quit the application by pressing the Home button.
*/
println("Unresolved error \(error), \(error!.userInfo)")
abort()
}
}
// if there isn't an ingredient object, create and configure one
self.parentViewController.dismissViewControllerAnimated(true, completion:nil)
}
#IBAction func cancel(sender: AnyObject!) {
self.parentViewController.dismissViewControllerAnimated(true, completion:nil)
}
func textFieldDidEndEditing(textField:UITextField) {
// editing has ended in one of our text fields, assign it's text to the right
// ivar based on the view tag
//
switch (textField.tag)
{
case kIngredientFieldTag:
self.ingredientStr = textField.text
case kAmountFieldTag:
self.amountStr = textField.text
default:
break
}
}
}
I'm putting together a Mac OS X Application and I'm trying to register to receive Display Reconfiguration notices, but I'm very lost right now. I've been reading Apple's documentation and some forums posts, etc., but everything seems to assume a better knowledge of things than I apparently possess. I understand that I have to request the callback inside a run loop for it to work properly. I don't know how to set up a basic run loop for it, though. I also feel like the example Apple has in their documentation is missing stuff they are expecting me to already know. To display my ignorance here is what I feel like things should look like.
NSRunLoop *rLoop = [NSRunLoop currentRunLoop];
codeToStartRunLoop
void MyDisplayReconfigurationCallBack (
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo);
{
if (flags & kCGDisplayAddFlag) {
NSLog (#"Display Added");
}
else if (kCGDisplayRemoveFlag) {
NSLog (#"Display Removed");
}
}
CGDisplayRegisterReconfigurationCallback(MyDisplayReconfigurationCallBack, NULL);
The actual code I got was from Apple's Example, but it tells me that flags is an undeclared identifier at this point and won't compile. Not that it would work right since I don't have it in a run loop. I was hoping to find a tutorial somewhere that explains registering for system callback in a run loop but have not been successful. If anyone could point me in the right direction I'd super appreciate it.
(I'm sure that you'll be able to tell from my question that I'm very green. I taught myself Objective-C out of a book as my first programming language. I skipped C, so every once in a while I hit a snag somewhere that I can't figure out.)
If you're writing a Mac OS X application, the AppKit has already set up a run loop for you, so you don't need to worry about that part. You really only need to create your own run loop in Cocoa when you are also creating your own thread.
For the "undeclared identifier" part, it looks like it's due to a typo/syntax mistake:
void MyDisplayReconfigurationCallBack (CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo);
// Semicolon makes this an invalid function definition^^
{
// This is an anonymous block,* and flags wasn't declared in it
if (flags & kCGDisplayAddFlag) {
// etc.
}
Also, unlike some other languages, you can't declare or define functions inside of other functions, methods, or blocks* -- they have to be at the top level of the file. You can't put this in the same place where you call CGDisplayRegisterReconfigurationCallback.
Just as an sample (I have no idea what the rest of your code really looks like):
// MyClassThatIsInterestedInDisplayConfiguration.m
#import "MyClassThatIsInterestedInDisplayConfiguration.h"
// Define callback function at top level of file
void MyDisplayReconfigurationCallBack (
CGDirectDisplayID display,
CGDisplayChangeSummaryFlags flags,
void *userInfo)
{
if (flags & kCGDisplayAddFlag) {
NSLog (#"Display Added");
}
else if (kCGDisplayRemoveFlag) {
NSLog (#"Display Removed");
}
}
#implementation MyClassThatIsInterestedInDisplayConfiguration
- (void) comeOnBabyAndDoTheRegistrationWithMe {
// Register callback function inside a method
CGDisplayRegisterReconfigurationCallback(MyDisplayReconfigurationCallBack,
NULL);
}
#end
*The basic C curly-brace-delimited thing, not the new cool Obj-C ad hoc function thing.