As you might know, iOS supports Dark mode since iOS 12. It is pretty
straightforward to implement it by using dynamic colors. Either
system provided ones or adjust them using trait collections.
Dynamically switch
At times, we can not rely on default colors but we have to listen
for traitCollection changes in order to do what is
appropriate for adapting the UI to the mode being active.
It is easy to do by checking current traitCollection :
overridefunctraitCollectionDidChange(_previousTraitCollection:UITraitCollection?){super.traitCollectionDidChange(previousTraitCollection)letuserInterfaceStyle=traitCollection.userInterfaceStyle// Either .unspecified, .light, or .dark}
App switcher tricks
One thing that I was not aware is that iOS switches back and forth
between active mode and the complementary when the app goes to the
background :
To have proper representation in application switcher, if the trait
collection is changed, the OS takes a screenshot of your app using
light and dark schemes.
Consequences
This can lead to subtle problems, in my case, I was loading a
specific stylesheet for MapBox, that, when loaded, was registering /
refreshing elements on map. Thus, when the app was put to the
background, a lot of things were happening, without a clear
explanation (and the app was sometimes crashing due to the very fast
switching between map stylesheets).
The workaround is rather simple : if the app is in the background we
prevent loading the stylesheet, the trade-off is acceptable : the
app can display an invalid preview in application switcher, but, it
is nicer on CPU / Network.
If you’re like most developers nowadays you make an extensive usage
of git across a large number of repositories.
The now-classic branching model that consists in using a branch for
every fix / feature / experiment before creating a merge request is
perfect for collaborating efficiently. However it comes with one
downside, local branches can quickly become messy and removing stale
branches is not that easy
Removing remote branches
git is a really well designed tool and removing remote
references from your local repository is easily done by
git remote prune origin
This will remove local references to non existing branches, however
it is not useful to remember this command, we can configure
git so that it prunes automatically on fetching :
git config --global fetch.prune true
Removing local branches
This is where things can be quite messy, as local branches might
have been merged, rebased, or squashed, and “classic” commands
allowing to detected merged branches are not always working as they
ought.
If you search how to clean up local branches, you might find a lot
of commands involving git branch --merged with
grep and xargs all the way.
git-gone is even clever enough to give you the command
to restore a branch if change your mind (as long as you did not
git gc the revision is still available locally, so you
can restore it)
Wether you’re new to macOS or an user coming from different
machines, the tools we tend to use is very important to be
productive.
Here you’ll find my list and what I install on a fresh machine to
get started.
System tools
First and foremost, I start with
brew to handle all my
packages, but instead of doing so manually, I use my
dotfiles
and my “magic” install script that does all the heavy lifting for
me.
Keyboard tools
I use a TypeMatrix with a Colemak layout and also the internal
keyboard of my laptop with its default layout. Colemak layout is
available on a fresh macOS install but it is far from perfect as
there are a lot of missing dead keys (to type accented letters
mainly), so I start by installing the layout provided on this page:
Colemak mac
To easily switch between the two and get almost the same feeling, I
use
Karabiner Elements.
Everyday tools
sdkman: to manage installation of
various sdk (mainly Java based)
Now that iOS13 is available for more than year, we can start to set
it as a lower bound for our deployments.
This allows us to play with all sugar that Apple put in it, one of
the biggest thing is the arrival of SwiftUI to supercede UIKit
(write cross platform UIs and so on).
Bridging the two worlds
Using SwiftUI from UIKit
The first thing we might try to do, is embedding a SwiftUI View
inside our UIViewController based application. To do
so, Apple gives us UIHostingViewController which is a
simple bridging controller, straightforward to use.
structMyNewView:View{varview:someView{Text("I'm in SwiftUI")}}classMyNewViewViewController:UIHostingViewController{init(){super.init(rootView:MyNewView())}}
UIViewController in SwiftUI
However, at times we still need to reuse our good old
UIViewController, either because we can not afford a
full SwiftUI rewrite, so we want to keep old code and migrate pieces
by pieces or because we are using something not yet adapted to
SwiftUI.
In my case it was using the camera to scan a QRCode.
Non elegant solution
I faced a few issue with examples I found out, most of them are
adding an extension to the UIViewController that makes
it conform to UIViewControllerRepresentable.
classMyViewController:UIViewController{varcancellable:Cancellable?// classic stuff}extensionMyViewController:UIViewControllerRepresentable{publictypealiasUIViewControllerType=MyViewControllerpublicfuncmakeUIViewController(context_:UIViewControllerRepresentableContext<MyViewController>)->UIViewControllerType{self// don't .init() please, class instance is already available}publicfuncupdateUIViewController(_:UIViewControllerType,context_:UIViewControllerRepresentableContext<MyViewController>){}staticfuncdismantleUIViewController(_uiViewController:UIViewControllerType,coordinator_:Coordinator){uiViewController.cancellable?.cancel()}}structMyView:View{varbody:someView{MyViewController()}}
I find this not easy to read, as we’re doing weird thing by
returning self from a function named
makeUIViewController. Some example are telling to
return MyViewController() instead of self.
Please don’t do this otherwise you’re creating the
UIViewController twice for each call !
I also had a leak when using Combine (more to come
about this great framework), my Cancellables were never
freed, leading in a memory cycle that kept the
UIViewController living even though it was no longer
presented.
It is important to do proper house keeping in the
dismantleUIViewController method if you don’t want to
use too much memory and slow down your app.
Nice looking way of doing
You will find a working example below, basically we need to
implement a UIViewControllerRepresentable struct to
represent be the container for our UIViewController in
the SwiftUI world.
As I was working on an iOS project, I added unit tests to ensure
things are not behaving badly (and will not).
During the process, a common pattern showed up and a few fields were
required, I came up with the idea to basically create a
BaseTest for my tests, so that everything is unified.
Base idea
I came up with something like the following for my tests
It worked very well within XCode, I could run the tests by hitting
the 🔹 in the gutter.
Bad things happen
The thing that I discovered later (thanks to the CI feedback), is
that XCode was not properly discovering my tests as it should. At
first I blamed the fact that my new tests were not at the top level
of my sources folder (and the other ones were), but it was easy to
check that this was not the problem at all.
Then, I blamed fastlane and thought that I’ve missed
something in my test target configuration or something, but in fact,
the problem was similar when using classical
CMD + U key combo.
XCode was simply not discovering my test.
Workaround
Inheritance is often misused, in this case, I think it is relevant,
but I applied classical way of working around this. I changed my
BaseTest to a BaseHelper instead, to which
the test delegates the calls.
With this, the test class properly inherits
XCTestCase and is discovered as expected (even in
subfolders).