Débogage par dichotomie

Je suis peut-être le seul à appeler cela ainsi, mais je vais vous faire part de la méthode que j’utilise généralement pour déboguer un programme.

Recherche dichotomique

Le principe est expliqué sur Wikipédia, mais je vais vous l’expliquer avec un exemple, celui d’un jeu où le joueur choisit au hasard un nombre entier entre 0 et 100, et l’ordinateur doit le trouver avec le minimum d’essais, guidé par le joueur qui lui répond «plus petit», «plus grand» ou «gagné!».

L’algorithme consiste à diviser par deux l’intervalle de recherche à chaque coup. Disons que le joueur a choisi le nombre 13, on aura la séquence:

  • Au départ l’intervalle de recherche est [0; 100], l’ordinateur propose le milieu de cet intervalle, soit 0+(100-0)/2 = 50
  • Le joueur répond «plus petit»
  • L’intervalle est réduit à [0; 50], l’ordinateur propose 0+(50-0)/2 = 25
  • «plus petit»
  • L’intervalle est réduit à [0; 25], l’ordinateur propose 0+(25-0)/2 = 12 (c’est évidemment une division entière).
  • «plus grand»
  • L’intervalle est réduit à [12; 25], l’ordinateur propose 12+(25-12)/2 = 18
  • «plus petit»
  • L’intervalle est réduit à [12; 18], l’ordinateur propose 12+(18-12)/2 = 15
  • «plus petit»
  • L’intervalle est réduit à [12; 15], l’ordinateur propose 12+(15-12)/2 = 13
  • «gagné!»


Le travail du code est de prendre des données d’entrée, de leur appliquer un traitement, et d’obtenir des données de sortie. Cela signifie qu’il y a une chaîne de composants (objets, méthodes, fonctions…) qui mènent des entrées vers la sortie.

Vous devez commencer à saisir le parallèle: appliquer l’algorithme de dichotomie signifie identifier la chaîne de composants — l’intervalle de recherche — et inspecter au débogueur les variables. Si c’est correct à un endroit donné, alors le problème se situe en aval dans la chaîne, sinon, en amont.

Par exemple, disons que votre application permette de prendre une photo avec votre smartphone qui sera affichée sur un site web. Or vous prenez une photo et elle n’apparaît pas sur le site. Disons que la chaîne de composants ressemble à ça:

  • sur le smartphone, prendre la photo
  • la redimensionner
  • la convertir en Base64
  • l’envoyer en POST Form au site
  • sur le serveur web, traiter la requête POST
  • reconvertir le base 64 en binaire
  • enregistrer l’image sur disque
  • écrire le chemin du fichier en base de données
  • utiliser l’image lors de la composition de la page web

D’après mon algorithme de débogage, le premier test serait donc d’aller vérifier si on reçoit une requête POST quand l’image est envoyée par le smartphone.

J’ai évidemment simplifié mon exemple, puisque chaque étape ci-dessus est elle-même effectuée par un certain nombres de fonctions ou d’objets. Là encore, j’utilise la dichotomie pour restreindre mes vérifications.

Cette méthode économise des efforts, en évitant d’aller vérifier chaque point de la chaîne.

Finalement, la principale limite de cette technique est qu’il faut connaître suffisamment l’implémentation pour délimiter correctement l’intervalle de recherche.

A static library distributed through Cocoapods and Carthage

I had already delivered a SDK, written in Objective-C, as a Static library and its accompanying header files. After a few months, though, the developers who use the SDK came with the reasonable request to be able to integrate it in their apps using these two popular dependency managers.

Enabling bitcode

The developers also requested that Bitcode was enabled. Apple still does not make it mandatory to submit to the App store, but both Cocoapods and Carthage set the “Bitcode enabled” build flag to YES, so my static lib had to include the Bitcode sections.

Build a universal library

If you’ve ever built a “universal” static lib yourself — one that works both on iOS and iOS Simulator targets — then you know that building one consists in:

  • build the iOS target
  • build the iOS Simulator target
  • combine them in a single library using the lipo command.

