How to autocomplete enum switch using Xcode? - ios

Let say you have an enum:
typedef enum {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
} DayOfWeek;
when writing a switch statement, a Xcode is trying to help with switch statement snippet:
and then:
great, but I need to list all enums:
DayOfWeek day = ...; // a day of week
switch (day) {
case Sunday:
break;
case Monday:
break;
case Tuesday:
break;
case Wednesday:
break;
case Thursday:
break;
case Friday:
break;
case Saturday:
break;
default:
break;
}
Unfortunately, have to do it manually :( Is there any known snippet to populate all cases?
Of course I saw some answers 3 years old states that it is impossible, but maybe something changed since that time? Any ideas?

Short Answer :
Use Editor > Refactor > Add Missing Switch Cases
Convenient way :
Open Preferences by pressing [command] + [,] in Xcode.
Go to Key Bindings tab and set a shortcut for the command Refactor > Add Missing Switch Cases.
Input the shortcut in editor. Be sure the cursor is on switch letters.

Saw your question and decided to build an Xcode plugin that does exactly that.
Check it out: https://github.com/stefanceriu/SCXcodeSwitchExpander

in Xcode 10 do the following
add switch...case template and set your enum type instead of self so that xcode understands what it should autocomplete
move cursor inside word switch and go to menu > editor > refactor > expand switch cases
This is what you will get as result

Unfortunately this is not possible with Xcode out of the box. File a bug report and select the request an enhancement option.
https://bugreport.apple.com
I actually did this myself lately, and they gauge the severity and importance of bugs/enhancements by the number of duplicates they receive for a given issue - hence one of the reasons for not being able to search the tracker.

Just hit "Fix" button and the cases will appear:

While Xcode won't autocomplete the entire switch statement, there are tricks to speeding up writing them. This is what I've been doing lately:
Start the switch autocomplete, and type the expression
Copy the case block and paste it enough times to cover all the enum constants (actually, I just paste more than I need and delete later, rather than counting)
Move the cursor to before the first constant placeholder
Press TAB, then the first letter of the constant, then Enter to accept the first autocomplete suggestion
Repeat TAB, letter, ENTER for the rest. Autocomplete won't suggest constants you've already used in the switch, so the list narrows with each use.
Of course, it really helps if all the constants start with the same letter. In your case, consider naming them:
typedef enum {
dayOfWeekSunday,
dayOfWeekMonday,
dayOfWeekTuesday,
dayOfWeekWednesday,
dayOfWeekThursday,
dayOfWeekFriday,
dayOfWeekSaturday
} enumDayOfWeek;

Hey I made an Xcode 8 Source Editor Extension for that in Swift.
https://github.com/timaktimak/SwitchCaseGenerator
Hope it is helpful!

It's not using Xcode, but you can very quickly burp out processed snippets with any scripting language. Throw it as a plugin in your favourite text editor too (e.g Textmate).
I have a ~/bin folder which i place helpers like this and chmod +x them so i can use anytime like this:
enumify.pl ~/tmp.txt
or even better use the command line pasteboard tools:
pbpaste | enumify.pl
#! /usr/bin/perl
#enumify.pl
#reads from STDIN or use filename as first arg
#on OS X combine with pbpaste e.g pbpaste | enumify.pl
sub trim { my $s = shift; $s =~ s/^\s+|\s+$//g; return $s };
my #lines;
$filename = $ARGV[0];
if (length($filename) > 0) {
{ local *FILE; open FILE, "<$filename"; #lines = <FILE>; close FILE }
} else {
#lines = <STDIN>;
}
if (scalar #lines) {
my $target = "switch(<#switchvar#>) {\n";
my $enumlock = 0;
foreach (#lines) {
if ($enumlock == 0 && m/enum/) {
$enumlock = 1;
}
elsif ( $enumlock == 1) {
$_ = trim($_);
if (!(m/[{}]/) && (length($_)>0)) {
$_ =~ s/,//;
$target = "$target case $_:\nbreak;\n";
}
}
}
$target = "$target default:\nbreak;\n}\n";
if ( $enumlock == 1) {
print $target;
}
}
It turns
typedef enum {
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
} DayOfWeek;
into
switch(<#switchvar#>) {
case Sunday:
break;
case Monday:
break;
case Tuesday:
break;
case Wednesday:
break;
case Thursday:
break;
case Friday:
break;
case Saturday:
break;
default:
break;
}

You can just right click the switch case itself and select Refactor > Expand Switch Cases and Xcode will fill in all cases that match the option. Confirmed working in Xcode version 12.0.

As of Xcode 13 (I think) it's finally built in!
Just start typing switch yourEnumName, and while you're in the middle of typing yourEnumNa... look for the autocomplete ellipsis:
I missed it at first because if you type the entire name, the top result becomes something else, leaving you to do things manually.

Related

Appium: automatically handling the iOS location permissions popup

I need to be able to either prevent the location permissions popup from occurring or have it handled by Appium/WebDriver.
It looks like the Appium settings API supports these two settings for this purpose:
appium:autoAcceptAlerts
and
acceptAlertButtonSelector
We're using Java and unless I'm mistaken, settings have to be applied via a driver instance, something like this:
driver.setSetting("appium:autoAcceptAlerts",true);
The problem that I have is that when creating the driver instance, we have to pass in the capabilities that we require. In practice, this seems to mean that by the time the instance has been created, the application has already launched and the location permissions popup is already on display before we can set the appropriate settings.
I'm sure I must be missing something so I'd appreciate it if anyone can point out my mistake
TIA,
Mike
You can Try this in your capabilities setting:
capabilities.setCapability("autoAcceptAlerts", "true"); // for auto alerts
capabilities.setCapability("autoGrantPermissions", "true"); // for auto premission
or you can try manual handle with this:
custom element when you needed with alert displayed
public void alertAction(String action) {
By element;
switch (action.toUpperCase()) {
case "DONT ALLOW":
element = MobileBy.id("Don’t Allow");
break;
case "ALLOW":
element = MobileBy.id("Allow");
break;
case "ASK APP NOT TO TRACK":
element = MobileBy.id("Ask App Not to Track");
break;
case "OK":
element = MobileBy.id("OK");
break;
case "TONTON SEKARANG":
element = MobileBy.id("Tonton Sekarang");
break;
case "CANCEL":
element = MobileBy.id("CANCEL");
break;
case "LOGOUT":
element = MobileBy.id("LOG OUT");
break;
default:
throw new IllegalStateException("Unexpected value: " + action.toUpperCase());
}
if (isPresent(element)) {
clickOn(element);
}
}
this work for me on appium version 1.21.0, and iOS version 15

Fill TextField in UI Test Fails

I'm trying to make a simple UI Test in my iOS Application. I'd like to fill a textfield with some text but an error always appears.
First try:
let searchField = app.textFields.elementBoundByIndex(0);
searchField.tap()
searchField.typeText("Holy Grail")
The field is highlighted, the keyboard appears and, after some seconds, randomly one of these:
- Timed out waiting for IDE barrier message to complete
- failed to get attributes within 15.0s ui test
- failed to get snaphots within 15.0s ui test
Second try:
func setText(text: String, application: XCUIApplication) {
//Instead of typing the text, it pastes the given text.
UIPasteboard.generalPasteboard().string = text
doubleTap()
application.menuItems["Paste"].tap()
}
...
let searchField = app.textFields.elementBoundByIndex(0);
searchField.tap()
searchField.setText("Holy Grail")
Same result.
Tried with Connect Hardware Keyboard on and off.
Tried with iPhone 6s simulator, iPad Retina simulator, iPad 2 simulator.
Tried only with Xcode 7 (8 will break the project)
Ideas? Thanks in advance!
I don't have an answer on why this is happening, but for any future guy that has my same problem here is my workaround, on which I'm working on.
Basic idea is to make the keyboard appear, and then hit every single key I need to build my string.
func testPlayground() {
let app = XCUIApplication()
waitAndTap(app.textFields["MyTextField"])
type(app, text: "hej")
}
func waitAndTap(elm: XCUIElement){
let exists = NSPredicate(format: "exists == true")
expectationForPredicate(exists, evaluatedWithObject: elm, handler: nil)
waitForExpectationsWithTimeout(10.0, handler: nil)
elm.tap()
}
func type(app: XCUIApplication, text : String){
//Wait for the keyboard to appear.
let k = app.keyboards;
waitForContent(k, time: 10.0)
//Capitalize the string.
var s = text.lowercaseString;
s.replaceRange(s.startIndex...s.startIndex, with: String(s[s.startIndex]).capitalizedString)
//For each char I type the corrispondant key.
var key: XCUIElement;
for i in s.characters {
if "0"..."9" ~= i || "a"..."z" ~= i || "A"..."Z" ~= i {
// Then it's alphanumeric!
key = app.keys[String(i)];
}
else {
// Then is special character and is necessary re-map them.
switch i {
case "\n":
key = app.keys["Next:"]; // This is to generalize A LOT.
default:
key = app.keys["space"];
}
}
waitAndTap(key);
}
So: waitAndTap() is a function that I use A LOT in my tests. It waits for the existence of an element and it taps it. If in ten seconds it doesn't show up, fail the test.
testPlayground() taps the textfield in which I wanna write and then calls type()
type() is the core. Basically, look at every single character in the string and tap it. Problems:
If I look for character H in a keyboard where shift is not activated it will never find an uppercase letter. My solution is to capitalize the string, but it works only in specific cases. This is enhanceable
It doesn't work with special characters, where you cannot look for it exactly, but for the key´s name ("space" instead of " "). Well, no solution here, aside that horrible switch-case, but well, for what I need to do it works.
Hope this can help someone!
First try to disable I/O -> Hardware Keyboard and see what will happen.
If still not working
instead of
searchField.tap()
searchField.typeText("Holy Grail")
try this
app.keys["H"].tap()
app.keys["o"].tap()
app.keys["l"].tap()
app.keys["y"].tap()
This method allows you to enter Uppercase and Lowercase strings
func enterUsername(username: String?) {
guard
let input = username,
var previousCharaterIsLowerCase = input.first?.isUppercase else {
XCTFail("Username is not set")
return
}
for char in input {
if char.isUppercase && previousCharaterIsLowerCase {
// Switch on capslock
app.buttons["shift"].tap()
app.buttons["shift"].tap()
}
if char.isLowercase && previousCharaterIsLowerCase == false {
// Switch off capslock
app.buttons["shift"].tap()
app.buttons["shift"].tap()
app.buttons["shift"].tap()
}
previousCharaterIsLowerCase = char.isLowercase
app.keys[String(char)].tap()
}
}

RxSwift: How to use shareReplay to lazily get subscription

So I want to be able to lazily subscribe to shared data without it persisting when nobody is subscribed. Then if someone subscribes again, a new observable will be created. I would use a Variable, but I don’t want it to persist if no one is subscribed (because if I were using arrays or something larger than an int I don’t want to keep them in memory). My current implementation works, except when resubscribing it still gets the last value, which means the value is still persisted. I’m thinking of setting the observable to nil, but I don’t know where to do that. Can anyone help me complete this? The code below shows it mostly working, but it looks like the data is sticking around when no one is subscribed.
var switchTwoDisposable: Disposable? = nil
​
#IBAction func switchOneChanged(sender: UISwitch) {
if sender.on {
self.switchOneDisposable = currentNumber().subscribeNext { (value) in
log.debug("Switch 1: \(value)")
}
} else {
switchOneDisposable?.dispose()
}
}
​
#IBAction func switchTwoChanged(sender: UISwitch) {
if sender.on {
self.switchTwoDisposable = currentNumber().subscribeNext { (value) in
log.debug("Switch 2: \(value)")
}
} else {
switchTwoDisposable?.dispose()
}
}
​
var numberObservable: Observable<Int>? = nil
​
func currentNumber() -> Observable<Int> {
if let number = numberObservable {
return number
}
self.numberObservable = Observable<Int>.interval(5.0, scheduler: MainScheduler.instance).shareReplay(1)
return self.numberObservable!
}
​
​
// Switch 1 turned on
// logs "Switch 1: 0"
// logs "Switch 1: 1"
// Switch 2 turned on
// immediately logs "Switch 2: 1"
// logs "Switch 1: 2"
// logs "Switch 2: 2"
// Switch 1 turned off
// logs "Switch 2: 3"
// Switch 2 turned off
// nothing happens here until we take action again
// Switch 1 turned on
// logs "Switch 1: 3"
// logs "Switch 1: 0"
I finally found the convenient method that does exactly what I need. shareReplayLatestWhileConnected() on an observable will replay the latest value to the 2nd, 3rd, 4th, etc. subscribers, but when everyone unsubscribes, then the last value is not retained.
From the example above replace this line:
self.numberObservable = Observable<Int>.interval(5.0, scheduler: MainScheduler.instance).shareReplay(1)
...with this line:
self.numberObservable = Observable<Int>.interval(5.0, scheduler: MainScheduler.instance).shareReplayLatestWhileConnected()
Update
In my case, I specifically want to get a value from disk (e.g. Core data or NSUserDefaults) and then if someone updates that value, they can post to a notification that I'll observe with rx_notification. Therefore, in order for this lazy loading to truly work, I would also want an initial value. Therefore it would be helpful to use startWith in this case, where the value given to startWith is the current value on disk. So the code would be analogous to:
Observable<Int>.interval(5.0, scheduler: MainScheduler.instance).startWith(100).shareReplayLatestWhileConnected()

Swift code compiles fine on iOS, gives warning on OSX: Enum case pattern cannot match values of the non-enum type 'SKPaymentTransactionState`

I have a swift file that I'm using in a couple applications (one being OSX, one being iOS). The code compiles and works fine on iOS, but fails to compile on OSX. Here's the iOS friendly code:
if let transactionsArray = transactions as? [SKPaymentTransaction]{
for transaction in transactionsArray{
switch transaction.transactionState{
case .Failed,.Purchased,.Restored:
queue.finishTransaction(transaction)
default:
break
}
}
}
I can change it to:
if let transactionsArray = transactions as? [SKPaymentTransaction]{
for transaction in transactionsArray{
switch transaction.transactionState{
case SKPaymentTransactionStateFailed, SKPaymentTransactionStatePurchased, SKPaymentTransactionStateRestored:
queue.finishTransaction(transaction)
default:
break
}
}
}
and it'll fix it for OSX— but then iOS throws a fit. I can't seem to come up with anything that'll keep them both happy.
Looking at the documentation, I see that on OSX, SKPaymentTransactionState is define as:
typealias SKPaymentTransactionState = Int
where as on iOS the type is define as:
enum SKPaymentTransactionState : Int {
case Purchasing
case Purchased
case Failed
case Restored
case Deferred
}
I really don't want to do a #if os(iOS)… #else …, so what is the proper way to deal with this discrepancy?

Design pattern for waiting for user interaction in iOS?

I'm developing a BlackJack game for iOS. Keeping track of the current state and what needs to be done is becoming difficult. For example, I have a C++ class which keeps track of the current Game:
class Game {
queue<Player> playerQueue;
void hit();
void stand();
}
Currently I'm implementing it using events (Method A):
- (void)hitButtonPress:(id)sender {
game->hit();
}
void Game::hit() {
dealCard(playerQueue.top());
}
void Game::stand() {
playerQueue.pop();
goToNextPlayersTurn();
}
as more and more options are added to the game, creating events for each one is becoming tedious and hard to keep track of.
Another way I thought of implementing it is like so (Method B):
void Game::playersTurn(Player *player) {
dealCards(player);
while (true) {
string choice = waitForUserChoice();
if (choice == "stand") break;
if (choice == "hit")
dealCard(player);
// etc.
}
playerQueue.pop();
goToNextPlayersTurn();
}
Where waitForUserChoice is a special function that lets the user interact with the UIViewController and once the user presses a button, only then returns control back to the playersTurn function. In other words, it pauses the program until the user clicks on a button.
With method A, I need to split my functions up every time I need user interaction. Method B lets everything stay a bit more in control.
Essentially the difference between method A and B is the following:
A:
function A() {
initialize();
// now wait for user interaction by waiting for a call to CompleteA
}
function CompleteA() {
finalize();
}
B:
function B() {
initialize();
waitForUserInteraction();
finalize();
}
Notice how B keeps the code more organized. Is there even a way to do this with Objective-C? Or is there a different method which I haven't mentioned recommended instead?
A third option I can think of is using a finite state machine. I have heard a little about them, but I'm sure if that will help me in this case or not.
What is the recommended design pattern for my problem?
I understand the dilemma you are running into. When I first started iOS I had a very hard time wrapping my head around relinquishing control to and from the operating system.
In general iOS would encourage you to go with method A. Usually you have variables in your ViewController which are set in method A(), and then they are checked in CompleteA() to verify that A() ran first etc.
Regarding your question about Finite State Machines, I think that it may help you solve your problem. The very first thing I wrote in iOS was a FSM (there for this is pretty bad code) however you can take a look here (near the bottom of FlipsideViewController.m:
https://github.com/esromneb/ios-finite-state-machine
The general idea is that you put this in your .h file inside an #interface block
static int state = 0;
static int running = 0;
And in your .m you have this:
- (void) tick {
switch (state) {
case 0:
//this case only runs once for the fsm, so setup one time initializations
// next state
state = 1;
break;
case 1:
navBarStatus.topItem.title = #"Connecting...";
state = 2;
break;
case 2:
// if something happend we move on, if not we wait in the connecting stage
if( something )
state = 3;
else
state = 1;
break;
case 3:
// respond to something
// next state
state = 4;
break;
case 4:
// wait for user interaction
navBarStatus.topItem.title = #"Press a button!";
state = 4;
globalCommand = userInput;
// if user did something
if( globalCommand != 0 )
{
// go to state to consume user interaction
state = 5;
}
break;
case 5:
if( globalCommand == 6 )
{
// respond to command #6
}
if( globalCommand == 7 )
{
// respond to command #7
}
// go back and wait for user input
state = 4;
break;
default:
state = 0;
break;
}
if( running )
{
[self performSelector:#selector(tick) withObject:nil afterDelay:0.1];
}
}
In this example (modified from the one on github) globalCommand is an int representing the user's input. If globalCommand is 0, then the FSM just spins in state 4 until globalCommand is non zero.
To start the FSM, simply set running to 1 and call [self tick] from the viewController. The FSM will "tick" every 0.1 seconds until running is set to 0.
In my original FSM design I had to respond to user input AND network input from a windows computer running it's own software. In my design the windows PC was also running a similar but different FSM. For this design, I built two FIFO queue objects of commands using an NSMutuableArray. User interactions and network packet would enqueue commands into the queues, while the FSM would dequeue items and respond to them. I ended up using https://github.com/esromneb/ios-queue-object for the queues.
Please comment if you need any clarification.

Resources