How I deleted all my local Evernote notes and recovered them

I am not the first nor the last idiot to delete all my local Evernote notes. Given that they were in a local notebook, you can imagine how important they were to me. Commence panic.

Events leading up to the catastrophe

1. I updated Yosemite.

2. Evernote starts acting up and won’t let open.

3. I go to the app store to download Evernote only to find that I can only “Install” it. Hit install.

4. Find all local files missing. Have a heart attack.

How to Recover your Local Evernote notes

1. Turn on hidden files.

Hit Command + Spacebar to bring up spotlight and search for Terminal

After opening terminal, it should look like this:

Screen Shot 2015-02-01 at 9.54.02 PM

Close all of your finder windows

Enter this line in Terminal and hit enter

defaults write com.apple.finder AppleShowAllFiles TRUE

Nothing will happen. Open Finder again and you will see grayed out files. Those are the hidden ones.

2. Restore notes from Time Machine

Go to Macintosh HD/Users/yourUsername/Library/CoreData

It should have be empty.

Open up Time Machine and go back to an hour ago when you still had the com.evernote.Evernote folder. If you don’t have Time Machine enabled, then you’re out of luck. Sorry. Should look something like this.

Screen Shot 2015-02-01 at 9.58.47 PM

The green files there are your Evernotes.

Hit restore and you will find them in Finder.

3. Delete Evernote from Applications

4. Download Evernote from the website if you had installed Evernote from the App Store

5. Install Evernote again and find your files

Why did it happen?

Installing Evernote from the App Store replaced my old version of Evernote with a clean one which synced with the server. Unfortunately, my local notebooks were not synced with the server, so they were lost.

Why does this solution work?

Evernote Installed from the App Store and Evernote downloaded from the web and installed store their notes in two different places.

Evernote from the App Store puts their notes in the folder:

Macintosh_HD/Users/yourUsername/Library/Containers/
com.evernote.Evernote/Data/Library/CoreData/
com.everNote.Evernote/longAlphanumericString/ENNote/
_records/

Evernote downloaded from their website puts their notes in the folder:

Macintosh_HD/Users/yourUsername/Library/CoreData/
com.evernote.Evernote/longAlphanumericString/ENNote/
_records/

This makes sense because Apple wants all the files for the apps in the App Store to be neatly organized in one container. Normally, when you install a program, it will add file in lots of different places.

Installing Evernote from the App Store deleted the data from the folder where Evernote from the website would typically store it’s files.

You just have to put it back by restoring from Time Machine and getting the right Evernote (the one from the website) to read the files from the right place.

What attempts did not work?

Replace the CoreData folder in the container with the restored one

You would think that simply moving the files from the CoreData folder into the container that App Store Evernote uses would be sufficient, but in fact that Evernote will create a new folder to store it’s notes because the longAlphanumericString is not the same as the one it was expecting.

The notes simply will not open in Evernote or worse tell you that it has an unspecified owner and for you to log out.

Copying the .enspot files directly over also did not work

I will be keeping my notes synced from now on

What use is privacy when you lose your data?

Playing with iOS MapKit Part 4: Race Conditions, Other Bugs, Wrap-up

Playing With MapKit

Part 1 MapKit Tutorials 

Part 2 Reverse Geocoding and Custom Annotation Callouts

Part 3 Making it Pretty

Part 4 Race Conditions, Other Bugs, and Wrap-up

As the functionality and the look was coming into place, I took note of bugs that I saw.

The most insidious bugs are ones that don’t happen all the time or under conditions (ex. when internet is especially slow or the computer is especially fast). One example is the race condition.

What is a Race Condition?

A race condition is a situation when the sequence of asynchronous responses results in an undesired effect.

My Example

Very quickly after starting the app, I was tapping the current location dot and I was seeing “Current Location” in the bubble. It should have said the address that the address was closest to. If I waited a couple seconds to tap the current location, it would display the address as expected.