Generate the Bitcode

This article explains that when building the two targets, the OTHER_CFLAGS="-fembed-bitcode" must be passed to xcodebuild. And that’s all that is needed.

Surprisingly to me, Bitcode is not an other architecture, but is a section for each architecture instead, marked as __LLVM, __bitcode in the .a file.

To check that the generation was correct, I searched these sections using MachOView, but you may also use otool and grep.


To my surprise, this was the longest part. This is actually not difficult, but there is a catch.

  • In Xcode, create a new project, of type “Cocoa Touch Framework”. This is a Dynamic framework.
  • Put the .a and the .h files in it.
  • Edit the Scheme, and set it as Shared. Otherwise, Carthage won’t be able to build it.

Now the catch: when building the final dynamic framework, Carthage first checks out the framework to the Carthage/Checkouts folder. The important information here is that it does a git checkout; not a copy!

This is implicit but it was not obvious to me. I wondered why my Objective-C symbols did not show in the final framework, while the reason was simply that my changes to the local directory were not taken into account.

One last point: in the Cartfile, it is possible to provide the name of a git branch instead of a version:

This is handy since you don’t need to create a new git tag for each change for it to be taken into account. You still need to commit though.

Finally, in my own repository, I added a Carthage directory which contains the Xcode project for the Dynamic framework. I was not able to add the Framework target to the other Xcode project, because of name conflicts.


Cocoapods deserves its reputation of being complicated and invasive. After reading its documentation again and again, I did what I should have done earlier: find a similar Pod (distributed as a compiled .a) and do the same.

My repo has the following structure:

The Podspec:

Private Podspec

My client does not want anyone to download its SDK, so it is stored in a private github repo, only accessible to a chosen few.

To use the private podspecs, clients only need to write in their Podfile:

The private Podspecs repository itself must be organised as follows:

Finishing words

Nothing was particularly difficult, but it took me a lot of time, because of the lack of information, and sometimes contradictions, particularly for Cocoapods (lot of steps described in the doc are not really needed). Carthage proved to be simple enough but has its own logic.

LuaJIT on iOS

Someone created a fork, which essentially adds a script to build LuaJIT for the several architectures needed by iOS, then combines them using lipo: github.com/cailei/luajit.

I updated the script by taking the latest SDK versions and removing the reference to gcc inside the Xcode bundle:

The script works seamlessly with LuaJIT 2.0.4.

Unfortunately, LuaJIT does not support the arm64 architecture yet.

Speeding up Swift code using an enum

The most significant limitation of SwiftRay is its slowness. A few weeks ago, I had already sped it up by exploiting more than one core. Since my Mac has 4 cores, it ran about 3,5 times faster.

The algorithm

The first version of SwiftRay was built after a book, which I bought the second volume, Ray Tracing: the Next Week. It exposes an algorithm, the Bounding Volume Hierarchy, which consists in sorting primitives — only spheres at the moment — in a binary tree. The principle is that instead of testing if a ray hits any object in the scene, we first test if it hits the volume of the whole hierarchy at all. If it does, then we test for the two halves, then their two halves, and so on, until we get to a leaf of the tree — that is a primitive.

The speed-up comes from the facts that:

  1. a lot of rays won’t hit any object at all
  2. we can get quickly to the object that does receive the ray

Straight from C++

The original C++ code is the following:

A bvh_node inherits the hitable “protocol” (thought there’s no such thing in C++). Its constructor method takes a list of hitables, which are actually the primitives in the scene.

I will spare you the implementation of the constructor, which actually produces the binary tree. When the tree has been built, each bvh_node has a left hitable and a right hitable as children, which either have two children as well, or point on the same hitable.

The following method determines whether the volume (bounding box) of the BVH node is hit. If it is, it calls itself recursively for its left and right children to see which one is closer, or it hit at all:


Let us take a look at my implementation. It is nearly a straight port, with the exception that my Swift code makes use of optionals instead of booleans to tell if there is a hit:


When the optimization slows down computations

I had a problem: images still rendered, but computations were actually much slower ! By a factor of 2 to 40 depending on the scene.

Since I’m only human, my premise was that my code had an error. And yes it had: the code to determine if a Bounding Box was hit was wrong. This would make the process slower. But once fixed, it still was very slow.

My second thought was that maybe building the tree was buggy. This would also slow down by big amounts the code since it would not tell quickly if a volume was hit. It was very hard to see in the debugger, so I eventually introduced Rectangle primitives, so I could draw Bounding Boxes. And to my surprise, my code was correct!

That was the time I thought that profiling my code could provide me an insight, so I fired up Instruments > Time Profiler, to state that the code spent a lot of time into

Searching the web, I learned that “protocol witness” is a virtualization table to solve the conformance to protocol.  That was odd to me. BoundingNode being a struct, I expected to have a straight call to the hit() method, with no indirection.

I watched a video of the WWDC ’16 which proved me I was wrong. Yes, Swift does Dynamic Dispatch on structs when they conform to a protocol.

Once, I learned a little Haskell

A few months ago, I took some time off to learn the basics of Haskell. A lot of exercises in the books have to do with… Trees.

A binary tree in Haskell may be declared as:

In English: A Tree is either a Leaf (with a as data) or a Node with two child trees.

I have watched a presentation which was boring for the most part, but had an interesting point: Haskell’s Algebraic Data types can be implemented as Enums in Swift:

The indirect keyword is a recent addition to Swift (version 3?) and informs the compiler that the recursive definition of the enum is voluntary.

In case you have not yet understood: I am going to replace the struct calling its own method with an enum, to get rid of Dynamic Dispatch.

The new code

I don’t find the new implementation very elegant, since I need the box() method to extract the Bounding Box from the Tree, but I don’t know Swift enough yet to envision a better solution.


I won’t make you wait: yes, the implementation is efficient now. I’m very happy for the improvement, since I was hopping for a speed improvement of 2 times.

Simple Scene

200 x 200 px @ 100 rays/px
without BVH: 10.76 s
with BVH: 11.01 s
speed-up: 0.977 times

In this Scene, there is a slight slow down, which is not surprising since there are only 4 spheres, and hierarchizing them does not provide any benefit.

Sphere Array Scene

200 x 200 px @ 100 rays/px
without BVH: 42.25 s
with BVH:  11.42 s
speed-up: 3.7 times

I designed this scene knowing that the algorithm would be efficient. Removing the rectangles makes it more efficient.

Big and Small Spheres

400 x 266 px @ 100 rays/px
without BVH: 523 s
with BVH: 48s
speed-up: 10.9 times

The algorithm becomes really efficient when there are a lot of objects, which only occupy small portions of the space. The speed up is impressive in this reference scene.


In the last two years and half, I have been working for Meteo Consult on an internal application running on a Mac, to create 3D meteorological maps, broadcast on the TV channel La Chaîne Météo.

A sphere, colour-mapped with NASA’s Blue Marble Next Generation. The Blue Marble measures 86400 by 43200 pixels, which is too big for most hardware, and too long to load anyway.

A detailed texture is needed

One major problem we have since the beginning is how to cover the Earth with a texture, since the texture has to be huge to be detailed enough. Currently we are stuck with a smaller texture, which presents two drawbacks:

  • The Earth is hardly detailed enough, so the minimum altitude of the camera is limited. For example, La Martinique or La Guadeloupe are only small blurry dots. We currently rely on 2D maps instead.
  • Even with its low resolution, the texture takes a lot of time to load on the GPU; about 3 s on my MacBook Pro 2013.

Hopefully, the application runs on a Mac Pro, which has a lot of GPU RAM; but even if we could load a big texture, GPU generally don’t handle textures larger than 16384 pixels, so we would be stuck anyway.


