<![CDATA[jarek.makes()]]>https://blog.pendowski.com/https://blog.pendowski.com/favicon.pngjarek.makes()https://blog.pendowski.com/Ghost 4.3Fri, 15 Dec 2023 14:53:53 GMT60<![CDATA[Fixing Xcode 9 UI tests]]>

After switching to Xcode 9 on our CI extended UI tests started failing. It turned out that referencing elements from navigation bars set in storyboards by image name stopped working.

I'm not a fan of such approach in the first place, but there's no way to

]]>
https://blog.pendowski.com/fixing-xcode-9-ui-tests/6080546795a3cc2495d8b84cThu, 09 Nov 2017 17:56:00 GMTFixing Xcode 9 UI tests

After switching to Xcode 9 on our CI extended UI tests started failing. It turned out that referencing elements from navigation bars set in storyboards by image name stopped working.

I'm not a fan of such approach in the first place, but there's no way to set accessibility identifier from Xcode's interface builder. UIBarButtonItem does implement UIAccessibilityIdentification but there's no way to change its values.

Two ways of solving that is either by adding it manually to "User Defined Runtime Attributes" yourself

Fixing Xcode 9 UI tests

Or you can add small workaround extension to your project:

extension UIBarButtonItem {

    // Workaround for Xcode to show option to specify accessibility identifier from IB
    // Read more: http://blog.pendowski.com/fixing-xcode-9-ui-tests
    @IBInspectable
    var xcode_accessibilityIdentifier: String? {
        get { return accessibilityIdentifier }
        set { accessibilityIdentifier = newValue }
    }
}

and edit it nicely from the UI:

Fixing Xcode 9 UI tests

]]>
<![CDATA[Super Mario Run - Part 2. It's All About the Coins]]>https://blog.pendowski.com/super-mario-run-part-2-its-all-about-the-coins/6080546795a3cc2495d8b84aWed, 18 Jan 2017 15:48:13 GMTSuper Mario Run - Part 2. It's All About the Coins

In previous post I concentrated mostly on technical aspect of where Super Mario Run falls behind. I listed technical solutions or different game design which would greatly improve initial gameplay.

Here I want to concentrate on financial aspect of the game and the causes of poor App Store ratings. And no - saying that people are being jerks that don't want to pay for things is too simplistic and doesn't get us anywhere.

There are actually two issues with Nintendo's newest game. The first one is related to state of current App Store environment, the second is design choices that Super Mario Run creators made that changes the perception of it among other titles. Let me start with the latter.

The Coins

When you think of Free to Play (or F2P for short) games there are two rules that apply to most of them:

  1. They need to be actually free to play
  2. To generate revenue you charge for customization and for making the game easier to play

This means that you should be able to play the game without spending a dime on it. It might not be the best way to experience the game, but it should be possible in the long run. It usually leads to content of the game being available to everyone for free. You don’t split the community - paying and free users both experience essentially the same game. If you pay, you might “just” get more lives, more skins, get slightly better stats or you don’t have to wait as long as free users for in-game items.

It's important to keep the community as one group because very few users actually spend any money on F2P games. Depending on where you check, only 0.5% to around 6% of players pay anything. Imagine if those players had different content - let’s say - 3 additional maps. This would mean, that for every 200 people online, you might get only one person who could also play a paid map. Multiply it by the probability of you both choosing the same map and you get poor experience as a result. That's why, in most cases, content comes free.

Why do I even mention Free to Play games? Super Mario Run isn't a F2P title, right? Well - we don't know for sure but there are a lot of things that player faces from the very beginning that could suggest different. Of course it's largely a matter of perception, but bear with me.

First of all, Super Mario Run is available for free. It's sort of a dumb argument, but without it we can't really talk about free to play game. To make money, it uses In-App Purchases, which can be anything. It can be just new level, which Nintendo is selling right now, but it can be anything within the game. There's nothing stopping Nintendo from changing that. Especially that it allows them to make money from both - new and existing users.

Another design that has emerged in the F2P game design are in-game currencies. The game has to have at least one in-game currency, but usually ends up with couple of them. Some (let’s call them coins) are usually easily available. You get them for interacting with the game, that's your reward for playing the game - it keeps you motivated to come back, earn more and spend it. Other (we'll call them diamonds) are more premium. Sometimes you can still earn them in the game, but are much harder to get and there are very few of them. Both of them can be bought by real money directly or by just earning more of them in game, if you're a paying customer.

Super Mario Run - Part 2. It's All About the Coins
Coins, caps, simoleons - they are all the same

Super Mario Run is not different in that regard. There are coins and there are tickets. You get coins by interacting with the game, you can play the same level over and over again and you'll always get 100+ more coins. Tickets are more premium currency. If you want to get them, there's basically only one "sure" way1 of getting those for free - once every 8 hours you can play a minigame and have 50% chance of getting one(!) ticket.

With those game coins you can buy some in-game customisations - new buildings and elements for your village which are mostly decorative. For other items you have to first earn Toads (which can also be treated as a currency). To get those you have to spend tickets - the premium currency - and play the Rally part of the game.

Is this really any different than Caps and Nuka-Cola from Fallout Shelter or Views and Subscribers from PewDiePie's Tuber Simulator? For now, in Mario, you can't buy those currencies with real money directly, but that's just a detail and no-one says that you won't be able to. It's actually quite likely to see an option in the future to buy something that will allow you to buy this building or character, without playing the game for 30 hours.

The point here is, even if Nintendo didn't plan it to be a standard Free to Play title (which I doubt), the game feels like one. It has all the mechanics of F2P title and you encounter them from the very first few minutes of experiencing the game.
But in contrast to normal Free to Play games, it also tries to charge you for content. This can lead to kind of cognitive dissonance, which can lead to radical drop in experience once the game asks you to pay for more levels.

Super Mario Run - Part 2. It's All About the Coins
Free to play? Demo?

The Store

The second issue is not a Mario Run specific one. It's a more general problem of the App Store and it all comes down to behavioural economics. Mario like many other apps just falls victim to it.

If I would ask a group of people how much should I charge for an iOS app, I will hear different answers. I'm sure there would be a free answer, I'm sure that $0.99 would be in there. And even if someone would give more vague answer, like "it depends on the app", I'm sure that for most, they would still think of something under 5 or 10 bucks, much closer 99 cents or magical free option.

The reason for this is the way our minds work and the way App Store worked at the very beginning. It's difficult to assess the value of something, especially if it's something somewhat new. To work around it our brain uses comparisons. We search for things that are in some way similar and have a specific, average price. Once we have it, we try to use some heuristics to guess the price, compared to what we know. In the early days of the App Store, that was kind of an advantage, since for many the closest thing that they could think of was a Mac application. And those did cost easily anywhere between $25 to $50 or more. Even if you would apply heuristic of - "well, the screen is smaller, so it had to take less time to do" or "I can't do all the things on my phone, that I could do on my powerful computer" you still had an advantage and could price your apps between 5 and 10 dollars without a problem.

But prices dropped rapidly. The App Store gold rush caused a lot of apps to be priced at the lowest tier of $0.99, hoping to make up the earnings in volume. Especially after reading about over-the-night success stories, which made millions by making a simple, cheap app. For most the dream never came. They went bankrupt, had to find some other way to make money, but the market has settled and 99 cents for paid app had become a de facto standard for most. Something that in most cases doesn't allow a sustainable income, but it's very hard to get rid of from the mind of the customer.

In that regard I actually root for Nintendo for setting the price higher than lower. The current business model isn't sustainable in the long run or causes the emerge of other, more shady in my opinion, models - like Free to Play games or aggregation and trading of personal data. The more developers and publishers will try to bring up-front prices higher, to levels which will allow to create new software and support existing one, the more chance we have to reverse the trend. But it's difficult, especially on a market where an average app did cost only $0.19 just 3 years ago. In other areas, like in food industry, they figured it out sooner. Instead of dropping prices, they use other tricks to cut down costs. In times of need, they lower the volume of their product, while sometimes keeping the package same, they change the recipes to use less or cheaper ingredients. But in development, where the biggest cost is experience and people, it's more tricky. And while everyone has to eat something, not everyone has to play your game. Unfortunately we didn't learn that soon enough how much of an impact and for how long does the price dumping have.

Super Mario Run - Part 2. It's All About the Coins
Which would you buy? Which would you rate higher?

There's also a special case of low price - free. Free has magical properties. It turns off our brains, turns off any cost-benefit analysis and leads us to make bad decisions. Dan Ariely, professor of behavioural economics did and experiment that illustrates this perfectly. In the experiment he offered two kinds of chocolates - a premium one priced at 15 cents and a cheap one for 1 cent. The cost-benefit analysis kicked in and 73 percent of people saw the better deal and chose premium chocolate. But when the price was reduced by 1 cent, making the cheap chocolate free, people started to act irrationally - choosing the cheap one 69 percent of the time, even though nothing really changed - the price difference stayed the same, making the premium chocolate still the better deal.

In this case App Store and chocolates are very similar - if we have a choice between free app and one that we have to pay for, we stop thinking rational and choose the free one. Even if the paid one is better designed, better written and is in our best interests to have a team that's able to support the app in the long run. It's just how our brains are wired and it requires significant amount of energy to work against it.

The Conclusion

So as you can see, the problem is more difficult than just people being cheap. We have a large group of clients, not thinking rationally and in their best interests, because the way we perceive and calculate value of things. On one side we don't generally compare our digital purchases with other things. We don't think that the app costs the same amount as two burgers and will be useful for longer. We compare it with other apps, which in most cases are still underpriced, because other apps are priced that way. It doesn't also help that the app is available for free and has a lot of mechanics and design choices known from free to play games, which changes our way of perceiving it.

You add all those things together and you get a good game with a rating between 3 and 4 stars. It's really scary when you think about the fact that it's a game from major, well known and received company, from one of the biggest franchises in games history. And it was and is actively promoted by Apple and Nintendo for months.

Super Mario Run - Part 2. It's All About the Coins
Love it or hate it, there's very little in between

Post Scriptum

The scary part, lurking somewhere in the background, is - if companies like Apple and Nintendo can’t sway the public perception of price for apps, how a smaller company or even an indie developer can? Especially when there’s very little place for brand on the App Store, no room for conversation with your clients, almost no room for experimentation that could lead to better solutions and fair but sustainable business models.

Only Apple can change that. Because even if we design our apps just right, there’s still a problem that general public thinks that apps should cost very little, they should be supported for a long time and updates for them should be free. Even if Nintendo made enough thanks to all the promotion and despite the poor ratings, we have a big problem, one that can’t be solved alone, can’t be solved overnight with one decision.

We have a problem of perception, of how people feel and it is in best interest of every party involved to solve that problem. Including Apple, which used to care about how their clients feel, not just to get good consumer ratings but also as a way of thinking about their products and business.


1. To be exact - you can exchange Nintendo Points for more tickets and there’s also a chance of gaining one after getting all purple coins or by buying right buildings in your village, but those aren't really repetitive in-game tasks or require purchase anyway. ]]>
<![CDATA[Super Mario Run - Part 1. The Princess from Another Castle]]>https://blog.pendowski.com/super-mario-run-part-1-the-princess-from-another-castle/6080546795a3cc2495d8b849Fri, 23 Dec 2016 17:21:27 GMTSuper Mario Run - Part 1. The Princess from Another Castle

On September 7, 2016 an extraordinary thing happened. Shigeru Miyamoto came on stage of Apple's annual iPhone event and announced that Nintendo will be releasing a Super Mario game on iOS this year.

It's big news, because it's very rare for a title of such magnitude run on non-Nintendo hardware. Even more so since the company promotes its own new console - Nintendo Switch. It shows that the company is expanding its interests and trying to take a first step into mobile entertainment market. Market which is new to them. One that they do not control anymore - the hardware, the software, nor the distribution and experience. And it my opinion it's visible in their first game - Super Mario Run.

The screens

From the very beginning the game feels Nintendo-ish and not something build with iOS at its core. I'm not talking about the bubbly design language, but a fact that the first screen you see is a region choice list.

Super Mario Run - Part 1. The Princess from Another Castle

It's really hard to make this screen any worse than it is. The UI seems like something from Wii where the input method is different. There's no sign of inertia, natural on all mobile platforms. When you touch the screen to scroll you can hear the sound of pressed buttons.

But the worst part is that there should be no such screen. They could read the region from system settings. Or at least provide an autocompletion option, since the device have keyboard. Or stick to just 10 icons of flags, showing all supported languages, instead of 150 countries. All those would be better, but in reality, this screen shouldn't be here in the first place since all the info you need is provided to you by system settings.

Then you're asked to link with our Nintendo Account. I had (and still have) the Wii but the account I used on it to buy games wasn't the account I could link on that screen. I would have to create a new one using a built in web browser. Again, not an ideal experience.

Super Mario Run - Part 1. The Princess from Another Castle

What are the benefits of having a Nintendo Account?

  1. We can sync our progress with other devices
  2. We can setup in-game avatar to Mii
  3. We gain access to some mysterious special features

I'm not going to guess what the third one means. Setting up avatar is odd, because you are able to choose from few simple ones without registration so this one only expands your options. And to do it you probably have to have some recent Nintendo system or access to separate mobile app, which isn't available worldwide. And let's face it - it's just cosmetics and not a very enticing one.

The real reason you might want to register is to sync your game progress between your iPhone and your iPad. But that's again something which is already built in the system. iCloud provides seemless synchronization between all of user's devices and GameKit provides a simple API abstraction for saving game progress (GKSavedGame and GKLocalPlayer). Just like the region, it's usually setup once for the device, so all applications can use it.

Nintendo decided to not only build their own solution to something that exists but also limit this feature only to people willing to create a separate Nintendo Account. That could be understandable if it would provide additional functionality or would just work better than iCloud does. But it does not. Because funny enough - the important information isn't written in red.

This is the only moment you can link your game into an existing account. Once you skip this screen, you can't login. You can create a new account, but trying to login into an existing one will result in unpleasant error.

Super Mario Run - Part 1. The Princess from Another Castle

The game does not ask you if you want to overwrite your current progress and it doesn't provide a logout or reset option. The only way to link once you're past the second screen is to remove the app from the device and install it again from the App Store.

Super Mario Run - Part 1. The Princess from Another Castle

Another already existing functionality, that Nintendo decided to reimplement is User Agreement and Privacy Policy screen. My guess is that when you buy a game for Nintendo console, you might buy it in some local store or on Amazon. Game developer does not have a way to present this information prior to that purchase, so they present it when first launching the game. And fair enough - your game requires always-on internet connection, sends some data back and forth and you want to be clear.

But Apple already provides a way to do this - on the App Store. There are fields where you can provide a full, custom License Agreement or use Apple's standard one. There's also a field to link to your privacy policy. When you download anything from the App Store, it's asumed that you agreed with those terms. Unfortunately Nintendo decided to use the custom license field to provide unclickable link to one and put those as one of the setup screens. This has pretty much the same legal binding but much worse user experience.

If you don't agree, you can't continue, if you do, you're almost there. You just need to wait for the game to download, of course.

Super Mario Run - Part 1. The Princess from Another Castle

I'm not sure what's happening here. The game on the App Store is over 170 MB, but it downloads another 170 MB of data here. It might make sense in titles like Rayman Adventures which is on the market for a long time and its content changed multiple times, but this is initial release of the game, no new levels or assets were made since Nintendo released it.

Currently Apple allows apps under 100 MB to be downloaded over 3G, if you want to download something larger you need WiFi. But since the game is almost twice as large on the App Store, there's no good reason not to include those extra 170 MB which is being downloaded here and skip another screen.

After the download finishes, you're presented with request to allow Push Notifications from the games, to get notifications like updates about events. It could be worse, some apps just throw all requests for notifications, address book, camera at once, without any explanation. But it's considered a bad design if you ask for those, without valid reason and at the right time.

Here, it just feels lazy and I would probably push the question after you initially experienced the game and with more specific explanation what are the benefits of allowing Push Notifications. Especially since if user won't allow it on initial request, there's additional hassle to enable them in the future.

Finally

After all those screens, you finally get into the real game. Kind of. Super Mario Run follows a poor game design of unskippable intro/tutorial.

The problem with tutorial is that it's a lazy approach to teaching user how to play the game. You are pointed to elements on the screen that you have to touch in order to continue. You can't skip it, you can't tap anywhere else to dismiss that "hint", you just have to follow the instructions on screen, launch first level and only then you can quit it. While tutorials like this are important for people not similar with the game, they are often boring and irritating to people who just want to play. Especially that Super Mario Run isn't that complicated.

Nintendo used to be really good at doing it the right way. The original Super Mario on NES had brilliantly designed first level which uses the level design to teach everything the user needs to know.

Others also do it, all the time. Thomas Was Alone used story-heavy way of introducing player to its rules, while making the experience fun. Super Meet Boy uses similar approach by designing small levels which, while challenging you, also teach you moves and skill that you'll need to finish the game. Portal does a mix of both - requiring precise, complicated skills, but mixing it with game narrative.

And one could argue that all those games need more explanation and mastering of controls to enjoy and beat.

The conclusion

All those screens and interactions is something that you experience when you first run the title. It's a hard wall you hit for initial experience. Especially if you realise that with none to very little effort, most of those could be avoided.

You could download the app from the App Store, tap on the icon and you could just start to play and enjoy the game. No need for region selection, no need for account setup (or not as a first thing you're asked for). You already agreed the the license when you downloaded the game from the App Store. And you could have already all initial levels and assets within the game, so you could play the "demo" part of the game, without seeing additional download screen.

Overall it's hard to say that the initial experience is good. You do have to struggle to play the game. And the fact is - the game is pretty nice from that point on. It's something that you can enjoy and is well prepared for mobile experience. Levels are bite-sized so you can play on transit or when you have few minutes to spare. There's fair amount of replay-ability built in: both to get better but also by having multiple challenges presented. There's an easy to learn, hard to master approach in the game.

It could have been much better experience if a little more extra effort would be put into it.

For me it's an indication that Nintendo didn't do their homework. At least not well enough. If you're in charge of both - making games but also making a whole hardware platform those games run on - you make the rules. For example the Wii is largely experienced the way Nintendo designed it to be experienced. Accelerometers in the pads are used as steering wheals instead of D-pads or analog sticks. You jump and wave your hands in front of the TV instead of just sitting on the couch. People are buying buying it, because of that experience. They don't start the console to play Battlefield, they have it as a party entertainment system.

If you are in charge of designing these experiences from start to finish, you might believe that same rules apply everywhere. You might believe that Mario defines the experience on iOS, not the other way around. But the truth is people buy their phones for different reasons they buy your family console. Your game is just a few minutes distraction from 20 other things that they use their mobile device for. You have to adapt, change your way of thinking, change your approach, look at what other people are doing well on that platform and think if you could use it in your product.

I'm afraid that Nintendo might lived in their own bubble. A bubble filled with people willing to buy 8th edition of Mario Kart or 8th generation of Pokemon game. They lived in it long enough so they failed to realise how much the world have changed, how mobile market is different and that "the good old way" might not be that good anymore.

That is also partially the reason for low ratings of the game, but that's a story for Part 2.

Now imagine - you could just tap that red icon on your screen and Mario would just run!

]]>
<![CDATA[11 things I learned at Mobile Central Europe 2016]]>