This led me to believe that I had a race condition.

Under normal conditions:

  1. The map updates the user location calling its delegate mapView:didUpdateUserLocation: which starts the reverseGeocoding call based on the user’s location.
  2. The reverseGeocoding response is received and the completion block sets the annotation’s title with the address of the user location.
  3. When the user location dot is tapped, it will display a custom bubble using the annotation’s title in the label.

Usually the internet is fast enough that the reverseGeocoding response returns before the user location dot is tapped.

The two things that are racing are

  1. the reverseGeocoding network call and
  2. the tap of the current location dot.

If 1 finished first, then the app would work as intended. If 2 finished first, the experience would be suboptimal.

Design Choice

One way to fix this is to update the label when the reverseGeocoding network call came back.

At the end of the completion block, I added a method call to updatePinCallout, which pretends that the user location dot was tapped again and reloads the address.

- (void)updatePinCallout
{    
  if (self.userLocationPin.selected) {
   [[NSOperationQueue mainQueue] addOperationWithBlock:^{
    [self.userLocationPin setSelected:YES animated:YES];
   }];
  }
}

Just remember to use the mainQueue to retap the dot or else the address won’t update on screen.

This change also fixed another bug that happened when the user location would jump when WIFI was enabled and the address label would not update to the new address.

Alternative Choice

Another thing that I found solved the problem pretty well was to zoom the map to the user location immediately, so that the animation didn’t even give a chance to tap the user location dot.

Other Bugs

Writing code for multiple scenarios unnecessarily

When I first implemented zooming to fit destinations, I only zoomed when the location was outside of the visible region. I realized that sometimes this would not be sufficient because sometimes the map would be zoomed way out, so I decided to account for that scenario too. I would zoom in if the destination was in the original visible area. This got complicated quickly.

Eventually, I realized that it was too much and just made it always zoom to the destination. The code went from 14 lines to 1.

Simpler is Better

Testing Days Later

I thought I was done with the project. Wrong. A few days later, I was trying out the app on the bus and came across two bugs.

  1. I was on a bus and the location was updating very quickly and constantly centering the map on the new location. The problem was that I wanted to look around the map and couldn’t do that without it changing while I was using it.
    • This is easily fixed by only zooming on the time the map loads.
  2. I was looking for a Pret A Manger. When I saw the results page, I found it utterly useless because all of them said the same thing and I couldn’t tell which location was where.
    • I think it would have been cool to add some directions as to how far each location was from my current location and in which direction or add the cross streets.

As the Pragmatic Programmer said:

“Test your software, or your users will.”

Where to next?

There’s certainly a lot of ways this can go. Add friends, add Facebook integration, add Yelp results, use Google Maps. That would be fun to do, but would lack direction.

For a product to be real, it must have a real use.

The way that this app fits into my life has something to do with lunch (ergo the name LunchTime). In an effort to build good habits, one of them is to walk 10K steps a day. As I’ve discovered, this means walking 1.3 hours a day. If I don’t plan to walk, I won’t get near 10K.

So over lunch, I like to walk 10 to 15 minutes away and back. I think it would be cool to draw a diamond around your current location to see how far you can walk in 5 and 15 minutes and show how far each location is.

Options abound. Looking forward to the next expedition.

It’s been fun working with MapKit. Time to play around with something else. Who knows? I might be looking at WatchKit, EventKit, or something else entirely.

Flatiron School Week 9 – Week in Review

Mistakes and Lessons

Symptom: With custom UITableViewCell xibs, I was getting key value coding-compliant errors.

Mistake: When I was dragging over the views from my xib to my custom UITableViewCell class, the object chosen was “File Owner” and not the cell.

Lesson: Make sure there are no connections in the xib’s File Owner connection manager.


Symptom: Multipeer connections were not being made.

Mistake: The MCServiceType was too long.

Lesson: The MCServiceType has to be 15 characters or less.