Probably the solution was obvious to you: use tiles, Boy ! Of course, we thought of that since the beginning of the project, and I even tried to make something work, but to no avail. The major difficulty was to determine which tiles were visible. It’s rather easy on a flat 2D surface, but I could not find a reliable solution for the round 3D surface of the Earth.


Megatexture, also known as “Sparse Virtual Texture”, is a technique to compose a big virtual texture using tiles. The term was coined by John Carmack, who imagined this technique. I’ll stick with “Megatexture” since it sounds much cooler than “Virtual Texture”.

The virtual texture is made of tiles, which number varies with the Mipmap level.

Determining visible tiles

The great insight is how visible tiles are determined: the scene is rendered to an offscreen buffer, with a special fragment shader. In my case, the Megatexture is at most 256 by 256 tiles, and has a maximum of 8 mipmap levels, so the shader stores the tile’s x in the red component, the tile’s y in the green component, the mipmap level in the blue component, and the Texture ID in the alpha component. The scene is rendered to a RGBA8 offscreen buffer.

Texture ID

There may be several megatextures in a same scene. The texture ID permits to differentiate them in the Cache and in the Indirection Table later. Since objects which are not megatextured won’t be processed in the shader, you need to reserve a special Texture ID to mean “No texture”. It must corresponds to the buffer’s clear color, therefore I advise you choose the value 0x00, so it corresponds to a transparent color (since the texture Id is saved to the alpha channel).

Tiles determination shader

I’m sorry but I can’t provide my own code, so I’ll give you Sean Barrett’s instead, who was a pioneer in the technique, and made his code public:

This first part determines the mipmap level. The formula is copied straight from OpenGL’s implementation, so everyone uses the same.

  • vt_dimensions_pages is the size of a Tile (what Barrett and a number of people call a “Page”, but which I find inappropriate).
  • vt_dimension is the size the megatexture at the most detailed level (mipmap 0).
  • you’ll see below that the CPU is going to read the pixels of the offscreen buffer. To save a lot of processing power, the scene is not rendered at full size. readback_reduction_shift is a power of two; since it equals to 2 here, the offscreen buffer is a quarter of the width and height of the final rendering. I personally set this value to 4, and set the width and height of the buffer to 1/16th of the size of my view.
  • I’m not sure what mip_bias is. I believe this is a way to make the shader less agressive in its changes of mipmap levels, at the cost of the texture being a little blurry at times. (I don’t use it my own implementation).

The second part determines the Tile’s x and y and renders them in the color buffer:

Note that there is a mistake here: the mipmap level must be floored! Otherwise there will be a discrepancy between the level determined here, and the one determined in the Texturing shader.


A small image:

I changed the way colours are rendered so the image is visible, but the size is real. If the OpenGL view renders at 800 x 600, then the offset buffer is rendered at 1/16th of that, that is 50 x 37.

Loading tiles in the Cache

Reading back the offscreen buffer

I use glReadPixels to get the pixels. Every pixel corresponds to what I call a “Tile Id”: texture Id, x, y, mipmap level. Pixels with the “None” texture ID are discarded immediately. Others are converted to TileId objects, which are added to a NSMutableSet, in order to remove duplicates: since a same tile shows at several places, its TileId will appear several times.

It is not necessary to read the buffer, and therefore determine visible tiles, at every draw cycle. I do it only once every 4 frames (at 60fps = every 15th of second).

Determine the tiles to load

Now we have a list of visible tiles, we can compare them to the ones already in the Cache. The difference is the tiles to load.

In my implementation, tiles are loaded as background tasks, but textures are loaded in GPU memory on the main thread, because we have to with OpenGL. This textures obviously don’t have mip maps, but do use interpolation.

While the tile is loading, you will like to replace it with a “parent” tile— one with lower details — already in the cache. This is not too difficult, since the replacement only consists in a substitution in the Table of Indirection. Since the parent might not be in the Cache either, you should look for the grand-parent or grand-grand-parent, etc. I add the “base tile” (the lowest resolution one) to the set of visible tiles, so I’m always sure that at least the Base tile is in the Cache.