Last week, along with a couple of colleagues I attended the Mobile Central Europe (MCE^3) conference in Kinoteka, a sprawling cinema complex in downtown Warsaw.

This year saw the third installment of the conference, focused on bringing designers, developers, and everyone in between together to share knowledge with people

]]>
https://blog.pendowski.com/11-things-i-learned-at-mobile-central-europe-2016/6080546795a3cc2495d8b844Fri, 29 Apr 2016 18:22:30 GMT11 things I learned at Mobile Central Europe 2016

Last week, along with a couple of colleagues I attended the Mobile Central Europe (MCE^3) conference in Kinoteka, a sprawling cinema complex in downtown Warsaw.

This year saw the third installment of the conference, focused on bringing designers, developers, and everyone in between together to share knowledge with people from around the world.

The number 3 in the name of conference had one additional meaning. There were three talk tracks, each one with a separate overarching theme: Engineering, Design−both of which we already know from prior editions of the conference−and a new one, introduced only this year, Product. I decided to leave my developer comfort zone and learn something new from all three tracks.

Day 1

Ash Furrow - iOS Checkup

Although marked with the Engineering tag to denote that was intended for developers, it turned out to be a very different kind of talk. Ash decided to touch upon the more human part of what we do. He spoke at length about responsibility and communication between developers and the people around us.

It's important to remember that the language we use affects our reality (something we at Macoscope found important enough to inscribe in our Core Values). Whatever you write will be perceived as either good or bad, since neutrality does not exist on the Internet. It's why we should take extra care with what we write to other people and try to be extra positive with our feedback.

In a world of asynchronous communication, GitHub issues, and round-the-clock Slack conversations, it's even more important to make an effort to engage in more direct interactions with people. Just use Skype or Google Hangouts (the latter has a recording feature, giving you the ability to review the footage later or even attach to your issue tracker), because talking with someone face to face conveys much more information than any email or instant message you can send. Especially if there's a language barrier between the interlocutors.

Boaz Katz - Prioritization is Hell

Boaz Katz, the product strategy guy from Bizzabo, spoke about the importance of and struggles with prioritization in any product you're trying to create. Lack of well-considered and transparent priorities causes chaos and interrupts the flow of shared knowledge inside teams.

Since most products have to satisfy the needs of very different people, it's rarely just one cause animating a company, one singular direction in which it’s heading. That's where priority buckets come in. Any task should have at least one "tag" assigned to it which should state the reason for the completion of this particular task. Is it an Improvement, something that brings in New Value, is it a Sales Enabler or part of a Crazy Experiment? This allows us to spend more time thinking about the business value of the task. Are you changing your infrastructure because it improves the experience of your end user or just because you want to?

The next step is to assign percentages to all those buckets, based on company strategy for a given quarter. Are you losing customers because your app is too buggy? Assign 40% to Improvements. Are your customers happy, but sales have been dwindling? Maybe 35% for the Sales Enablers bucket will help with that. Just don't make those percentages equal−concentrate on a clearly defined for this quarter and produce data that you will be able to analyze at the end of it to check whether the decisions you made were good.

