Tuesday, April 11, 2017

UINavigationController - ask for confirmation on Back

Didn't expect this to be such a problem, but once you tap into the UINavigationController things become hairy.

Requirement:

When user leaves a screen by tapping on a "back" in navigation bar and there are changed data in the screen I should ask for a confirmation and keep user at the current UIViewController if she decided to continue editing the data.

Solution.

You may run into this: http://stackoverflow.com/questions/1214965/setting-action-for-back-button-in-navigation-controller/19132881#19132881 (particularly this: https://github.com/onegray/UIViewController-BackButtonHandler).

Once, I was trying to solve the keyboard accessory to be shown each time for each UITextField on shouldBeginEditing by writing a category for a UITextField. And here is something I learned in a hard way:

When you plan or see any category re-writing the existing framework method, STOP! Simple as this and go read on what can turn wrong with this.

The solution mentioned above use this:

1
2
3
4
5
@implementation UINavigationController (ShouldPopOnBackButton)

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {

 if([self.viewControllers count] < [navigationBar.items count]) {

No go for me, no need to study, excuse for being abrupt :). But one part of code from this solution turned actually to be useful.

One of the comments on another post: http://stackoverflow.com/questions/20327165/popviewcontroller-strange-behaviour got me here: http://www.hkwebentrepreneurs.com/2013/11/ios-prevent-back-button-navigating-to.html

And was not I lucky? It really makes sense, no private APIs, framework's UIViewController gets the chance to do its stuff always. What I wanted to improve though was that "safeDelegate" property and the way it is established. So I added a new method (into UISafeNavigationController.m):


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-(id<UISafeNavigationDelegate>) popDelegate
{
    UIViewController *topController = [self topViewController];
    
    if ([topController conformsToProtocol:@protocol(UISafeNavigationDelegate)]) {
        return (id<UISafeNavigationDelegate>)topController;
    }
    
    return nil;
}

And then you can just substitute safeDelegate with popDelegate:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
    if (self.popDelegate && ![self.popDelegate navigationController:self
                                             shouldPopViewController:[self.viewControllers lastObject]
                                                                 pop:^{ [super popViewControllerAnimated:animated]; }])
    {
        if (self.navigationBar) {
            [self restoreViewsForNavigationBar:self.navigationBar];
        }
        return nil;
    }
    
    return [super popViewControllerAnimated:animated];
}

Also note lines 7-9 where I call a new method (borrowed from the first stackoverflow solution that I actually criticize :)):


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
-(void) restoreViewsForNavigationBar: (UINavigationBar *) navigationBar
{
    for(UIView *subview in [navigationBar subviews]) {
        if(0. < subview.alpha && subview.alpha < 1.) {
            [UIView animateWithDuration:.25 animations:^{
                subview.alpha = 1.;
            }];
        }
    }
}

This is to avoid the back arrow in the navigation bar looking as disabled when answer from our controller to the shouldPop is NO.

Then protocol method in the related view controller may look like:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
- (BOOL)navigationController:(UINavigationController *)navigationController
     shouldPopViewController:(UIViewController *)controller pop:(void(^)())pop
{
    if (!_item.id) {
        UIAlertController *alert = [UIAlertController alertControllerWithTitle:LSSTRING(@"Save the item?") message:LSSTRING(@"You are closing this screen by using Back button and have not saved the item.") preferredStyle:UIAlertControllerStyleAlert];
        
        [alert addAction:[UIAlertAction actionWithTitle:LSSTRING(@"Save and close") style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
            [self done:nil];
        }]];
        
        [alert addAction:[UIAlertAction actionWithTitle:LSSTRING(@"Don't save and close") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) {
            [self doCancellationCleanup];
            if (pop) {
                pop();
            }
            
        }]];
        
        [alert addAction:[UIAlertAction actionWithTitle:LSSTRING(@"Cancel") style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
            
            
        }]];
        
        [alert show];
        return false;
    }
    
    return true;
}

Hope this can help someone! All the credit goes to Hong Kong Web Entrepreneur guy! Once again, here: http://www.hkwebentrepreneurs.com/2013/11/ios-prevent-back-button-navigating-to.html

[UPDATE] In the end I had to rework the final part presented here - showing the confirmation as the way it is presented was not releasing the controller correctly and was not cleaning up the views as well. Quite bigger effort is required to get it right and it is not that generic in the end. But this is a good start! :).

Sunday, April 9, 2017

Debugging in elixir. Old erlang version got into my way.