The Cache

The Cache itself is simply a texture (not mipmapped, but interpolated), which forms a grid of tiles. You need somewhere a table of correspondance between a position in the Cache and a TileId. I use a dictionary, indexed by the TileId. Use glTexSubImage2D() to replace only the part of the texture which contains the new tile.

When the Cache is full, some tiles must be dropped. People and I use a simple Least Recently Used mechanism to determine which ones. It’s simple, it works. I tried other heuristics, based on the mipmap level, to drop the least detailed tiles in last resort, but it did not work great, leading to load the most detailed tiles too frequently.

Dropping a Tile consists in marking its position as free in the table of correspondance. Since it does not perform OpenGL calls, it can be done at any time.

The Cache does not have to be huge: 16 x 16 tiles works. In my experience 8 x 8 tiles is not big enough on a Retina display: the program loads tiles and drops them continually. Make the Cache bigger if you want to remove some burden on the CPU, or have several Megatextures.

A 256 x 256 tile takes 250 KB of memory, so a 16 x 16 tiles cache takes 64 MB. That is very reasonable.

Table of Indirection

The Texturing Shader needs to know what are the coordinates of a Tile in the Cache texture. For that purpose, it is provided a Table of Indirection, which is a mip mapped texture.

A pixel of the texture contains the following information:

  • x position in the cache (stored in .r)
  • y position (in .g)
  • mipmap level (in .b).

For a particular mipmap level, this table has one pixel per tile. For instance, say that my megatexture measures 256 x 256 tiles at mipmap 0, then the texture measures 256 x 256 pixels at mipmap 0. There are only 128 x 128 tiles at mipmap 1, and hence the table measures 128 x 128 pixels at mipmap 1. There is a straight correspondance, so the Texturing Shader determines the tiles x and y according to the texture coordinate, and does a simple look up. (In other words, there is a table of indirection for every mipmap level. All these tables are combined in a single mipmapped texture).

You might wonder why the mipmap level is stored in the table, since it can be determined by the shader. Actually, this is what allows to substitute a parent tile; in that case, the pixel contains the x, y, and mipmap of the parent — not the child. The mipmap level of the parent tile is needed to determine correctly the position within the parent tile.

Texturing Shader

Finally, we arrive at the end of the chain !

Let’s first see the main() function: you’ll notice that texturing of the object is done as usual, using the boring texture2D() function.

  • phys_tex_dimension_page is the size of the megatexture at the maximum mipmap level, expressed in Tiles
  • page_dimension is the size of a Tile, it is not used directly because…
  • … page_dimension_log2 is the same size, but expressed as a power of two (2^8 = 256)
  • pageTableTexture is the Indirection Table texture
  • physicalTexture is the Cache texture

Now, I would like to focus on this:

Older versions of OpenGL (like the one I’m constrained to use, because of Apple), did not allow to sample the texture for a particular mip map level. However, texture2D() may take a third parameter, which is a value added to the implicit mipmap level (the one computed by OpenGL). I don’t know why 0.5 is substracted, but it works better this way.

I must say that I had a lot of problems with this principle because it assumes that:

  1. Tiles are square
  2. The megatexture is square

Since I had to cover the Earth, my megatexture was not square, but had a 2:1 ratio instead. And my tiles were 512 x 256 pixels. If this is not the case, you will run into troubles, because the computation of the mipmap level is right vertically, but not horizontally, and you will have visual artifacts, since the wrong mipmap level is sampled from the indirection texture.So, don’t do that: make your tiles square and stretch your megatexture if needed. It will save you a lot of pain.

(With a more recent version of GLSL, you might use texture2DLod(), and compute the mipmap level like in the Determination Shader, and not have this problem).

The MegaTexture is precise enough so the Richat Structure (“The Eye of Sahara”) can be seen.