Natasha Murashev - Practical Protocol-Oriented Programming in Swift

Most of us iOS devs still come from an Objective-C background. That means that we used a hammer for so long that now we tend to see nails everywhere. Even if some of them are actually screws and Apple gave us a swifty looking screwdriver in the meantime.

Natasha tried to demonstrate how some everyday problems we used to solve by banging classes with categories in Objective-C can be solved in a clearer and simpler manner with protocols, protocol extensions, and generics in Swift.

Jonathan Flint - What I Cannot Create, I Do Not Understand

Jonathan told a story of two projects that he was involved with in which he examined where does making a product intuitive and really understanding how everything works meet.

The first project explored the potential of using drones in different social, political, and cultural scenarios of our everyday lives. Jon spoke about how along with figuring out the concepts behind using drones in different environments the company organized workshops with different groups of people to bring both concepts and actual, physical drones closer to people and to observe how they see and work with that technology.

The second case told the story of the BuggyAir prototype. An Internet of Things device, BuggyAir was a small suitcase furnished with with GPS and a bunch of sensors that made it possible to monitor the air quality in areas where people wanted to take their children for a walk. This was supposed to give parents new valuable information and allow them to change routes on-the-fly in order to to decrease exposure to elements like nitrogen dioxide or particulate matter.

Matteo Lai - Making a Medical Wearable End to End: Empatica War Stories from the Trenches

Empatica is a company that released two wristbands allowing people to track their activity and stress levels, and therefore their mental and physical wellbeing, in real time and to find patterns based on that data. Empatica concentrated on people suffering from epilepsy and tuned their hardware and software to the point that they were able to predict upcoming seizures allowing to user to prepare for it.

But the company didn't plan on this from the very beginning. Matteo Lai, the co-founder and CEO of the company, spoke about changing directions and adapting to the realities around them in order to get to where they are today. A long journey from a software and data company to one developing the innovative and well-designed hardware products they’re offering now.

Todd Lombardo - Organizational Dynamics of Innovation

The talk started with 3 sheets of paper and a question whether we would invest our money in a company that sell pairs of sock in which 3 socks make up a pair. Almost none of us in the audience would and our reluctance would lead to us losing a shot at a $25M business.

Todd spoke of how different companies try to fuel their innovations. What works, what doesn’t and why. And, finally, how we should all be designtists - people who can not only design and innovate, but also apply the scientific method to experiment and test our hypotheses.

Day 2

Kyle Fuller - End-to-end: Building a Web Service in Swift

When Swift was open-sourced a couple of months back, a handful of companies and individuals got really excited and started working on tools that would allow developers to write backend software in this new, exciting language. Kyle is one of those individuals and in his talk he went through the whole process of creating a backend in Swift, from writing the server code through testing, deployment, and up to monitoring it.

The bottom line of the presentation is that the interest is there, even from big players like IBM, but choosing to wait a few more months for the stable, 3rd version of Swift before doing some real production work would be the wisest option. Now, however, is the perfect time to create libraries and tools. Just remember to think in Swift, rather than just port solutions and ideas from different platforms and languages.

Ramzi Rizk - Everything Breaks, or How I Learned to Stop Worrying and Embrace Change

Starting off with a Dr. Strangelove reference, Ramzi, the CTO of EyeEm, spoke of what he learned from running his photography community and marketplace. He discussed the ideal times for finding and hiring people, ways of organizing them, and working with products in a world where there wasn’t even a Facebook a mere couple of years ago. How important it is to adapt to the ever-changing realities of the market.

Ramzi threw some cold water on the engineers assembled in the room when he stated that the technology stack doesn't matter in the long run. In a world where everything changes all the time, you can't prepare for what you do not know. Speaking from his experience, Ramzi claimed that it might be more efficient to deal with an imperfect setup, by commenting code, disabling features, just to handle a difficult situation in the future, rather than to try and cover every possible case ahead of time.

Stefaan Top - Innovative Technologies and Bright Ideas. Now What?

Stefaan from AllThingsTalk tried to demystify the Internet of Things to people not interested in the subject. He also explored the path which the IoT ecosystem may take in the near future, both as a large movement and within each company.

The subject is bigger than just the hardware itself. That data collected by IoT devices is basically meaningless without proper context. That's something which ties in with what Mateo Lai spoke about the previous day−all your data is meaningless if you aren't able to use it properly. The whole IoT movement will make sense only when all those small devices, built by different manufacturers, can interact with each other.

That's the problem which the AllThingsTalk team tries to tackle with their SmartLiving platform. It brings that communication, connection, and interaction between devices closer to developers. The presentation concluded with a demo in which I played a major part. Who said that having a QR code reader on your iPhone is useless? ;)

Naren Shaam - PODs - New organization to optimize your team, improving autonomy and alignment

A couple of months ago, the GoEuro management realized that handling over 100 engineers and their everyday work is a task more difficult than it has any right to be. That's when they decided to split their teams into separate, mostly independent and self-sufficient PODs, a concept originally laid out by Facebook.

Each POD is responsible for one main goal, a feature to improve or implement. Instead of having a backend team, a product team, and so on, the company is split into separate groups with each responsible for things like search or international connections. Each of the PODs has a complete set of people required for the task laid out before it, so there are no excuses and finger pointing when a POD fails to deliver on its promise.

The new setup works better and yields higher responsibility and ownership of the end product inside the PODs. But the approach is not without its limitations and challenges, including the emergence of a sort of silo mentality due to increased strength of individual teams.

Steli Efti - If you don't understand your user/customer, you're fucked

A very spirited presentation from Steli covering the basics of creating and maintaining a product or service. Basics that a lot of people, even seasoned entrepreneurs, tend to forget or ignore, which in the end may cause businesses to fail.

The bottom line is that you always need to remember that whatever we do as a business, we do for our clients, not just to satisfy our need to create. Talking and working with existing and potential users to find a better way to fulfil their needs is absolutely essential. The ideal way would be to do it in person, because it gives you much better feedback than studying analytics or reading survey results. Focusing on a small group of people, satisfying their needs and then making that group bigger is much better (and more sane) than trying to solve the problems of 8 billion people all at once.

Day 5 - My summary

This year I went to talks from all three tracks and loved it, I managed to learn a lot of new stuff from outside my area of expertise. The new Product track was really strong, with stories ranging from good business practices to retrospectives of real products and challenges faced by teams behind the most interesting and innovative solutions on the market.

From what I heard, the talks I haven’t seen were at least as interesting as the ones I attended, so I’m planning on watching footage from all of them to squeeze even more from this 2-day-long journey. Truth be told, I can't wait for the 2017 edition of Mobile Central Europe. Hopefully, it will once again take place in the warmer months, so I will be able to meet even more interesting people−on stage as well as in the crowd−with beautiful sun shining down on us as we leave the conference hall, tired but inspired.

]]>
<![CDATA[Plugin Architecture in Swift(ish)]]>https://blog.pendowski.com/plugin-architecture-in-swift-ish/6080546795a3cc2495d8b83aMon, 11 Apr 2016 18:10:01 GMTPlugin Architecture in Swift(ish)

I have a strange thing with plugins. Every application which has plugin support is much better from the one that does not, even if the core functionality is smaller. It's about the potential of community being able to tweak and bend the application to their own needs. It's partially a reason why I tried to implement plugin architecture in most languages and technologies I worked with. Now it's time for Swift.

