Xcode Enum woes - objective-c

I thought I had this sorted, but I am still missing something.
Very simply, I have a Settings class that hold a DAO (sitting on a plist). I want to have a couple of enums for the settings for convenience and readability, such as GamePlayType and DifficultyLevel. Right now I am defining them in the Settings.h file above the #interface line as such:
typedef enum {
EASY,
NORMAL,
HARD
} DifficultyLevel;
and
typedef enum {
SET_NUMBER_OF_MOVES,
TO_COMPLETION
} GamePlayType;
If I access them from within the Settings class like:
- (int)gridSizeForLOD {
switch ([self difficultyLevel]) {
case EASY:
return GRID_SIZE_EASY;
case NORMAL:
return GRID_SIZE_NORMAL;
case HARD:
return GRID_SIZE_HARD;
default:
return GRID_SIZE_NORMAL;
}
}
everything is fine.
But, if I try to access them outside of the Settings class, let's say in my main view controller class, like this:
if (([settings gameType] == SET_NUMBER_OF_MOVES) && (numMoves == [settings numMovesForLOD])) {
[self showLoseScreen];
}
I get errors (like EXC_BAD_ACCESS) or the condition always fails. Am I doing something incorrectly?
Also, I should point out that I have this code for the call to gameType (which lives in the Settings class):
- (GamePlayType)gameType {
return [dao gameType];
}
and the DAO implements gameType like this:
- (int)gameType {
return (settingsContent != nil) ? [[settingsContent objectForKey:#"Game Type"] intValue] : 0;
}
I know I have the DAO returning an int instead of a GamePlayType, but A) the problem I am describing arose there when I tried to use the "proper" data type, and B) I did not think it would matter since the enum is just a bunch of named ints, right?
Any help, greatly appreciated. I really want to understand this thoroughly, and something is eluding me...
Cheers,
Chris

If you don't have it there already you'll need an import like below in your other class
#import "Settings.h"

Related

Swift 4 / Objective-C instrospection

I have a weird situation with unit tests that would be easily solved by a bit of an hack, and would like to know if this is easily doable as a temporary fix before tackling a major rewrite.
import Foundation
class Foo : NSObject {
func bar() -> Void {
// third-party code
}
}
Is there any way that given Foo.bar would return the string "-[Foo bar]"
Edit: I didn't explain my situation well, sorry. I'm looking for something like a function:
MyStringifyingFunction(Foo.bar) // returns "-[Foo bar]"
This will be used to compare with name of a XCTest instance which is populated with the string "-[MyTestClass testMethodOne]" for example.

How do I access private instance variables in Obj-C or Swift?

I'm trying to get access to MKMapSnapshotter's private instance variables _lodpiSnapshotCreator and _hidpiSnapshotCreator in Swift on macOS.
Thanks to class-dump, I know they're there (see here):
#interface MKMapSnapshotter : NSObject
{
[...]
VKMapSnapshotCreator *_lodpiSnapshotCreator;
VKMapSnapshotCreator *_hidpiSnapshotCreator;
}
but no matter what I do, I can't get ahold of them.
This is how I checked whether I could access them or not:
let snapshotter = MKMapSnapshotter(options: snapshotOptions)
let varNames = ["_lodpiSnapshotCreator", "_hidpiSnapshotCreator"]
for varName in varNames {
if let testIVar = class_getInstanceVariable(MKMapSnapshotter.self, varName) {
if let test = object_getIvar(snapshotter, testIVar) as? VKMapSnapshotCreator {
print(test)
} else {
print("Got ivar, but \(varName) is still nil (getInstanceVariable)")
}
} else {
print("\(varName) is nil (getInstanceVariable)")
}
}
Curiously, class_getInstanceVariable doesn't return nil, but object_getIvar does.
Got ivar, but _lodpiSnapshotCreator is still nil (getInstanceVariable)
Got ivar, but _hidpiSnapshotCreator is still nil (getInstanceVariable)
I'm at my wit's end here. All I can find via Google is people recommending the use of class_getInstanceVariable (which I already use) and key-value-coding (which doesn't work at all).
This must have been done before. Can anyone help me out?
Edit: So far, I have tried this:
#interface MKMapSnapshotter() {
#public VKMapSnapshotCreator *_lodpiSnapshotCreator;
#public VKMapSnapshotCreator *_hidpiSnapshotCreator;
}
#end
That compiles successfully, but when trying to use it, Swift keeps insisting that the members _lodpiSnapshotCreator and _hidpiSnapshotCreator don't exist.
Since we don't have or control the source code we can't change the variables to properties. Just tried this works for your case:
extension MKMapSnapshotter {
func getPrivateVariable() -> String? {
return value(forKey: "_lodpiSnapshotCreator") as? String
}
open override func value(forUndefinedKey key: String) -> Any? {
if key == "_lodpiSnapshotCreator" {
return nil
}
return super.value(forUndefinedKey: key)
}
}
You can find more about this here.
If this does not work then I believe that there is no way to access Objective-C instance variables from Swift. Only Objective-C properties get mapped to Swift properties.
Hope this helps!!