Today, some dialyzer stuff, but mainly debugging with Elixir was my study subject and so first I ran into iex telling me "Could not find 'wxe_driver.so'" on running :debugger.start(). Then I read that debugger starts anyway, so I tried simple :int.ni(Module), but was greeted with "no beam found for Elixir.Module". Read more and it pointed to Erlang version being lower than 19. Really iex and elixir --version were telling its 18 that is being started. Using brew upgrade erlang or elixir, brew reinstall - same thing. A year ago I installed erlang from the official installer here (https://www.erlang-solutions.com/resources/download.html). Looked there and there is an even version manager for the Erlang on OSX now?! Cool:



This surely helped to have version of erlang as 19.3 for elixir now and all the debugger stuff worked as expected.

I'm looking more and more into the Sasa Juric blackjack example as I'm kind of getting a bit sad about the book I wanted to read as a practical guide to building elixir/OTP apps: https://pragprog.com/book/lhelph/functional-web-development-with-elixir-otp-and-phoenix
I'm kinda on Sasa's side of analysis how the functional apps should be built and the book mentioned is not leading in the right way I believe. I'm still at its start though, author may still refactor his approach later... :).

Sasa uses dialyzer in his example and I read before that it is a good idea to use it, so studied today on this and typespecs in Elixir as well: http://elixir-lang.org/getting-started/typespecs-and-behaviours.html. I also loved a TDD example on building roman numbers converter here: https://medium.com/@barruumrex/seeking-simple-satisfaction-2a098902ddff


Wednesday, April 5, 2017

Elixir - bind the match subject in case statement


Before I forget again on how to bind the whole match subject in case statement in Elixir:



"state" as fn argument name and "state" in case surely are having the different scope, that's just I wanted to have the same name saying "state" :).

Reading now "Functional Web Development with Elixir, OTP, and Phoenix", so this is my version of authors to_string:

def​ to_string(coordinate) ​do​
 ​"​​(in_island:​​#{​island(coordinate)​}​​, guessed:​​#{​guessed?(coordinate)​}​​)"​
​end​

I thought I'd write it in a way that I would not call the wrapping functions on Agent for each interpolated field, plus would avoid changing to_string implementation with every struct change.

By the way, new cool link for learning Elixir:


Also, just in time for reading the book mentioned above comes an article from Saša Jurić:
To spawn, or not to spawn? Definitely going to validate the book suggested solution/architecture with what Saša Jurić is writing.
Done with learning for today, back to creating some added value to my customers!

Thursday, March 30, 2017

xcode 8.3 CompileStoryboard Internal error. UICGColor encodeWithCoder. Please file a bug at bugreport.apple.com and attach ...

Was dancing with my voodoo drum for a few minutes after upgrading to xcode 8.3 today. One of my storyboards that have existed for last 3 years suddenly started to cause the compile error:

CompileStoryboard bLocNote/taxi/EditTariffStoryboard.storyboard
2017-03-30 17:48:00.332 Interface Builder Cocoa Touch Tool[29650:5173036] *** Assertion failure in -[UICGColor encodeWithCoder:], /BuildRoot/Library/Caches/com.apple.xbs/Sources/UIKit_Sim/UIKit-3600.7.47/UIColor.m:1549
2017-03-30 17:48:08.466 ibtoold[29647:5172916] [MT] IBPlatformTool: Wrote failed marshalling request diagnostics to path: /var/folders/8j/x30frbx948l8djrb6ff7mljc0000gn/T/IB-agent-diagnostics_2017-03-30_17-48-08_415000
/* com.apple.ibtool.errors */
/Users/stanislavdvoychenko/Documents/code/speedo/bLocNote/taxi/EditTariffStoryboard.storyboard: error: Internal error. Please file a bug at bugreport.apple.com and attach "/var/folders/8j/x30frbx948l8djrb6ff7mljc0000gn/T/IB-agent-diagnostics_2017-03-30_17-48-08_415000". 

Was compiling fine just an hour ago on xcode 8.2.1! Ok, it tells something on not liking the UIColor, and log it mentions further says colors should be RGB or White space. I went by "binary search" removed everything from the storyboard except the table view -> build still fails. Only value catching the eye was "Tracking" color for the section view saying showing a black stripe with no color name, I have many of these assigned in other places though, guess it is still somehow different in this storyboard, so I reselected a "black" color for it:


And it compiled! Huraaay and back to programming!

Thursday, March 16, 2017

Inherited uiviewcontrollers references in one storyboard. This class is not key value coding-compliant for the key ...

I often inherit my UIViewControllers to add functionality on top of already existing. All works fine, but today I ran into the weird problem. Setup like this:



Where rangeItemEditor view controller inherits from itemEditor view controller. All outlets connected in the parent view controller. On row tap I do performSegue and then this:

This class is not key value coding-compliant for the key ...

Complaining about one of the outlets. After standard troubleshooting, checking that .destinationViewController is right, bunging my head a bit against the wall and trying [navigationController push] which worked I looked more carefully at the Storyboard ID values for these references. Here is obviously a good one:



And the other one, inherited rangeItemEditor had it empty. Filling it in and running again proved the idea that missing Storyboard ID in Identity tab for a references storyboard is a bad thing. I was not clearing this out though, not sure how it happened to be empty.

Thought I'd share to make your troubleshooting of similar cases faster!

In the end I got rid of these references in the storyboard and used [navigationController push]. Why? I just really don't want these crashes when xcode UI Editor decides to remove something behind the scenes. I've witnessed this "open the file -> get the mess" already in xcode storyboards and I really better stay safer here with using old gold manual push. Staying away from the magic until it has predictable results :).