Symptom: [self.tableView reloadData] was not reloading cells.

Mistake: I was calling the reloadData method inside a notification.

Lesson: NSNotifications are run on the background queue by default.

Notes to Self

Message syncing between servers and devices is a pain.

Reading code is a whole other skill.

I need to use CocoaPods more.

Flatiron School Week 7 – Week in Review

Topics: APIs + Core Data, Authentication, Multithreading

Mistakes and Lessons

Symptom: API calls don’t tell you a lot when they don’t work. I wasn’t getting anything out of an internet request. Debugging API calls can feel like a text based adventure.

Mistake: Errors in strings are hard to debug. Apparently, when I had made the url, there was an extra “%”.

Lesson: Copy and paste urls that work from Postman, or use an encoder to convert strings to urls.

Symptom: When I changed the colors in my bocce game, the logic for choosing the next team to go broke.

Mistake: I had changed my colors away from the default colors (e.x. blueColor) to RGB values and had used == to compare the objects. The correct way to compare the objects is using an isEqual. Turns out that when you use [UIColor colorName] using == to compare is ok because they are at the same memory location.

Lesson: Use isEqual with objects and == with primitives.

Notes to Self

There are some times when I just have to slow down and plan out how something works. I was making a tableView that updated an API and a Core Data store. This very quickly got too complicated to keep in memory and I had to write down the interactions step by stem.

My partner and I decided to divide and conquer a lab. It became very hard for me to test along the way, because I was dependent on his piece to make mine work.

“If You Want To Go Fast, Go Alone. If You Want To Go Far, Go Together”

Flatiron School Week 6 – Week in Review

Topics: Blocks, APIs

I’ve been writing three miniblogs a week on Mistakes and Lessons, Questions and Answers and Notes to Self. As I’ve progressed, I feel composition of my attention shift as well.

  • Since I’ve been making less time consuming mistakes and asking for help earlier, I don’t really have anything in the Mistakes and Lessons category. Since I’m still learning a lot, I’m going to call it Cool Stuff I Learned This Week
  • In terms of Questions and Answers, most of the questions I have are focused in areas that will take the form of side project demos.
  • I’ll still have making lots of Notes to Self.

My blog reflects that with a change in format with a Week in Review entry each week instead of three smaller ones.

Cool stuff I learned this week

  • UI changes must be made on the main thread.
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
         //stuff goes here ex:
         [self.tableView reloadView];
    }];
  • I didn’t know the difference between an access token and an API Client id & API Secret.
    The access token is used to authenticate an individual user, so that you can access their personal data.
    The client id and secret are used to authenticate to the API that you’re a registered app so that they allow you to continue to send many request to their server. This does not provide user authentication by itself.

Notes to Self

I like generalizing problems to find patterns in solutions and that turns out to be a really useful skill for a programmer, so that I can write reusable code.

Programming is very cumulative. Everything is built on a previously made part.

Playing chess while I was kid really helps me to visualize objects moving in my mind and that helps me keep track of blocks moving between classes.

Flatiron School Week 5 Mistakes and Lessons

Topics: Autolayout, views, gesture recognizers

Symptom: The gesture recognizer doesn’t work.

Mistake: Making one recognizer and assigning it to four objects.

Each gesture recognizer can only have one owner and each assignment meant that it was just being reassigned to the latest view.

Lesson: Make one gesture recognizer for each object.


Symptom: Constraints were not working.

Mistake: I had assigned constraints to the view.

Lesson: Constraints are sent to the superview. Gesture recognizers are sent to the view.


Symptom: My game’s win conditions were being satisfied when they shouldn’t have been.

Mistake: When comparing the two views’ centers, I checked if the difference was less than a threshold, when I meant to use the absolute value of the difference.

Lesson: Unexpected wins are bugs too. They should be understood as much as bad bugs.


Symptom: My autolayout had more constraints than I thought I had specified.

