iOS app’lerinde backwards compatibility problemine bir çözüm

Erk Ekin
4 min readOct 5, 2019

--

Mobil uygulamaları web uygulamalarından ayıran en önemli özellik, ilkinin çok daha az güncelleniyor olmasıdır. Örneğin telefonunuzdaki tarayıcıya www.gmail.com yazdığınız zaman Gmail ekibinin yayınladığı en son web uygulamasını kullanıyor olursunuz. Ancak durum mobil app’lerde böyle değil.

Her ne kadar Android dünyasında Progressive Web Apps bu problemi bir açıdan çözmeye başlamış olsa da native app’ler uzun süreler hayatımızda kalacak gibi. Dolayısıyla mobil uygulama geliştiriciler olarak bu durumun yarattığı zorlukların üstesinden gelmeye çalışmak boş bir çaba olmayacaktır.

Oyunların da birçoğu olmak üzere modern bir mobil uygulama, çoğunlukla uzak bir kaynağa ihtiyaç duyar. Bu kaynakla çoğunlukla bir API üzerinden haberleşir, ve yine çoğu haberleşme Json objeleri aracılığıyla yapılır. Websocket ile anlık haberleşen app’lerin de çoğu Json objelerini kullanır. Örnek bir iç içe veri taşıyan kullanıcı objesini ve Swift Codable karşılığını burada görebilirsiniz.

Bu problemi çözmek için her endpoint’in döndürdüğü olası Json dökümanlarını projemize yerleştiremeli ve decoding testlerini yazmalıyız. Örneğin kitapları getiren servisin döndürdüğü Json metnini alıp projemize gömebiliriz. Ardından bu dosyaları belirli bir düzende okuyup birim test sırasında karşılaştırmasını yapıp doğru decode edilip edilmediklerini kontrol eden test kodlarını yazabiliriz.

Bu şekilde testlerinizden bundle’ınızdaki .json dosyalarına hızlıca erişebilirsiniz.

İlgili Json dosyalarına Fixture deniyor, türkçe olarak demirbaş ifadesini kullanabiliriz. Zaten buraya kadarki kısım her uygulamada olmalı, böylece güvenle kodunuzu marketlere gönderebilirsiniz.

Her endpoint’in olası Json objelerini düzgün bir şekilde bir klasörün içine yerleştirelim. Bu klasörün bundle’ımızda, yani uygulama derlendikten sonra elimizde olan klasörde, aynı klasör yapısında kalacak şekilde projemizi düzenlememiz gerekiyor. Örneğin tüm demirbaşlarımızı içeren klasörün bir altında books adında bir klasör daha varsa, aynı yapı bundle’da da korunmalı. Diğer türlü demirbaş klasörünün içindeki tüm dosyalar otomatik olarak root dizinine atılıyor ve farklı klasörlerde bulunan fakat aynı ismi taşıyan iki farklı demirbaşın birini kaybediyoruz. Klasör hiyerarşisinin korunması bu açıdan çok önemli. Zaten bu işlemi yalnızca bir kere yapıyoruz.

Xcode’da bu engel, klasörler Project Navigator’a sürükle bırak ile taşındıktan sonra beliren diyalogda Copy items if needed ve Create folder references kutucukları işaretlenerek aşılabiliyor.

Bu aşamadan sonra projemizi geliştirmeye devam ediyoruz ve her sürümü git ile tagliyoruz.

$ git tag 1.0.0

Diyelim ki şimdiye kadar 3 sürüm çıkardık, her sürümde demirbaş klasörümüzdeki json metinlerini güncelledik ve decode testlerini yazdık. Sürümlerimizi aşağıdaki gibi listeledik.

$ git tag1.0.01.0.12.0.0

Her sürümün müşterilerin cep telefonlarında ayrı ayrı yüklü olduklarını hayal edelim.

Şimdi 2.2.0 sürümünün çalışmalarının bittiğini ve geçmişe yönelik demirbaş testi yapacağımızı düşünelim. Yapılan son API değişikliklerinin önceki sürümlerde sağlıklı bir biçimde decode edilip edilmediklerini nasıl test ederiz?

Sonuçta önceki 3 sürümün hepsinde demirbaş klasörlerimiz bulunuyor ve decode birim testlerimiz de yazılmış durumda.

$ git archive <tag> | tar -x -C /app/tagler/<tag>

komutu ile reponuzun herhangi bir tagindeki halini içinde .git klasörü olmadan dışarı aktarabiliyorsunuz. Eğer çok sürümünüz varsa -ki bu bir gün olacaktır- bu taglere sahip sürümleri derleyip bir yerde tutmak size zaman kazandıracaktır. Aşağıdaki komut ile arşivlediğiniz kaynak kodunu test edilebilir biçimde derleyebilirsiniz.

yuvarlaklar bundle’ları temsil ediyor, 2.2.0 henüz yayında değil.

4. sürümde, yani 2.2.0 adlı olan sürümdeki demirbaşların, önceki tüm sürümlerde testleri geçirmesini istiyoruz. Eğer en son sürümdeki demirbaşlar önceki sürümlerin bundle’larına tek tek kopyalanıp öncekilerle yer değiştirilirlerse ve birim testler tek tek çalıştırılırsa en son demirbaşlarla önceki tüm sürümleri test edebiliriz.

2.2.0'daki demirbaşlar önceki bundle’lardakilerin yerlerine kopyalandı.

Böylece son yapılan API değişikliklerinin önceki sürümleri kullanan kullanıcıları olumsuz etkilemediğinden emin olabiliriz.

Önceki sürümleriniz değişmeyeceği için bunları bir yerde saklamak mantıklı olcaktır.

$ xcodebuild clean -scheme <target-adı> \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=<simülator-adı>,OS=<os-sürümü>' \
-derivedDataPath <bundle-yeri>
build-for-testing

Ardından her yeni sürümde son sürümün içinden demirbaş klasörünü alıp önceki test edilebilir bundle’larınızın içerisine yazma yöntemi ile API değişikliklerinizi güvenle yapabilirsiniz. 🤘

$ xcodebuild test-without-building \
-xctestrun '<xctestrun-yeri>' \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=<simülator-adı>,OS=<os-sürümü>'

Siz Rest Api’nizi versiyonlamak dışında bu problemi nasıl çözüyorsunuz?

İleri okuma

Beni Linkedin’den ekleyin.

https://www.linkedin.com/in/erkekin/

--

--