Creating an enum in Swift, exportable to ObjC, based on another enum: String

I have an enum Foo: String in Swift (therefore not exportable to Objective-C) and I'm trying to create another enum FooObjc in Swift to kinda "wrap" the existent one so that it is 1) available to use in Objective-C and 2) convertable back and forth (Foo <=> FooObjc). The original Foo enum is part of a framework that I don't want to modify. Of course, it's very easy to do what I want if I use a class instead, like this:
#objc public class FooObjc: NSObject {
public private(set) var foo: Foo
#objc public var string: String {
return foo.rawValue
}
#objc public init?(string: NSString) {
guard let foo = Foo(rawValue: string as String) else {
return nil
}
self.foo = foo
}
internal init(foo: Foo) {
self.foo = foo
}
}
(PS: not able to inherit from NSString because the Swift compiler still doesn't accept creating initializers for that class)
Ok, but I definitely don't like this approach because then my Objective-C code will be string-typed. I really want to use an enum instead because after all, that's what it is. This is the least bad working version I could get:
#objc public enum FooObjc: Int, RawRepresentable {
case undefined = -1
case bar
case baz
// omitting many more cases
internal init?(_ foo: Foo?) {
if let foo = foo {
self = fooMapping.filter { $1 == foo }.map { $0.key }.first!
} else {
self = .undefined
}
}
// MARK: - RawRepresentable
public typealias RawValue = String
public init?(rawValue: RawValue) {
let filter = fooMapping.filter { $1?.rawValue == rawValue }
guard filter.count > 0 else {
return nil
}
self = filter.map { $0.key }.first!
}
public var rawValue: RawValue {
switch (self) {
case .undefined: return "undefined"
case .bar: return Foo.bar.rawValue
case .baz: return Foo.baz.rawValue
// omitting many more cases
}
}
}
private let fooMapping: [FooObjc: Foo?] = [
.undefined : nil,
.bar : .bar,
.baz : .baz
// omitting many more cases
]
Notice that:
the fooMapping is useful to avoid one switch-case for each initializer
this undefined case was necessary because in Swift you can have optional enum properties, in Objective-C you can't, so this case will directly map to a Foo? which value is nil.
What worries me here is that I had to write the same cases from the original Foo three times... I'm completely satisfied if I repeat it only twice, but I wasn't able to use the fooMapping in the rawValue property because then I get a cycle between these two.
Note: I'm not sure if this is relevant to the question, but in the original enum, some of the cases have special String attributions, e.g. we have simply case bar but we also have case baz = "something".
So, the question is: does anyone have suggestions to improve this approach or even suggest something completely new that would avoid so much code repetition?
Thank you very much!
What worries me here is that I had to write the same cases from the original Foo three times
Consider an array ["bar", "baz" ...]. By looking at the index of a string in this array and making the necessary adjustments, you can translate from a string to an integer (and thence, via raw value, to a case). By indexing into the array and making the necessary adjustments, you can translate from an integer to a string (and thence, via raw value, to a case). So you will only have to write out the string values once. This eliminates two of your repetitions.
You will still have to write out the case names in the enum declaration, because there's no other way to tell Objective-C the names of the cases. The way to eliminate that repetition, obviously, is to be willing to change the implementation of Foo itself so that it becomes Objective-C-compatible. But you have stated up front that you refuse to do that, so now you must pay the price.

Can I add methods to an Objective-C enum?