Mistake: To deal with the iPhone size plus, I had constraints for when trait collections display scale were 3, but had forgotten to wrap the other constraints in an else statement.

Lesson: Those willTransitionToTraitCollection:withTraitCollection: methods can get really complicated and I have to be careful with the large if statements.


Flatiron School Week 4 Mistakes and Lessons

Topics: Cocoapods, Core Data

Symptom: The search functionality works most of the time but in a small set of users, the search results are empty.

Mistake: Sending an API call in the prepareForSegue method.

Let’s say that we have a view controller that searches Foursquare for restaurants. If I press submit on my search view controller, I’m pushed to a table view controller with the results. I can put the API call to fetch the data from Foursquare in the prepareForSegue call.

99.5% of the the time, it works, but the other 0.5% of the time, the previous view controller can be removed before our API call has come back with the results and we’ll be left with a table view controller with no results.

This is called a race condition and it’s a very annoying problem to fix because it’s hard to detect and I would have to have millions of users like Facebook to have this problem.

With search view controllers, pass the query to the results view controller and send the API call from there.

Lesson: Avoid race conditions always.


Symptom: I was trying to test a tableViewController to make sure that it worked with Core Data by adding 3 test objects: @”Test”, @”Test Test”, and @”Test Test Test”. I was only seeing one of the three items repeated three times.

Mistake: Messing up basics when moving on to more advanced topics.

I wasted a couple hours last week when I messed up the cellForRowAtIndexRow by writing

cell.textlabel.text = self.store.pirates[[self.tableview indexPathForSelectedRow].row];

instead of

cell.textlabel.text = self.store.pirates[indexPath.row];

Lesson: Watch for simple mistakes, when something is not working as expected.

Week 3 Mistakes and Lessons

Topics this week: TableViewControllers

Symptom: When I see an empty table view controller, this is usually the culprit. 

Mistake: Forgetting to initializing the array in a table view controller.

Lesson: always initialize the array.


Symptom: The dreaded Apple Mach-O Linker Error. Linker command failed with exit code 1. This error is super intimidating because it’s so cryptic. (What the heck does that mean anyway?)

Mistake: Turned out that I had imported a KIF test file “TableViewControllerSpec.m” into another TableViewControllerSpec.m and it was giving the errors:

duplicate symbol _OBJC_METACLASS_$_TableViewControllerSpec ...
duplicate symbol _OBJC_CLASS_$_TableViewControllerSpec ...

Lesson: Don’t quit when you hit error messages. Read the logs, it usually gets you close to the problem.


Symptom: Realizing that the full solution to a problem is not worth implementing and not having the previous version. 

Mistake: Deleting code when it’s a partial solution to go for a full solution.

Lesson: Commit code before trying an experimental solution.


Symptom: Tests fail sometimes but not all the time.

Mistake: Not resetting the test conditions after each or before each tests. Test do not go on in order in KIF.

Lesson: Check the starting condition each time before a test is run. Resetting the tester is cool.

Week 2 Mistakes and Lessons

Mistake: Thinking that tests in Specta run in order.

I was running into trouble with a test that setup a BeforeAll statement that initialized the objects and tested the initialized conditions without reinitializing the objects. The test would sometimes pass and sometimes fail. The reason was that another test was changing the variables before the test that tested the initialization was run.

Lesson: They do not! Make sure that the test resets the objects each time by using BeforeEach instead of BeforeAll.


Mistake: Designing apps for one screen size.

I was playing around with a craiglist type app before coming to the Flatiron School and was about to add the item page for each of the listings. In Xcode 5, the UI in the storyboard was in the shape of an iPhone 5s. In Xcode 6, this became a 600 x 600 point box. I learned that since there were now four screen sizes ( 3.5″, 4.0″, 4.7″ and 5.5″ diagonal), Apple wants apps to be able to support the different screen sizes. If you design the app for the 4.0″ screen, the constraints that Storyboard uses will be messed up for the other formats.