Generating Tiles

We’re not done yet! Remember that the megatexture is a huge image that must be cut into tiles. I personally wrote a Python program that uses Image Magick to cut tiles and resize them. I won’t go into details here, but you must know that Image Magick is slow, and not very user friendly (and that by default, rescaling is proportional). You may do it otherwise, maybe using a Photoshop script or whatever.


There is one final problem with the principle of the megatexture itself. Because tiles are all stored in the Cache texture in a random order, a tile is unrelated with its neighbours. This causes visual problems because of the linear interpolation of tiles, which will cause half a pixel of neighbour textures to show:

Tiles Seams
Borders of tiles are visible

The solution is well known: leave a margin of 1 pixel on each side, and sample at this size. Hence the actual usable size is 254 x 254 pixels on my 256 x 256 tiles.

Further reading

I could not have made my Megatexture work without the following resources:

  • http://www.noxa.org/blog/2009/11/29/megatextures-in-webgl-2/
    This was my main source of inspiration, because it is very synthetic, covers most issues and guides toward a practical implementation. I don’t use his principle for the Indirection Tables though, which I find awkward. Maybe he could not do otherwise in WebGL.
  • The example of Sean Barrett
    There is a video, but I found it rather difficult to follow. It does not explain the basic technique well, but it might be interesting if you want to handle tri-linear filtering (which I don’t). You might also like to take a look at the source code, since most shaders written by other folks are based on it.
  • Thesis by Albert Julian Mayer
    This is really interesting as it sums up a lot of the techniques that are known for virtual texturing. You should definitely take a look if there are details you did not understand in my post, or if you want to push the technique further.

So, I made a Raytracer

I decided to take a month off from client projets, so I could work on subjects which I don’t usually have the time to work on.

Since I began learning Haskell lately, I knew that I had to code a real project. Actually, I think it’s the only way to study programming seriously: stick to a problem and find ways to attack it. A Raytracer looked like a reasonable idea for a project, since Raytracers use recursivity, which is the specialty of functional languages like Haskell.

Anyway, to sum up: I began with Haskell, and I ended with Swift. I met some difficulties regarding pseudo-random numbers in Haskell, I was tired and I was not sure about what was wrong (this was my first raytracer). I have not really given up, just passed on to keep my motivation.

The result is two projects:




I had no precise idea on how to code a raytracer, so I followed an e-book, Ray Tracing in One Weekend by Peter Shirley.

The original source code was written in C++. I was asked if porting it to Swift had been hard: Not really. Sometimes it was difficult to follow the C++ code, because of the way it is written, but Swift is way more elegant than C++, and has all required features — in particular operators overloading, which are more than useful when working with vectors.

The second question was how it performs, compared to C++. I don’t know, since I have not measured. I don’t care really, and that was not an aim for my experiment. All I can say is that it’s about the speed I expected: very slow. Shirley uses a “Pathtracer” algorithm. Wikipedia says that this is a characteristic of these raytracers. It already takes hours to render with 100 rays by pixel (112 minutes on my Mac for the very small and simple image above), and the image is still very noisy. At least 500 rays would be needed!

Currently the program uses a single thread. The obvious next step is therefore to parallelize the work so I can use all 4 cores of my Mac. Since I mostly used Structs (≃ immutable objects), it should be easy. I let you know when I find the time…



Elegant Objects by Yegor Bugayenko

How I came to follow Yegor Bugayenko’s blog

I’ve been reading Yegor Bugayenko’s blog (yegor256.com) for a year or so. At the time I was struggling with Core Data, and I could not really explain why until I stumbled upon one of its blog posts entitled along the lines of “Why ORMs are evil”. This post made explicit in my mind what I felt was wrong with ORMs but I could not articulate, and also provided an alternative.

At the time, I was studying a little of Functional Programming and was begining to think that what was wrong might be Object-Oriented Programming in the first place. And then Yegor’s blog opened up my eyes, and I discovered I have been doing it wrong for ages. Not that my code was terrible; it was actually very close to the standards of our industry — which means not so good.