Luckily Cocoa has already a plugin architecture in place, it's just a matter of using that functionality in your own app. The architecture uses bundles, which connects code and resources into one package. Your application is one of those bundles and you should be used to interact with its resources using NSBundle.mainBundle() object. But it's much more powerful mechanism, which allows to dynamically load and use code, which is available in another bundle.

To create simplest plugin architecture we need a contract between host application and every plugin. They will be compiled independently, potentially by different developers, so we need to agree on how much they need to know about each other. A good construct to establish that contract is a protocol. We'll start with a very simple one:

// Plugin interface protocol

protocol PluginInterface
{
	var name: String { get }
	
	init()
}

Each plugin will have it's name, which we will be able to print after loading it the binary. We also add init() method, so we can create an object of it.

Humble Bundle

To create a plugin we need to create a Bundle target, which for now we can just as well add to the main app project:

Plugin Architecture in Swift(ish)

With this target you'll get only an Info.plist file, which among few other things specifies a Principal class. The field is a name of a class, compiled in the bundle's binary, which will act as a communication interface between code from inside the bundle (in our case - the plugin) and outside of it (in our case - the host app). This string field maps to NSBundle's property called principalClass which returns AnyClass? type.

If you leave Principle class field empty or fill it with a string that doesn't map to any class it returns first from all classes compiled in the bundle. It's useful if you forget that the type name is actually SamplePlugin.SamplePlugin (or $(PRODUCT_MODULE_NAME).Class_Name in general), but if you're making a bundle with more complicated class structure it might be tricky to debug why principalClass is some other random class, not the one you thought you specified.

All that's left is to add the PluginInterface.swift file to newly created bundle and implement a class with that protocol

class SamplePlugin : PluginInterface
{
    var name = "SamplePlugin"
}

Everything builds successfully, for step 2/2 - loading it.

Lock and load

With NSBundle in our hands, it all boils down to locating a bundle - for now we'll use the folder the app is built:

let path = NSBundle.mainBundle().bundlePath

pluginHost.loadPluginsFromPath(path.stringByAppendingString("/../"))

And then loading a bundle, finding the principalClass and instantiating an object of that class.

// Loading Swift protocol from another bundle

if let bundle = NSBundle(URL: url) where bundle.load() {
	
	if let cls = bundle.principalClass as? PluginInterface.Type {
		let plugin = cls.init()

		plugins.append(plugin)
	}
}

There's just one problem here. The casting to PluginInterface.Type fails so we'll never get to initialize our object. That's the difference between dynamic Objective-C and type-safe Swift. Even though we're using the same protocol file, for Swift those are two different types. I tried few different approaches, using dynamicType field (initially suggested by Xcode), unsafeBitCasting, but at the end of the day I always saw unsatisfying nil as a result. I just couldn't convince Swift to trust me that the type in both bundles is the same (which kind of make sense).

With a little help from my friend

Does it mean it's impossible? Of course not! It works with Objective-C and I know few apps that though themselves are written in Objective-C, use plugins made in Swift.

First try was to mark the protocol using @objc. But provided the same outcome - casting resulted in nil. Even when the plugin protocol extended NSObjectProtocol, which shouldn't make any difference other than that it should act like a normal Objective-C protocol.

Surprisingly - doing just that - declaring the protocol in Objective-C itself and accessing it in Swift using bridging header made it work.

// Objective-C version of the protocol

@protocol PluginInterface <NSObject>

@property (nonatomic, copy, readonly) NSString *name;

@end 

Now since have a protocol in Objective-C, we have to modify the loading code slightly (and get init method "for free" from NSObject).

// Loading Swift class implementing Objective-C protocol

if let bundle = NSBundle(URL: url) where bundle.load() {
	
	if let cls = bundle.principalClass as? NSObject.Type,
		plugin = cls.init() as? PluginInterface {

		plugins.append(plugin)
                                
		print("> Plugin: \(plugin.name) loaded")
	}
}

And voila! We're getting the right object after casting to NSObject and PluginsInterface protocol from Objective-C bridge.

Plugin Architecture in Swift(ish)

It's just a beginning

Now when code is properly loaded and we're able to interact with code from another bundle by both reading and "writing" to it. We can add actual functionality (even when it's a simple one).

// More Swift-friendly plugin protocol

@protocol PluginInterface <NSObject>

@property (nonatomic, copy, readonly) NSString * _Nonnull name;

- (NSString * _Nullable)convertString:(NSString * _Nullable)string;

@end

And with that new protocol we can build another simple plugin (in a separate Bundle target):

class Base64Plugin : NSObject, PluginInterface
{
    var name = "Text to Base64"

    func convertString(string: String?) -> String?
    {
        if let string = string,
            stringData = string.dataUsingEncoding(NSUTF8StringEncoding) {
                return stringData.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
        }

        return string
    }
}

I'll make an example out of you

To see it all in action, we just have to create a single window application, drag a NSTextView, add an NSMenu Plugins item and connecting them all with few lines of code:

// Loading and using plugins to manipulate strings

func applicationDidFinishLaunching(aNotification: NSNotification)
{
    let path = NSBundle.mainBundle().bundlePath
    
    pluginHost.loadPluginsFromPath(path.stringByAppendingString("/../"))
    
    pluginHost.plugins.forEach {
        let menuItem = NSMenuItem(title: $0.name, action: Selector("pluginItemClicked:"), keyEquivalent: "")
        menuItem.representedObject = $0
        
        pluginsMenu.addItem(menuItem)
    }
}

func pluginItemClicked(sender: NSMenuItem)
{
    let selectedRange = textView.selectedRange()
    
    if let plugin = sender.representedObject as? PluginInterface,
        string = textView.string as NSString? {
            
            let selectedString = string.substringWithRange(selectedRange)
            if let convertedString = plugin.convertString(selectedString) {
                textView.replaceCharactersInRange(selectedRange, withString: convertedString)
            }
            
    }
}

The code will load all bundles which are in the same directory as the app (again - for testing purposes - there are actually better places to load from), fill an menu bar menu with actions for each loaded plugin and use method from the protocol to manipulate selected string.

That's just a beginning but should be enough to get you started. I still want to investigate if there's a way to avoid using Objective-C bridging header, since that's one of the biggest annoyances for now. Maybe there's some bug in Swift, especially that using @objc didn't work. But that's a subject for another post.

For full example source code visit my github repository.

]]>
<![CDATA[Ringodoro - self made Pomodoro timer]]>https://blog.pendowski.com/ringodoro-self-made-pomodoro-timer/6080546795a3cc2495d8b839Tue, 15 Mar 2016 19:51:44 GMT

Recently I got interested in electronics again. By "again" I mean that it always interested me, in a way that children want to build robots and know how things work. Even with that curiosity it somehow never stuck with me for longer than a semester at school. A few weeks ago, after connecting Spark Core to some LED strip controller and frying the whole thing I decided to give it another shot. The plan is to learn some basics to not break another board again and make few simple projects.

Ringodoro, my first project, is a pomodoro timer which uses multicolor LEDs to show the passage of time. Pomodoro is a technique in which you maximize your productive time by splitting it into roughly half an hour chunks - 25 minutes of work and 5 minutes of rest. That coincides with human's capacity of concentrating on one task effectively. I've heard of the technique multiple times and used it occasionally, but never gave it a proper chance to decide if it works for me. Ringodoro allows me to learn some electronics and test the technique at the same time.