Lesson: Design for all four screen sizes. 


Mistake: Giving my classes confusing names.

Here are my class names for a project with a tableview and a detailed view.

ViewListingsTableViewController
Listing
ListingTableViewController
ListingTableViewCell

What’s the difference between the first and third controller? I couldn’t remember and ended up making the first one into ListingDetailsViewController.

Lesson: Make class names self documenting. 


Mistake: Not initializing an array and thinking that an array property is an empty array to begin with. 

If you don’t initialize an array, as in

NSArray *newArray = [[NSArray alloc] init];

(even if it’s a property) then, the new array property is nil and it will not be able to add things to it.

Lesson: Initialize and avoid nils.


Mistake: Staying late to finish something only to oversleep the next day. 

Lesson: Have a hard deadline for when to leave the office.


Mistake: Not selecting the storyboard as the main interface and not being able to see anything on the app.

Lesson: Make sure that General Settings > Deployment Info > Main Interface is not empty. Otherwise, nothing will show up.

Screen Shot 2014-10-11 at 8.26.05 PM

Week 1 Mistakes and Lessons

Topics: Git/Github, Environment Setup, Objective-C basics (loops, methods), unit testing, NSArray, NSDictionary

Mistakes and Lessons

1. Mistake: Going directly into Xcode with a half-baked plan leads to a lot of spinning my wheels and rabbit holes.

What happens here is that I think I know what I want to do in my head, sort of, but I don’t have the steps explicitly worked out yet and so as I write, I concentrate on one part of the program and forget details that I hadn’t accounted for in the other parts. Any thing that I find myself doing is just spamming different code and praying for it to work. I’m using the computer to validate for me instead of having a plan and running code that I think will work.

Lesson: Know how to do a task without a computer before trying to tell a computer to do it.


2. Mistake: not paying enough attention to spelling.

Spot what’s wrong with this for loop:

for (NSinteger fiboCounter = 1; fiboCounter < numberInFiboSequence; fiboCounter++)
{
    NSNumber *currentFiboNumber = fiboNumbers[fiboCounter];
    NSNumber *previousFiboNumber = fiboNumbers[fiboCounter - 1];
    NSInteger nextFiboNumber =  [currentFiboNumber integerValue] + [previousFiboNumber integerValue];
    fiboNumbers[fiboCounter+1] = [NSNumber numberWithInteger:nextFiboNumber];
}

Yeah, that’s right. It’s the NSinteger in the first line. It should be NSInteger.   I was getting missing identifier errors for the fiboCounter, which totally didn’t make sense, because I had already initialized it (I thought).

Lesson:  Misspelling sucks, listen to Xcode.


3. Mistake: thinking that doing everything by myself is more original and superior to copying other people’s way of doing things.

First of all, pretty much nothing is completely original.

Everything is a remix of previous things made. Why would you want to ignore how other people are doing it anyway?

“If I have seen further, it is by standing on the shoulders of giants.” 

-Isaac Newton

Even this quote proves my point. Newton was a smart guy, probably on of the few that actually did make significant advances in science. Despite that, the idea of “shoulders of giants” wasn’t original to Isaac Newton. He was preceded 500 years earlier by Bernard of Chartres.

When I first approach a problem, multiple ways of solving the problem will appear in my mind. If I was working alone, I might implement one and then try another one. The benefit of having a class of people who are working on the same problems means that I can crowdsource other solutions from them. They can teach me smart methods that I didn’t even know existed and I can learn 19 new methods in the time it would take to learn one.

Pair programming is another great way to learn from classmates. I might have my way of doing it, but pairing means that I must contemplate another person’s way as I am still solving the problem and can follow it all the way through. An additional benefit is providing motivation to finish and having someone to celebrate with when we’re done.

Lesson: Solve the problem one way and then rely on my classmates to give me other ideas for solving the same problem. Even better, pair with another person to program it, especially late at night.