His blog confirmed I was on the right direction on some things: for example, my latest code was written so my objects were immutable, which proved to make them easier to design and test, without any inconvenient in practice. It also made me reconsider my use of abstract classes, by using small protocols (you would say “interfaces” in Java) instead.

A manifesto for Object Thinking

The book is a kind of collection of the most emblematic blog posts he had written. However, it is certainly not copy-and-paste. The book is well organised into four parts — Birth, Education, Employment, Retirement — carefully chosen to emphasize the anthropomorphic nature of Objects. The chapters and paragraphs themselves were rewritten to make the whole book consistent.

Yegor Bugayenko thinks our industry is all wrong with OOP. People on its blog frequently treat him of an «OOP extremist», which he would take as a compliment! As such, the book is very cleaving, with frequent words like “evil”, “all wrong”, “you must”, “I think”. It is very opiniated, which is its greatest quality, a book is meant to present things an other way; otherwise you would not learn anything.

What distingues its discourse from trolling is that each point is argumented. The author tries to convince with examples, how they are wrong and how they could be made better. Most examples are great, a few are awkward, but in all manners, they have the merit to make the reader think.

I would recommend the book to any seasoned OOP programmer, although it is not perfect. In its current state, it looks a lot like a manifesto: it strongly tells what the author is against, but not enough what can be done instead. I wished the author had better explained alternatives that he uses, like the Decorator design pattern, or how he passes dependencies around the application, for example when they are shared resources.

But maybe this first edition had to look like a manifesto, because this thinking is too radical. I wish the second edition will be less defensive and will provide more practical examples.

Solicitation pour un développement sous iOS

Je reçois régulièrement des solicitations pour développer des applications iOS, en échange de parts sur des gains éventuels. La dernière demande était bien plus sérieuse que d’habitude, j’ai donc décidé de répondre de façon détaillée.

J’y explique pourquoi j’ai refusé toutes les demandes qui m’ont été faites jusqu’ici.


Je suis actuellement pris dans une mission de longue durée; je ne peux donc pas répondre favorablement à votre demande.

Pour être tout à fait honnête, j’aurais probablement refusé. Laissez-moi vous expliquer pourquoi:
Depuis quatre ans, j’ai déjà été sollicité de nombreuses fois pour réaliser des développements, avec des projets plus ou moins sérieux.

L’une de ces solicitations venait d’un ami d’un ami que j’avais déjà eu l’occasion de rencontrer. À cette époque, je débutais comme indépendant, et je n’avais pas de missions, aussi j’avais du temps, et le besoin de faire mes premières réalisations. La première discussion s’est très bien passée: le projet semblait un peu trop ambitieux, mais a priori rentable. Un truc dans l’hôtellerie. Il s’agissait d’une sorte d’appli iPhone en marque blanche que nous allions personnaliser pour chaque hôtel.

Evidemment, ils n’avaient pas d’argent, donc je devais travailler en échange d’une part sur les gains de la vente de l’appli. Tout le monde avait l’air heureux, aussi je me suis mis au travail; nous allions régler les questions contractuelles dans la semaine.

La première douche froide fut quand je reçus les premières conditions contractuelles: non seulement on me proposait peu (parce qu’ils avaient beaucoup de frais de déplacement pour acquérir les clients), mais ils voulaient même que je leur cède la propriété intellectuelle de mon travail.

La deuxième douche froide fut qu’au fil de la semaine, ils ont commencé à réclamer fonctionnalité sur fonctionnalité, ce qui signifiait concrètement pour moi, que je devais travailler plus longtemps. En d’autres termes, j’investissais également les gains financiers que j’aurais eu en travaillant pour d’autres clients.

Finalement, j’ai mi le hola: j’acceptais le partage des gains proposé, mais je délimitais clairement ce que ferait l’appli iPhone. Les discussions se sont arrêtées là. Je ne l’ai jamais regretté.