The hardware part of the project is quite simple - a Neopixel RGB LED ring and an Arduino-compatible board to control the lights. The cool feature of Neopixels is that you can control each individual LED "pixel" with just one output pin (for the whole ring), using real time data protocol. My idea is to show passing time, using colors of each LED on the ring. The colors are based on the appearance of a tomato. When it's time to work, the ring fills with red lights as the time passes by. Then, after 25 minutes, the ring fills with green lights, letting you know that it's time to rest. Everything ends with an small dancing tomato animation.

I wrote and rewrote the firmware using few different boards. Starting with the "standard" Arduino Uno, then ESP8266-based board (which would allow to create a REST API for it) and finishing with much more suiting, tiny Digispark board, perfect for minimalistic design of the whole project.

The code needed few changes along the way for each board, but the most challenging part was to fit it on the ATtiny85. The MCU has only 8KB of memory, with only about 6KB for user program. My code consists of Ringodoro controller class, timer class, button handler, Neopixel library and sketch that connect all these things together. Seems very little but at some point it took 103% of available space and had to do some hardcoding, just to fit on the small chip. Interesting experience after working with current generation mobile devices or computers with lots of memory.

I painted the PCB black for more awesomeness, added a button to the initial design to be able to toggle between modes (idle animation, work, rest) and soldered everything together. And there it is - my first electronics project in a long time - a simple pomodoro timer.

All that's left now is to force myself to use it properly during work. I also want to clean up the code, since learning new stuff like this made it a bit too messy for my taste.

Lessons learned?

  • It's much harder than "normal programming", where we're spoiled with software and hardware.
  • At the same time it's much more accessible than I remember. Lots of good boards and components, widely available on the internet.
  • It's much closer now to building from Lego blocks, than having to be electric engineer to do anything.
  • Debugging is much harder, especially on a board with no Serial support (like Digispark).
  • And interacting more directly with physical world can be really surprising. Single press of a button, can be read as multiple, just because of mechanical construction of one and you have to be prepared for that.


Testing Ringodoro while trying out standing desk at work

]]>
<![CDATA[The Double Edged Sword of Duolingo]]>

About four months ago I decided to try to learn a new language. The reasons are a bit more complicated but I want it to give it a real shot this time.

Since it's a try and I wanted to increase the chances for success by lowering the

]]>
https://blog.pendowski.com/the-double-edged-sword-of-duolingo/6080546795a3cc2495d8b846Wed, 20 Jan 2016 21:27:44 GMTThe Double Edged Sword of Duolingo

About four months ago I decided to try to learn a new language. The reasons are a bit more complicated but I want it to give it a real shot this time.

Since it's a try and I wanted to increase the chances for success by lowering the entry price, I chose Duolingo. It's easier to find five to ten minutes every few days, than to find a straight hour or two each week. The language - Spanish, no real reason there, it would be somewhat useful and it was more about testing the tool and my ability to learn in such way.

And it worked. I got hooked in, did at least the minimal effort each and every day, sometimes more if I had more time or will to learn more. I did it for 112 days straight. I was proud of that. And then this happened.

The Double Edged Sword of Duolingo

I forgot. I got the push notification, I even got the email, but I was busy and dismissed it and forgot. 112 days streak reseted, I'm starting from day 1.

Gamification

And that's my problem with Duolingo's system. It introduces a gamification system which in theory should motivate users to continue learning. You earn XP when you learn, you earn currency if you get unbroken streaks. You can even place a bet, spend 5 currency to earn 10, if you'll learn every day. Something I did from time to time after I realized that I actually enjoy learning Spanish with the app.

But using game mechanics it a tricky thing. It can be double-edged sword. Difficult games can be both - very rewarding and discouraging. Same with system using game-like mechanics, like Duolingo. I heard stories of people hitting some high streaks, breaking them and quitting using the app and I was aware it will most certainly happen to me one day. And it did. And I do feel discouraged to continue learning by using the app.

Quick Save

So what's the actual problem here? There are few. I believe the gamification works as a concept and is quite nice, just the way it's implemented could be improved.

First of all, the system is there and there's no way to opt out. You gain XP points, you gain lingots, the chart is large part of your profile. You can even share your progress and compete with your friends. I'm not saying it's a bad thing, just that it's a large part of the experience and as such should be handled with care.

The streaks system is rewarding but not as much as it can be discouraging. Look at the screenshot above. It hurts to see that red icon by itself, compared to repetitive green checks. And it doesn't help that it looks like we did something really wrong, scoring below the last line (as in worse than 0). And when we do, we begin from the start. It's a win some, loose all kind of game.

And I believe that's not the kind of experience when it comes to learning something.

Quick Load

So what to change? I like the gamification aspect of it. It makes learning more fun. The lingots system makes the app feel a bit like F2P title, in a bad way. With this feeling that the creators might designed the system, like such title, finding multiple ways to get your money with in-app purchases, to get the lingots. Maybe it's a matter of naming things. Shop might not be the best name to call a section of the app (although technically it's correct). Multiple currencies might be replaced with just XP (or just lingots, but I believe XP is safer and not stained that much with Pay To Win approach).

The biggest change would have to happen in the streak section. It can't be constant all-in kind of game. To learn something you need consistency and encouragement, rather than fear of loosing. When you're having a lesson in Duolingo and make a mistake, you don't start from the beginning. Not going further in the progress or even loosing a small portion of that progress is enough as a form of punishment for the mistake and still allowing you to continue. Same logic applies to your overall experience, not just one lesson. Win some, loose some. Create checkpoints along the way, so you don't have to start from the very beginning of the level. Good games takes away some of your points as a lesson to try harder or be more careful next time, but let you with majority of those which you earned previously, by playing well.

In Duolingo the simplest solution would be to cut streaks into smaller chunks. We have those already - weeks, months. Congratulate your users for finishing those, reward them for having a perfect week or month and reset the timer. Worst case scenario, they will have to wait few days to mentally start over, but they don't loose those previous rewards and high fives you gave them. You make those goals much more achievable while still rewarding. You make the failure temporary and short term, something you can recover from much easier if you just try a bit harder next time.

Another solution could be to earn the streak back, by working harder or more. You didn't do your last homework, but you can catch up, by doing twice as much today. That approach would need more tweaks and changes, but the bottom line is not to ruin the player. Balance the carrot and the stick. Make it possible to get back into the game with "I can do this!" approach, rather than "I need another 4 months just to be where I was yesterday" on their mind.

Try again?

Yes - some people get motivated when the stakes are high, when achievement comes with big effort and they either get the perfect score or no score at all. Permadeath games are gaining some popularity lately (again), but usually most games come with a difficulty slider and make the permanent death option for a hardcore level for a reason. And even then, I don't believe that's the good approach when it comes to learning anything. Learning is still work, maybe in a fun way but it's work. Rewards are there to keep you motivated to continue to do that work. Games can get away with harder punishment, because they're mostly an entertainment already.

As for me? Tomorrow I'll still going to learn Spanish on Duolingo. And day after that. Maybe just with a feeling of not so much achieving something new, but catching up. And I have 111 more days of catching up to do.

]]>
<![CDATA[Summer at the Q branch – solving problems of our special agents]]>

Go to post on Macoscope's blog

Wrote a post on Macoscope's blog about small side project we were working on, trying different approaches, failing and learning from our mistakes.

]]>
https://blog.pendowski.com/out-summer-at-the-q-branch-solving-problems-of-our-special-agents/6080546795a3cc2495d8b840Sat, 09 Jan 2016 16:32:49 GMT

Go to post on Macoscope's blog

Wrote a post on Macoscope's blog about small side project we were working on, trying different approaches, failing and learning from our mistakes.

]]>
<![CDATA[One drop of open source]]>