Monday, February 20, 2017

Working around WITH not being available in older versions of sqlite.

For one of my iPhone apps I need to rename route points to match their order as they are inserted or deleted, I started with an easy WITH version:


[NSString stringWithFormat:@"WITH wcte (id, wname) AS (SELECT w.id, '%@ ' ||  (SELECT COUNT(*) + 1 FROM waypoint WHERE vY1 < w.vY1) as wname FROM waypoint w)  UPDATE waypoint SET \"name\" = (SELECT wname FROM wcte WHERE id = waypoint.id) WHERE \"name\" LIKE '%@ %%' OR \"name\" is null OR \"name\" = ''", LSSTRING(@"Point"), LSSTRING(@"Point")]

And ran into the problem when testing on iOS8.1, obviously its version of sqlite didn't support WITH at that time.

So here is the workaround solution for older sqlite versions:

[NSString stringWithFormat:@"UPDATE waypoint SET \"name\" = (SELECT '%@ ' || (SELECT COUNT(*) + 1 FROM waypoint w WHERE w.vY1 < w1.vY1) FROM waypoint w1 WHERE w1.id = waypoint.id) WHERE \"name\" LIKE '%@ %%' OR \"name\" is null OR \"name\" = ''", LSSTRING(@"Point"), LSSTRING(@"Point")]

Not that nicely looking, but I'm still committed to support iOS8 for a few more months. If you are puzzled by that LSSTRING part - that's just my macro for the LocalizableString as I only want to rename these points that are named automatically and surely I want to name them in the localized manner. Punto it is in Spanish (I hope) :). vY1 is a cryptic name for the order column :).

Would not be publishing at all, but sqlite syntax sometime is surprising in what it can or can't do, so I thought I might save time to someone.

If you are into hiking, fishing, cycling or classic skiing here is the app link, it's free: https://itunes.apple.com/us/app/id1120906807


Friday, December 9, 2016

Elixir, Phoenix, React, Redux and ES6 - I'm a complete beginner again.

I started with Erlang and now progressing with Elixir, Phoenix, React, Redux and ES6 stack. Was feeling a bit lost when seeing stuff like this:

 var createStoreWithMiddleware = (0, _redux.applyMiddleware)(reduxRouterMiddleware, _reduxThunk2.default, loggerMiddleware)(_redux.createStore);

or:

const store = autoRehydrate()(createF8Store)(reducers);

Had to remind myself on a simple function chaining, like:

const p = (store)=>{console.log("I'm an almighty container, you passed me the store: " + store);};

const f = (a,b)=>{
  console.log("Thanks for a and b: " + a + ", " + b + " it helped me to build a container with I return to you. Pass a store into it!");
return p;
};

f(3,4)("Store is just a name 'John'");

With the output of:


  • "Thanks for a and b: 3, 4 it helped me to build a container with I return to you. Pass a store into it!"

  • "I'm an almighty container, you passed me the state: Store is just a name 'John'"
Beginner mind work play games with me, tendency to get lost on trivial subjects is quite high :):):).

A list of resources I really appreciated so far:

React:
https://facebook.github.io/react/docs
https://egghead.io/courses/react-fundamentals
https://egghead.io/courses/react-flux-architecture-es6

Redux:
http://redux.js.org
https://egghead.io/lessons/javascript-redux-reducer-composition-with-combinereducers

ES6:
https://ponyfoo.com/articles/es6

ES6 and modules (oh, how could have I got so much behind?):
https://24ways.org/2014/javascript-modules-the-es6-way/
https://babeljs.io/learn-es2015/

ES6 Promises: http://www.datchley.name/es6-promises/

I also found this article being an awesome walkthrough on getting React/Redux/ES6 and more together in Facebook's makeitopen app:
http://makeitopen.com/tutorials/building-the-f8-app

Webpack:
http://ryanchristiani.com/introduction-to-webpack/

Elixir and Phoenix: 
Pragmatic programmers - Elixir programming, Phoenix programming.
Phoenix and Trello clone: http://codeloveandboards.com/blog/2016/01/14/trello-tribute-with-phoenix-and-react-pt-1/