Ce jour-là, j’ai compris une chose: c’est une relation commerciale qui n’est pas saine. Quand je fais de la prestation, le client et moi délimitons un périmètre, et un prix. Si c’est trop cher, nous pouvons réduire le périmètre.
Mais dans la situation évoquée avant, le client et moi avons des intérêts divergents: moi de travailler le moins longtemps possible pour investir le moins possible, et lui d’avoir le maximum de fonctionnalités pour que son offre soit la plus sexy possible.

Par ailleurs, je ne suis pas un investisseur. Je ne dispose pas d’une réserve pécuniaire suffisante pour travailler des mois pour un gain potentiel. D’autant plus qu’avant d’investir, on se doit d’évaluer le potentiel du projet, et surtout l’équipe.
Parce qu’en pratique, ce que j’ai pu souvent observé, c’est que le porteur de projet n’y apporte rien qu’une idée, une vague expérience et un vague réseau professionnel. À me demander quel serait mon intérêt de m’associer avec un tel partenaire: c’est moi qui travaille et nous devons nous partager les gains.

(Je précise que la phrase précédente ne s’applique pas forcément à vous. En fait, je ne sais rien de votre situation, vous avez sans doute plus qu’une idée, d’autant plus que votre projet a l’air déjà assez avancé).

Bref, j’espère que vous ne m’en tiendrez pas rigueur, et j’ai pris le temps de vous écrire en toute honnêteté le fond de ma pensée; j’espère que cette honnêteté vous aidera à comprendre pourquoi il vous sera difficile de trouver un développeur iOS qualifié pour votre projet. Mais pourquoi pas aussi éventuellement à convaincre un éventuel développeur qui serait éventuellement prêt à se lancer dans l’aventure.


SCNSceneRenderer hitTest:options: limitations

I have met two serious limitations when using Scene Kit’s -[SCNSceneRenderer hitTest:options:] method:

  • it is not sufficient to add a node to the scene for the hit test to find it. The scene must also have been rendered once.
  • the method takes a long time. In my example, about 20 ms in a very simple scene which only contains a couple of spheres. Unfortunately, I had to do it 2000 times, so it takes 40 s ! Totally unusable in my case.

The method seems to have been designed for user interaction, and is only suitable for that case.

(I finally solved the problem by coding my own hit testing, which was possible because I work with a simple sphere. It was not easy because of the lack of information on what the matrix of the SCNCamera really contains, but I eventually managed to reduce the time from 40 s to a couple of milliseconds).

NSFileWarper pitfalls

Always use the .fileWarpers property

Altough keeping a child File Wrapper as a property does not look stupid beforehand, you will run into problems when doing so. The reason is because the parent wrapper might replace its instances of child wrappers by other instances if its see fit.
The solution is to always consult the .fileWarpers property of the parent Wrapper and enumerate child wrappers from it.

Updating files

There is no method to update a FileWrapper. If the content changes, remove the wrapper from its parent and add a new one.

Files named 1_#%$#%$_MyFile are created

This mechanism is used to ensure that the file names are unique. Therefore, if a file named “MyFile” already exists in the directory, and a child File Warper is added with its preferedName set as “MyFile” too, the second file will be named “1_#%$#%$_MyFile”. Hence the name of the “preferedName” property.

In general, this happens because you’ve messed up with the child wrappers — for example, when updating (see above), you forgot to remove the old wrapper before adding the new one with the same name.

# “File already exists” error when calling writeToURL

I’ve run into this during unit testing.
Say you have a file package on disk, and you want to update it. Calling writeToURL:options:originalContentsURL:error: will provoke an error “File already exists”. Pass the NSFileWrapperWritingAtomic and it will work.

I think the reason is because, by default, Apple engineers wanted to ensure that the file would remain untouched if the saving failed. It looks like NSDocument will ensure this, so it does not even set this option.