How little is enough to make a difference? Very little.

For as long as I remember I wanted to share some of my work as open source. I had some ideas, even sourceforge repositories for magnificent projects that I planned. I remember small and useful XML parsing library in C

]]>
https://blog.pendowski.com/one-drop-of-open-source/6080546795a3cc2495d8b842Sun, 16 Aug 2015 11:36:06 GMT

How little is enough to make a difference? Very little.

For as long as I remember I wanted to share some of my work as open source. I had some ideas, even sourceforge repositories for magnificent projects that I planned. I remember small and useful XML parsing library in C with no dependencies other than standard library. I remember big plans for JIM and JIL, Jabber Instant Messenger and Jabber Implementation Library, which were never born.

I always had this problem that any work I would publish as open source has to be perfect. I was scared that I'll make some stupid mistake and people will see it. Or maybe even more important, that unless it's something big, innovative, it's not worth sharing.

The truth is a tad different. With dependency managers like Cocoapods or NPM and other, you can see how small your library can be and still be useful. Just combine with a couple of other, smaller and bigger ones and you have a good foundation for an application.

Small categories like FrameAccessor or Color-Utilities live together with AFNetworking and other, bigger libraries in the same Podfile. And for a developer, they all serve the same purpose - solve a problem, no matter how complicated it is and in how many lines of code. They save time and money, therefore they are useful.

We all find problems we solve over and over again. Sometimes we forget that we are not alone in the world. That someone, maybe next door, maybe in some far country, probably have the same problem and we could help to solve it.

I had that with my "recent" open source contribution ObjExC, a category which not only I use almost everyday to make sure unexpected JSON data won't crash my app. Something I struggled with, solved in almost every project until I sat down and made this small category.

Yesterday, I solved a problem which we had on every scrum review session - showing a client on the "other side of the internet", where my finger is on the screen, while presenting a new feature. It's based on Tweaks, which we use in few projects right now, and it's called TweaksPresentation.

Am I the only one using Tweaks and their shake UIWindow sublcass? Of course not. Are we the only ones, which want to give better presentations to our clients, instead of describing "I'm going to tap the thumbnail on the left side of the cell now"? I doubt it! How much time did it take to make the solution and share with the world? Very little.

]]>
<![CDATA[// TODO: Write less comments]]>https://blog.pendowski.com/write-less-comments/6080546795a3cc2495d8b83cSat, 25 Jul 2015 11:10:41 GMT// TODO: Write less comments

I'm not a big fan of comments in code. Especially after few years of writing in Objective-C, which can be very descriptive in its nature.

Every time I want to write a comment, I think if what I'm trying to say could be expressed by the code I'm commenting. Or if case that I'm describing could be handled by the code somehow, by handling nil argument, using id with protocol, provide different method or class.

It's much better to write code than comments, because even if the code is temporary, you have better chance of finding the time to refactor the code later, than to go through some comments and check if they still apply.

Comments do get old really quickly and are often ignored. The more comments you have, the more they become background you ignore. Syntax highlighting doesn't help here either, since in most cases they are dimmed. And for a good reason, you're writing code not comments.

Recently I had a short argument weather it's better to use // TODO comments or #warning. A good point was made, that warnings should be treated as errors. A point that I agree on. But for the same reason I believe that #warning is a good tool to make a TODO statement. Something might not be an error from code standpoint (unless it's an empty method), but it might be an error from implementation standpoint. The feature you're working on isn't finished (in the scope you've planned), until you "fix that error". Mark it as such with a warning, as a reminder for you and your fellow developer to not forget about it, before closing the pull request.

And if it's not the case, if you're just seeing a place to refactor, want to get back somewhere after some other code is ready? Create a ticket in an issue tracking system of your choice, put it in the backlog somewhere and describe it however you like and wait for better times.

Remember - comment with TODO: is a message from your present busy self to your future busy self, thrown in the ocean of code, hoping they both meet at some point in time.

]]>
<![CDATA[Release no-oes]]>

There's the time in a life of every good developer to deliver an application. And a good developer not only ships but also supports. It's great if you can add or tweak some features, but if not, there's also one other category which causes

]]>
https://blog.pendowski.com/release-no-oes/6080546795a3cc2495d8b843Sat, 09 Aug 2014 20:42:00 GMT

There's the time in a life of every good developer to deliver an application. And a good developer not only ships but also supports. It's great if you can add or tweak some features, but if not, there's also one other category which causes us to update our apps.

Every piece of software has bugs. Always. As smart people say:

Ship the right bugs.

Great developers can do just that. They know their product will never be perfect. There's a place and time to just release something to people, let them work with it and give you feedback (and money). No "one more day/week" will make that big difference. It won't push your product that closer to perfection. It's not worth delaying the release. And even then - it will always have some bugs.

It's an important lesson. You can hear this when listening to experienced developers, entrepreneurs and product owners. But there's a thing which a lot of developers has to learn, but it isn't stressed nearly enough. Release notes. Here's a screen of my iPhone from this week:

* It's modified to concentrate on the issue.

Most of those messages don't mean anything. I will say even more. You could as well write "Added some more bugs". It would give the user about the same amount of information and it would be closer to your state of developer's knowledge.

Writing about fixing bugs and improvements is meaningless and lazy. It's not like I think that you added some new bugs on purpose, unless you state otherwise. In general, I expect you fixed some things, maybe added some new features. If you modified your code quite a bit, you probably added some bugs, you're not aware of yet. But with those "Bug fixes", it's a whole other story.

You know what you fixed. And what's even more important - that's something which your users are actually interested in! That's what can even make them excited about your software. And with App Store dynamics - that's how you get organic, good reviews, without ever asking for one.

It's your only, non-intrusive way of communicating with your users. No popup windows, no special code, libraries. You just have to state them in submission process. And it's something your users choose to read. On iOS they actually have to tap on a small label inside App Store, to read what you wrote to them. If they done so, they expect something more specific than just "Tons of bug fixes" or "Improvements". If there are a lot of them - tell about them! It shows how much hard work you did and how much you care about your client's experience and feedback.

It changes how they see your update. From "Oh. They fixed something" to "Yes! They read my e-mail and fixed my problem!". It actually carries some message, some information about your hard work.

So remember - next time you're writing a Change log or Release notes, be more specific. State those changes, fixes, improvements. If you think some of them are too technical, say which view or which feature got better. Maybe how. If there are a lot of them, choose the most important ones (or release more often?). Don't be lazy! It can sometimes be as important as the fix itself.

]]>
<![CDATA[Order of methods for UISlider]]>

An interesting thing I encountered while working on iOS 7 compatibility for one app. The bug was that customized UISlider was displaying the thumb part underneath minimum and maximum tracks.

Some inspection with Reveal showed that the UIImage responsible for this element was actually burried under other slider parts. It

]]>
https://blog.pendowski.com/order-of-methods-for-uislider/6080546795a3cc2495d8b841Thu, 06 Feb 2014 10:30:00 GMT

An interesting thing I encountered while working on iOS 7 compatibility for one app. The bug was that customized UISlider was displaying the thumb part underneath minimum and maximum tracks.

Some inspection with Reveal showed that the UIImage responsible for this element was actually burried under other slider parts. It turned out that the cause of it is the order at which images for those were provided. Pushing the setThumbImage:forState: so that it was last fixed the problem.

