The screen was a simple UICollectionView
and was displaying items correctly before the migration. The issue was obviously from the Swift 3 to 4 migration. But no code related to that screen had been translated.
While trying to understand what could happen, I began to read about the changes between Swift 3 and 4, hoping that it could point me in the right direction. One aspect was the deprecation of @objc
inference and as you might guess, this was the starting point of the issue.
To improve Objective-C interoperability, the Swift compiler conveniently infers for you a lot of @objc
making your Swift classes, methods and properties available to Objective-C. Even when you don’t want it. This was true up until Swift 3. Starting Swift 4, the compiler team decided to limit this behaviour and infer only for a handful of cases, for several reasons.
When upgrading to Swift 4, Xcode warns you about this evolution and lets you choose between two options: minimize the inference or match Swift 3 behaviour. The former is recommended and was used in my case.
Back to our issue. Looking closely to the Issue Navigator, I noticed a couple of runtime warnings.
implicit Objective-C entrypoint -[MyApp.MyCollectionViewController collectionView:layout:sizeForItemAtIndexPath:] is deprecated and will be removed in Swift 4
So this had to do something with the inference. By using a breakpoint, I saw that collectionView:layout:sizeForItemAtIndexPath:
was never called after the migration.
Fortunately, the compiler team thought about that and provided an environment variable to set in your scheme to help you identify the issue. SWIFT_DEBUG_IMPLICIT_OBJC_ENTRYPOINT
can take three values:
In my case, the value 2 is enough to understand the issue, but it is recommended to use the latest value after a migration while running tests.
Going back to the guilty class, MyCollectionViewController
. The Swift Language Runtime tells me that the method sizeForItemAtIndexPath
is found but cannot be called because it is not exposed to Objective-C.
I could add an @objc
to the method and call it a day, but this does not feel right.
The first thing to fix is the name. sizeForItemAtIndexPath is the old Swift 3 name, so I renamed it to sizeForItemAt.
sizeForItemAt is part of UICollectionViewDelegateFlowLayout
and MyCollectionViewController
uses a custom flow layout but does not declare its conformance to the flow layout delegate protocol. Since this protocol does not require anything (all method are optional), there are no compiler error or warning.
This was the root issue. A missing protocol conformance to UICollectionViewDelegateFlowLayout
. Using the Swift 3 compiler, the method was automatically exposed and so found at runtime. Using the Swift 4 compiler, the method is not exposed and it’s like we did implement it at all.
This last point is extremely important for large codebases. When the program takes several minutes to compile, even for small changes, one can quickly lose patience.
Because Xcode provides a great abstraction for the most used LLDB commands (adding a breakpoint, stepping over calls, ect.), most of the time, only a small fraction of its abilities is in fact, known and used.
Let’s take a look at some basic commands, which will give some super debugging powers.
The most used feature of LLDB is the evaluation of expressions on the current thread.
Using the expression
command, you can query or change the state of a property to alter the final output of the program.
The derivated forms of this command are well known.
expr
and e
are aliases for expression
p
, an abbreviation for expression -
po
, an abbreviation for expression -O --
The second most used feature of LLDB is the inspection of data on the current stack frame.
Using the frame
command, you can query the state of a property in the call stack.
I will focus on the subcommand frame variable
, which show variables for the current stack frame.
Several derivated of this command are also well known.
var
and v
, abbreviations for frame variable
vo
, an abbreviation for frame variable -O
.Note that using frame variable
(or its other forms) is more efficient than expression
to perform a simple inspection, since it uses memory reads directly, rather than evaluating an expression. When using po
, you are evaluating the object as an expression. Be aware of the potential side effects.
Another crucial aspect of debugging is the ability to control the execution flow of the program.
Using the thread
command, you can achieve almost any move you want.
As said earlier, Xcode already provides some graphical abstraction for some of its subcommands (next
, step
, ect.), I will focus on one not widely known.
thread jump
set the program counter to a new address. With this command, you can skip a section of the program, which can be interesting to access a particular point of the program.
As usual, some derivated forms of this command exist; jump
or j
is easier to remember and use.
Let’s assume our program paused and we want to skip two lines ahead.
By doing this, the program may enter in an unstable, potentially unknown, state, since this alters the correct execution flow of the program. Be aware of the consequences.
To control the execution flow, engineers often need to first interrupt the execution, to better understand it. That is what breakpoints are meant for.
LLDB provides numerous subcommands and options to list, set, modify or delete breakpoints.
One is yet poorly known, the ability to run additionals commands when you hit a breakpoint; you can either add LLDB or Python commands and completely change the execution flow of the program with only breakpoints. See the command breakpoint command
to further explanations.
Let’s also mention watchpoints, which are a special type of breakpoint. Watchpoints act as monitors, they will be trigger when the value of a variable changes. This is very useful to identify the culprits of the side effects when the program has state issues.
Before concluding, let’s name some specific commands that you might find useful in some extreme cases. Better to know they exist, if someday you need them.
It is recommended to understand the calling convention of the CPU you are using (AArch32 or AArch64 architecture on iPhone), to fully understand what you are doing.
With the disassemble
command you can disassemble specified instructions and get the assembly code.
With the register
command you can read and write directly into the registers of the CPU.
With the memory
command you can read and write directly into the memory. Make sure you are allowed to access the memory space you are operating with.
We only saw the small part of LLDB power and yet, when used correctly, these common LLDB commands will greatly increase your productivity, allowing to change and inject code. Mixing them with more advanced ones, and you will not feel the need to compile again.
]]>And this is painful for several reasons: we need entitlement, certificate (.p12) and to rely on APNs. You can’t test locally, we need Internet access.
I used to work with NWPusher or lola, small tools, to achieve that purpose.
And I still will use them, but now we have a better one.
Starting Xcode 11.4, it is now possible to simulate remote push notifications directly within the Simulator. All the pain points listed above are now gone as it happens locally.
The best way is to use the simctl
command-line tool, that provides the interface to use Simulator programmatically.
This is the device UUID on which we will send the notification.
You can either specify the UUID or booted
.
To find the UUID of your booted Simulator, xcrun simctl list | egrep 'Booted'
.
This is the APP_PRODUCT_BUNDLE_IDENTIFIER.
You can specify the bundle identifier as an argument.
This is the payload of the notification. Please see Creating the Remote Notification Payload for further documentation.
You can either specify the path of the file as the last argument or use -
for standard input.
We can also omit the bundle identifier by specifying it in the payload under the Simulator Target Bundle
key.
Luckily, this is a happy mistake, which prevented me hours of research. Let’s see what happened.
My enum is defined quite simply, this is a C-style enum with three cases.
At some point in my scenario, I instantiate my enum using init(rawValue:)
and I switch over it, handling each of the three cases.
When writing my tests, I made a typo leading to an instantiation of MyEnum
with the value of -2. This worked.
After some digging, I understood why. Formally, Objective-C allows storing any value in an enumeration as long as it fits in the underlying type. Since -2 is an integer, it works here.
The crash happens when I switch over this value. Since I handle only the “declared” cases of the enum, the program traps at runtime. My non-frozen C-style enum is the culprit.
My project still uses Swift 4, an element I forgot to mention earlier. This is why no diagnostic is produced by the compiler, either warning or error.
To handle this, we have three solutions.
To catch this, we can include a safety net, a way to safely handle unexpected cases, for example, future ones.
Since my switch is exhaustive, this solution takes the form of adding a default
case. And this works.
However doing this, we produced another unwanted side effect. Imagine if I choose to add another case to my enum, MyEnumFour(4)
. The compiler will no longer produce a diagnostic error, stating that this new case is not explicitly handled in the switch, since the default
already handles it.
To remedy that, we can use @unknown default
. This solution combines a catch-all and also alert us if all known elements of the enum have not already been matched.
Enums come in two forms: frozen (NS_CLOSED_ENUM
) and non-frozen (NS_ENUM
).
Another solution is then to choose using a frozen enum. The compiler will recognize this and will not ask to implement a default
case.
But there is a catch.
You are making a promise. The promise your enum will never change in the future. You still can add new cases to a frozen enum, but you are breaking that promise. And the consumers of your enum will remember that, believe me.
Of course, you can only choose this solution if you’re in control of the enum. For example, you can’t choose this solution for an enum of Apple.
Since Swift 5 (SE-0192), the diagnostic take the form of a warning.
Where this diagnostic will be raised, you can add @unknown default
to handle the unexpected cases.
Be advised, no diagnostic will be produced if your enum already has a default
case.
_TFC16MyProjectExample7MyClass10myFunctionfS0_FT1xSi_Si
This is the result of the object code, and is called name mangling. The good news is, Xcode embed a tool to retrieve the original form. But first, let’s dive a little into the concept.
In computer programming, when we wish to compile our program, the compiler goes by a numerous steps before producing a working executable file. The last step involves the linker, which will try to resolve all the references of the entities previously compiled (object code), and merge them into your executable file.
Unfortunately, nowadays, our programming languages evolved and supported a ton of great features such as function overloading or namespacing. But these two features make the life of the compiler impossible. How can he know which implementation link at compile time?
We humans do not even have unique name. Odds are there at least one other person who has the same name as you on this Earth. If you have a common name, the odds are skyrocketing. How can we differentiate you from the others you? With others contextual information, such as current location, birth place, etc.
Same applies in programming. Name mangling solves the problem. With this technique, the compiler encodes additional information on the function (think metadata), creating a unique name for it.
For example, dSYM files contain theses mangled symbols.
Let’s take a quick closer look on how this works for Swift. If you ever wrote a parser, it will be easy. You just have to know the rules.
_TFC16MyProjectExample7MyClass10myFunctionfS0_FT1xSi_Si
Component | Meaning |
---|---|
_T |
The start of a Swift symbol. |
F |
The symbol type is a function. |
C |
The function happens to be inside a class. |
16MyProjectExample |
The name of the module containing the class. The number at the beginning is the length of the following string. |
7MyClass |
The name of the class. The number at the beginning is the length of the following string. |
10myFunction |
The name of the function. The number at the beginning is the length of the following string. |
f |
This tells the parser we are entering the function. |
S0_ |
The type of the first parameter, the class instance (MyClass ). |
F |
This tells the parser we are entering the parameters list. |
T |
This tells the parser we are entering a parameter tuple (name, type). |
1x |
The name of the parameter. The number at the beginning is the length of the following string. |
Si |
The type of the parameter, Swift.Int . |
_Si |
The return type of the function Swift.Int . |
All the rules are on the Swift repo, if you want to keep having fun.
The good news is, you can easily unmangle your string using a simple command, included in every Xcode:
or you can use the online demangler!
]]>The Japanese imperial calendar is quite unique. It is based on the legendary foundation of Japan. The calendar works like the Gregorian, except for years. Years are based on the reign of the current Emperor. When a new emperor access power, years go back to one. With each new emperor, a new era begins.
For example, this piece of code will show 平成31年5月5日
. Which is wrong. Why?
The Hensei era (平成) ended the 30/04/2019 and a new one began the 01/05/2019, the Reiwa era (令和). The corretc displayed date should be 令和1年5月5日
.
The fact is, with these non-deterministic calendars, software manufacturers need to update their system with this new era to support it. For example, Apple issued the following in their latest beta software:
Support for the Reiwa (令和) era of the Japanese calendar, which begins on May 1, 2019, is now available. The first year of Japanese-calendar era is represented as “元年” (“Gannen”) instead of “1年”, except in the shorter numeric-style formats which typically also use the narrow era name; for example: “R1/05/01”. (27323929)
— iOS 12.3 Beta 4 Release Notes, macOS Mojave 10.14.5 Beta 4 Release Notes
At the moment I do not own a device with either of these betas, so I could not verify the update.
The Gregorian calendar also uses eras, except it doesn’t change often. It’s the same for 2019 years, and counting : AD (Anno Domini).
Since the imperial Japanese calendar heavily relies on eras, we need to take that factor in account when building our products.
Let’s go back to our previous example. The code uses the predefined format styles for dates property provided by Apple, dateStyle
. This property already considers the specifics of each calendar to display a properly formatted date.
Based on the values of the
dateStyle
andtimeStyle
properties,DateFormatter
provides a representation of a specified date that is appropriate for a given locale.— DateFormatter (Apple Documentation)
Calendar / Locale | ja_JP | en_US |
---|---|---|
Japanese | 令和1年5月5日 | May 5, 1 Reiwa |
Gregorian | 2019年5月5日 | May 5, 2019 |
However, what happens when we want to use our custom format without considering the locale ?
We do this using the dateFormat
property. Replace the instruction containing dateStyle
with the following:
Calendar / Locale | ja_JP | en_US |
---|---|---|
Japanese | 05/05/0001 | 05/05/0001 |
Gregorian | 05/05/2019 | 05/05/2019 |
We broke the era display. It only works for the Gregorian calendar. We don’t know what first year of which era we are talking about. We could use the following date format dd/MM/yyyy G
to display the era, but it would be ugly on Gregorian dates.
The only time you’re allowed to use a custom dateFormat
is when you work with fixed format date representations, such as timestamps (RFC 3339).
In any other case, especially if the date will be displayed to the user, you should use the dateStyle
and timeStyle
properties.
Japan is not the only country with another calendar (China, Thailand, India, etc.), so let’s keep the dates simple!
]]>While figuring things out, I decided to document my solution, so that others could do the same, without the burden part.
Continuous Deployment is the practice of deploying small changes frequently — rather than deploying a big change at the end of a development cycle. For example, GitHub deploys into production about 80 times a day.
The goal is to build healthier software by developing and testing in smaller increments. The same apply to content delivering.
Travis CI is a free Continuous Integration service for building, testing and deploying your GitHub projects. The service is free for open source repositories.
Travis allows us to have reproducible and clean builds, notifications, conditionals and a lot more. This is exactly what I want.
Our workflow will be simple, a single job with several phases.
To tell Travis CI what to do, we have to declare a .travis.yml
file. Begin to fill it with the following content.
This is quite self-explanatory. We want to use macOS, our project is Ruby based and we wish to have email notifications. That our environment.
The first phase also takes place, before installing our dependencies, we make our Gems up-to-date.
Next up, we install the dependencies.
In this phase, we build our website.
First, we declare a new type of environment variable. This one is located inside the .travis.yml
file, because there is no sensitive information. JEKYLL_ENV
is a Jekyll variable, JEKYLL_CONF
and LOCAL_FOLDER
are ones of mine.
Coming next, the generation of the blog.
I don’t have tests (yet), but I could have run them in the before_script
phase, for example. If they’d fail, my job would also fail. The build and deploy phases would not occur.
At the moment I use HTMLProofer to check the HTML validity (links, etc.).
$REMOTE_FOLDER
is an environment variable.
We are at the core of the job. Our content is ready, we need to deliver it onto our web server.
Travis offers a plethora of deployment strategies, to numerous providers like AWS or Heroku. But I want to deploy to my own provider, using rsync
through SSH. Fortunately, Travis provides features we can use to achieve that.
Using SSH implies having the private key available in the Travis build. But we don’t want having this highly sensitive information in the GitHub repository. Well, not in that form. So first, we need to encrypt the private key to make it readable only by Travis.
The steps are:
The Travis CLI utility created an encrypted version of the private key and store the decryption key as an environment variable on Travis. It also added some lines to the .yml
file which will decrypt the private key file during the build.
rsync
is a powerful utility for efficiently transferring files between two computers. On macOS, the utility is available through HomeBrew. The package will be installed before our workflow kicks in, at environment configuration. Don’t forget to add these two new environment variables as well.
The last step of our configuration will be composed of three phases:
rsync
does its part, uploading the blog into our web server, securely. I choose to only deploy the master
branch.Same as before, notice the new environment variables. Add the three of them as “Repository Environment Variable”, with the proper values: $HOST
, $USERNAME
and $REMOTE_PATH
.
or here for an up-to-date version.
You can now deploy automatically by pushing new content on the master branch, multiple times a day, and, avoiding any misfortune from using your own machine.
Isn’t that marvelous?
]]>Let’s take an example.
A function that can perform any type of arithmetical operation on Int
.
Let’s assume an operator function might raise an error.
The compiler now rejects us when calling the high-order function, because the signature of performArithmeticalOperation
does not match. We must update the performArithmeticalOperation
signature and surround its calls with a do catch block. Yikes.
And we can’t call performArithmeticalOperation
using the double
function without a do catch block, even if we know the function will never throw! A lot of unnecessary boilerplate code to come.
Swift already embed quantity of higher-order functions and nevertheless we don’t need to surround their call with a try catch block. How is that possible?
Looking closely to their signature, you can notice a strange keyword, rethrows
.
Only a quick section of our favorite book, The Swift Programming Language, mention it:
A function or method can be declared with the
rethrows
keyword to indicate that it throws an error only if one of its function parameters throws an error.
Using this keyword, the compiler is now able to check at compile time if the function parameter will throw an error. double
will never, so it does not need a try catch block. You’ll even get a warning if you add it anyway.
Codable
, transforming APIs into data models has been a blessing. All we need is a simple conformance to the Decodable
protocol, and let Swift do its magic.
But sometimes, we need more control. Quickly, we end up declaring our coding keys and overriding the default implementation of init(from: Decoder)
, to state our own business logic.
Eventually we’ll get exactly what we want. Well, almost exactly.
Let’s say we want to display a list of items, retrieved from an API.
A naive data model and a quick JSON mock. A PlanetarySystem
with a list of Planet
. The JSON data contains a list of PlanetarySystem
.
Executing this code will throw a DecodingError.dataCorrupted
, with the following, rather explicit, debug description.
And this is where the problem lies. We can’t be sure the data will be valid.
Decodable
provides a great way to handle that when the data is a simple object, thanks to optional. But it does not provide one when the data is a list. We either get a full list of items, or an error, if any of the item does not conform to the item’s data model.
What if we want something in between? Like the list of valid items, the items that conform to the data model?
In order to achieve this, we need to build a fault tolerant system, one that will allow lossy decoding of array elements.
The way Decodale
works when processing a collection is simple : it will throw as soon as one of the children’s collection throws. In our example, Decodable
throws at the very end, when reaching the pluto
value. The value is not a part of the enum ; the decoding of that item and so the decoding of the collection. All the data process before is throw away. A fail strategy.
Our first step is to define new strategies. We start simple: either we remove the faulty element, or we apply the standard behavior (fail).
We then need to use the chosen strategy to apply when encountering an invalid element while decoding.
We’ll use a generic enum to represent it. The current decodeItem
method will either:
The second step is a bit tougher. Our strategy is now defined, we want to use it, when processing a collection.
The key here is to rewrite ourselves the decode
method of KeyedDecodingContainer
, to use our strategy.
Hence, the method has the same signature as the standard with an additional parameter, our strategy. By default, the strategy is the standard one, throw an error.
The job is then as described earlier. We iterate over the container, try to decode our object using our strategy :
Updating our example code, and we have a rather simple fault tolerant decoding system.
I love the fact that if all of our objects are corrupted and we choose the remove strategy, we’ll get an empty array and not an error.
This solution works great when using a wrapper object, like the PlanetarySystem
. Not with a plain object like Planet
.
Executing this code will throw the exact same error as before. And same as before, we would need to create a new method, in JSONDecoder
this time, to have something like this :
Unfortunately, we hit the limit of our possibilities. In order to decode the Data
we would need access to the internal _JSONDecoder
type, which is… internal. Without it, we cannot do more, unless rewriting our own JSONDecoder
, which would be a terrible mistake and a waste, if you ask me.
Since September 2017, an issue about this subject is open on the Swift bug tracker, SR-5953. Unfortunately, not much has been made since. So, instead of trying to write your own decoder and if this issue is important to you, feel free to upvote and comment on the issue!
]]>It’s the perfect time to remind everybody that date formatting can be tricky, if you are not looking closely enough.
This will yield the following results:
"29 Dec 2018 at 00:00" (datey)
"25 Dec 2017 at 00:00" (dateY)
Notice the date format: the first use y
, known as year calendar and the latter Y
, known as year-week calendar.
As defined in ISO 8601, a year-week calendar has 52 or 53 full weeks. Meaning a year has either 364 or 371 days instead of the usual 365 or 366 days.
The first ISO week of the year is calculated according to a bunch of properties, and in the end, can begin from 29 December to 4 January. The minimum days in a week is 4 and first day of the week is Monday, according to ISO 8601 specifications. You can retrieve these values from minimumDaysInFirstWeek and firstWeekDay.
For example, 1 January 2019 is a Tuesday. Then the first year-week of 2019 starts on 31 December 2018 and ends on 6 January 2019.
However, if the first day of the week was Thursday, then the first year-week of 2019 would have started on 3 January, and would have ended on 9 January. The first two days of 2019 would have then been part of year-week 53 of 2018.
ISO year 2019 starts in year 2018, meaning the first ISO week of 2019 is also considered as the week 53 of ISO year 2018.
Using the Y
date format, you specify the year-week date format, which needs additional components from the same year-week date format. d
and M
are respectively day of the month and month, but in year based date format. This is why the formatter ignores both of them and only consider the Y
.
Without any other configuration, the formatter only consider the year, in a year-week calendar, and by default uses a zero based index for the day and the week: day 0 of week 0 of 2018 is 25 December 2017.
When using year-week date format, use w
to specify the week of year (ordinal) and e
to specify the day of week.
And you’ll get "19 Mar 2018 at 00:00"
. Why?
We changed the date format but forget the date representation! The formatter is now using year-week date format ; what it reads is: day 1 ($29 \bmod 7 = 1$) of week 12 of year 2018. And this is 19 March 2018.
We need to update the date representation to respect that format.
Day 6 of week 52 of year 2018. Finally!
Typically you should use the calendar year format, y
. Y
should be used only when you specifically need to work with an ISO week date system. This can be the case for fiscal years.
Also, you should only use y
and not yyyy
. Unicode Technical Standard #35 explains this causes a forced padding, which you want to avoid if you work with non-Gregorian calendar. Using y
will also enable forward compatibility.