In Java, I have an enum like:
public enum Toppings {
PEPPERONI,
EXTRA_CHEESE,
SECRET_SAUCE;
#Override
public String toString() {
switch(this) {
case EXTRA_CHEESE: return "Extra Cheese";
case SECRET_SAUCE: return "Secret Sauceā„¢";
}
String name = name();
return name.charAt(0) + name.substring(1, name.length()).replace('_', ' ').toLowerCase();
}
}
I want to re-made this in Objective-C. So far, I've done this:
NS_ENUM(NSInteger, Toppings) {
PEPPERONI,
EXTRA_CHEESE,
SECRET_SAUCE
};
And then I was stumped. How would I make the toString() method? I know it's rather complex and uses some Java-specific behaviors, but I'm sure there's a way.
The only thing that comes to mind is to have a separate helper class with this functionality, but that seems a bit much, doesn't it?
Unfortunately, there is no way to add methods to an Objective-C enum. (Sidenote: you can add methods to a Swift enum.)
Traditionally, a standalone function would be used for this purpose, with a body similar to your Java method:
NSString* NSStringFromToppings(Toppings toppings)
{
switch (toppings)
{
case PEPPERONI: return #"Pepperoni";
case EXTRA_CHEESE: return #"Extra Cheese";
case SECRET_SAUCE: return #"Secret Sauce";
}
}
(Sidenote: you should name your enum Topping instead of Toppings--you can see how the code above would be clearer with a singular type name. You should also add a two- or three-letter prefix to all your type names (and this function) to avoid naming collisions.)
NSString * const ToppingsList[] = {
[PEPPERONI] = #"Pepperoni",
[EXTRA_CHEESE] = #"Extra Cheese",
[SECRET_SAUCE] = #"Secret Sauce",
};
NSLog("Topping: %#", ToppingList[PEPPERONI]);
After declaring your enum, you can add this to use type string. It seems like toString() method
EDIT: Meanwhile #andyvn22 is right. There is no way to add methods to enums in Objective-C. I just gave a solution for using enums with string.
Yeah, it's not as straightforward as in, say, Java or .NET. However, I think that option 2 of yar1vn's answer looks ok:
Convert objective-c typedef to its string equivalent
You could also add enum serialization as an NSString extension, making it possible to ask NSString to give you a string based on your enum.
No, there is no way to declare a method in an enum using Objective-C.
However, you can use an enum as a parameter to any method. This might be a solution for you:
typedef NS_ENUM(int, PatientActivity)
{
Exercise = 101,
Smoke,
Alcohol
};
- (void)getPatientDetail:(NSString *)PatID withActivity:(enum PatientActivity) activity;

Best design for lookup-and-possibly-change method

I am designing a class that stores (caches) a set of data. I want to lookup a value, if the class contains the value then use it and modify a property of the class. I am concerned about the design of the public interface.
Here is how the class is going to be used:
ClassItem *pClassItem = myClass.Lookup(value);
if (pClassItem)
{ // item is found in class so modify and use it
pClassItem->SetAttribute(something);
... // use myClass
}
else
{ // value doesn't exist in the class so add it
myClass.Add(value, something);
}
However I don't want to have to expose ClassItem to this client (ClassItem is an implementation detail of MyClass).
To get round that the following could be considered:
bool found = myClass.Lookup(value);
if (found)
{ // item is found in class so modify and use it
myClass.ModifyAttribute(value, something);
... // use myClass
}
else
{ // value doesn't exist in the class so add it
myClass.Add(value, something);
}
However this is inefficient as Modify will have to do the lookup again. This would suggest a lookupAndModify type of method:
bool found = myClass.LookupAndModify(value, something);
if (found)
{ // item is found in class
... // use myClass
}
else
{ // value doesn't exist in the class so add it
myClass.Add(value, something);
}
But rolling LookupAndModify into one method seems like very poor design. It also only modifies if value is found and so the name is not only cumbersome but misleading as well.
Is there another better design that gets round this issue? Any design patterns for this (I couldn't find anything through google)?
Actually std::set<>::insert() does precisely this. If the value exists, it returns the iterator pointing to the existing item. Otherwise, the iterator where the insertion was made is returned.
It is likely that you are using a similar data structure for fast lookups anyway, so a clean public interface (calling site) will be:
myClass.SetAttribute(value, something)
which always does the right thing. MyClass handles the internal plumbing and clients don't worry about whether the value exists.
Two things.
The first solution is close.
Don't however, return ClassItem *. Return an "opaque object". An integer index or other hash code that's opaque (meaningless) to the client, but usable by the myClass instance.
Then lookup returns an index, which modify can subsequently use.
void *index = myClass.lookup( value );
if( index ) {
myClass.modify( index, value );
}
else {
myClass.add( value );
}
After writing the "primitive" Lookup, Modify and Add, then write your own composite operations built around these primitives.
Write a LookupAndModify, TryModify, AddIfNotExists and other methods built from your lower-level pieces.
This assumes that you're setting value to the same "something" in both the Modify and Add cases:
if (!myClass.AddIfNotExists(value, something)) {
// use myClass
}
Otherwise:
if (myClass.TryModify(value, something)) {
// use myClass
} else {
myClass.Add(value, otherSomething);
}