UIImage *trackImage = [[UIImage imageNamed:@"ArticleViewTabBarSliderSlide"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 4, 0, 4)];
[slider setMaximumTrackImage:trackImage forState:UIControlStateNormal];
[self.textSizeSlider setMinimumTrackImage:trackImage forState:UIControlStateNormal];
[self.textSizeSlider setThumbImage:[UIImage imageNamed:@"ArticleViewTabBarSliderDot"] forState:UIControlStateNormal];

The possible cause of it might be that on iOS 7 elements are drawn in code rather that using textures like on previous ones. The UIImageViews aren't created and added until they are needed. After that they are added without correcting the order. It looks like a bug but still an interesting small one.

]]>
<![CDATA[Useless App Store reviews]]>

I'm quite proud of the fact that I always try to provide the best app support. That means that I answer basically all emails and I even go as far as to find users which rated the app on the App Store and contact them, if the issue

]]>
https://blog.pendowski.com/useless-app-store-reviews-2/6080546795a3cc2495d8b83dSun, 22 Dec 2013 11:32:00 GMT

I'm quite proud of the fact that I always try to provide the best app support. That means that I answer basically all emails and I even go as far as to find users which rated the app on the App Store and contact them, if the issue seems important.

And that's where it's a little bit tricky. John Gruber, after releasing first iOS app with some friends, recently decided to share his thoughts about rating apps on the App Store (from a perspective of a person which doesn't really need reviews). He's apparently is very annoyed by occasional popups that asks users to rate the app. To the point that he encourages his audience to rate those apps with one star. Mature.

The problem here is that the App Store rating/review system is flawed. First of all - if you don't encourage people to rate your app - you (almost) get no reviews at all. The way people use their devices and apps doesn't support that behavior. It's more and more difficult to find a good app on the App Store, which fits your needs. Which leads to the fact that after checking several apps, people are usually tired of the whole experience and don't want to go to the App Store to leave a review. If they do, it's usually not a nice one, because for some reason the app didn't work as they expected it to work. If the app is fine, they usually leave it for later, telling themselves that they will rate the app if they'll use it after few days. Which of course they (almost) never do. Unless you show a popup from time to time, to ask for the review. It's not an easy task - if you'll show it too soon, you'll never get a review or get one star from John. If you'll show it too late, the person can be busy, too use to the app etc. With just the right timing the person will be excited enough that he/found the "perfect" app after long search, and will express that positive feelings the review.

Now there's another side of this story. There are people out there which uses App Store reviews as a way to reach the support, to say they have problems with the app. Those are most useless reviews there are. Not because they change the way people look at the app, since most of those describe only negative aspects of the app, complaining about bug which happens ones every 100 runs, forgetting about great 99 bug-less experiences. No - I think we can handle that part quite well, making sure there are least bugs possible. The problem is that there's no way for us - developers - to respond. If you have a problem, we want to reach you, we want to get as much information about your problem as possible, to fix the app for you and others.

If you'll leave a review, all we get (assuming we get those reviews somehow) is your very short explanation that something's not working, 1-5 stars and your name. We can't respond to that review, explaining what's happening, saying that we already know about the bug and maybe even already fixed it in the newest update. We can't contact you to, to ask about the details, to give you a promo code for one of our apps, as a thank you for mature review, instead of 1-star rage fest. Those reviews are for other users, and I would even say that they are pretty useless for them as well. You can say that app is bad if it has 90% of 1-star reviews, similar with 90% 5-stars, but with everything in between is not that simple anymore. As a result you won't get that problem fixed, you won't get kind e-mail from us where we could explain/thank/help you. You'll get us read your review and know that one from couple of hundred millions of potential users has a problem. That's it. Even if we really wanted to help (which we usually do), there's just no real way to do it. Even if we would try to to always actively search for you, you're not the only John Smith (or johns16) on the internet and it's infinitely easier to find a way to contact the app support, than harass random Johns on Facebook or Twitter, cause maybe they left that comment on the App Store.

]]>
<![CDATA[Cocoapods unwanted files]]>

I'm really a fan of Cocoapods and I'm learning more and more about them every time we use them in one of our projects (just like in last post). This time I have confirmation that there's no easy little command you can use to

]]>
https://blog.pendowski.com/cocoapods-unwanted-files/6080546795a3cc2495d8b83fSun, 25 Aug 2013 13:17:00 GMT

I'm really a fan of Cocoapods and I'm learning more and more about them every time we use them in one of our projects (just like in last post). This time I have confirmation that there's no easy little command you can use to deal with this problem.

While making one of the projects I ran into a problem - I wanted to use two Pods - AFNetworking and SDWebImage. One is great for network communication, the other for loading images asynchronously from URL. Unfortunately both of those libraries have categories added to UIImageView with the same method names. I wanted to use SDWebImage methods because they did give me a bit more control over how those images are loaded and cached AFNetworking classes was used in several places as well. You can't choose or exclude selected files from Pod - only person creating Pod can do that.

Solutions? A few at least. The one which is the best in the long run and suggested by Eloy Durán is to fork both libraries on github, prefix those methods in both libraries (as you should when you're creating categories which are then used in more then one project) and make pull request. It works, shows the power of OSS and will help other people as well. And I will end up doing that, once I will find a bit of time to make it right. In the mean time, while waiting for pull request to be accepted, you can use your own fork in Podfile by using :git option:

pod 'AFNetworking', :git => 'github.com/YOURUSERNAME/AFNetworking'

But of course since I like to try different things and it's an interesting way of learning Ruby I found another way - by using post_install hook which I also used last time. This time instead of changing configuration for Pods target I modified files which I didn't want to use in my project.

post_install do |installer_representation|
  installer_representation.pods.each do |pod_representation|
    pod_representation.source_files.grep(/AFNetworking\\/UIImageView/) { |element| File.open(element, 'w').write("/* Removed due to conflicting method names */") } 
  end
end

This way every time anyone will use pod install on that project all Pods will be installed as always but files with UIImageView category will be overwritten with comment which says why they are empty. Of course you could try to delete those files entirely but it messes up with the project which you would have to fix (by hand or by another script) and may cause problems if those files are imported/included in some other ones (like one big main header file which for convenience just imports all classes from the library).

]]>
<![CDATA[Cocoapods warnings tips]]>

A short tip for people using Cocoapods. Because of how Pods are working (or should be used as in you don''t edit anything in Pods since it can be overwritten when you use pod install) it's extremely annoying (to me) when I see warnings for

]]>
https://blog.pendowski.com/cocoapods-warnings-tips/6080546795a3cc2495d8b83eFri, 07 Jun 2013 16:26:00 GMT

A short tip for people using Cocoapods. Because of how Pods are working (or should be used as in you don''t edit anything in Pods since it can be overwritten when you use pod install) it's extremely annoying (to me) when I see warnings for Pods project which I can't fix. As a good sport you should always try to get no warnings when compiling your projects and getting those from code you can''t (or shouldn''t really, at least directly in your project) fix makes that impossible.

This can easily lead to just ignoring warning you should pay attention to (in your own code). Here's an easy fix for that.

Pods have pre-install and post-install hooks you can use to do a lot of things - one of which is modify Pods projects file. With that in mind there''s an "Inhibit All Warnings" compiler flag we can use to suppress warnings from Pods project. All you have to do is to add these few lines of code to your Podfile and do the usual pod install:

post_install do |installer_representation|
  installer_representation.project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings[''GCC_WARN_INHIBIT_ALL_WARNINGS''] = 'YES'
    end
  end
end

UPDATE

As Eloy Durán (creator of Cocoapods) pointed out on twitter - you can also use a shorter method of just adding inhibit_all_warnings! or even set that per Pod with: pod 'SSZipArchive', :inhibit_warnings => true

]]>