Goodnotes her yerde

Ürünü iPad'de kullanan bir kadını gösteren Goodnotes pazarlama resmi.

Goodnotes mühendislik ekibi, son iki yıldır başarılı iPad not alma uygulamasını diğer platformlara taşımak için bir proje üzerinde çalışıyor. Bu örnek olayda, 2022 yılında yılın iPad uygulamasının web teknolojileri tarafından desteklenen web, ChromeOS, Android ve Windows'a nasıl geçiş yaptığı ve WebAssembly'nin ekibin on yıldan uzun süredir üzerinde çalıştığı Swift kodunu yeniden kullanarak nasıl yeniden kullandığı ele alınmaktadır.

Goodnotes logosu.

Goodnotes; web, Android ve Windows'a neden geldi?

2021'de Goodnotes, yalnızca iOS ve iPad uygulaması olarak kullanılabiliyordu. Goodnotes'taki mühendislik ekibi, çok büyük bir teknik zorluğu kabul etti: Goodnotes'un yeni sürümünü yeni işletim sistemleri ve platformlar için oluşturmak. Ürün, iOS uygulamasıyla tamamen uyumlu olmalı ve iOS uygulamasıyla aynı notları oluşturmalıdır. PDF'nin üzerine alınan tüm notlar veya eklenen resimler eşdeğer olmalıdır ve iOS uygulamasında gösterilen çizgilerle aynı olmalıdır. Eklenen tüm fırçalar, kullanıcının kullandığı araçtan (ör. kalem, fosforlu kalem, dolma kalem, şekiller veya silgi) bağımsız olarak iOS kullanıcılarının oluşturabileceği fırçayla eşdeğer olmalıdır.

El yazısı notlar ve çizimlerle Goodnotes uygulama önizlemesi.

Gerekliliklere ve mühendislik ekibinin deneyimine dayanarak ekip, Swift kod tabanını yeniden kullanmanın en iyi adım olacağına karar verdi. Çünkü kodun zaten yazıldığı ve uzun yıllar boyunca test edilmiş olduğu düşünüldüğünde bu karara vardık. Peki mevcut iOS/iPad uygulamasını neden Flutter ya da Compose Multiplatform gibi başka bir platforma ya da teknolojiye taşımaya ne dersiniz? Yeni bir platforma geçmek için Goodnotes'un yeniden yazılması gerekir. Bu durumda, zaten uygulanmış iOS uygulaması ile sıfır yeni uygulama arasında derlenecek bir geliştirme yarışı başlatılabilir veya yeni kod tabanı yakalanırken mevcut uygulamada yeni geliştirme durdurulabilir. Goodnotes, Swift kodunu yeniden kullanabilseydi, platformlar arası ekip uygulamalarla ilgili temel bilgiler ve özellik eşitliğine ulaşma üzerinde çalışırken ekip iOS ekibi tarafından uygulanan yeni özelliklerden yararlanabilirdi.

Ürün, iOS'un aşağıdaki gibi özellikleri eklerken karşılaşacağı bazı ilgi çekici zorlukları zaten çözmüştü:

  • Not oluşturma.
  • Dokümanlar ve notlar senkronizasyonu.
  • Çatışmasız Çoğaltılan Veri Türleri kullanan notlar için çakışma çözümü.
  • Yapay zeka modeli değerlendirmesi için veri analizi.
  • İçerik arama ve doküman dizine ekleme.
  • Özel kaydırma deneyimi ve animasyonlar.
  • Tüm kullanıcı arayüzü katmanları için model uygulamasını görüntüleyin.

Mühendislik ekibi iOS ve iPad uygulamaları için zaten iOS kod tabanını çalışır duruma getirebilse ve Goodnotes'un Windows, Android veya web uygulamaları olarak sunabileceği bir projenin parçası olarak bunu yürütebilirse bunların hepsinin diğer platformlarda uygulanması çok daha kolay olurdu.

Goodnotes teknoloji yığını

Neyse ki mevcut Swift kodunu web'de yeniden kullanmanın bir yolu vardı: WebAssembly (Wasm). Goodnotes, Wasm'ı açık kaynak ve topluluk tarafından yönetilen SwiftWasm projesi ile kullanarak bir prototip oluşturdu. Goodnotes ekibi, SwiftWasm ile halihazırda uygulanmış olan tüm Swift kodunu kullanarak bir Wasm ikili programı oluşturabildi. Bu ikili program Android, Windows, ChromeOS ve diğer tüm işletim sistemleri için Progresif Web Uygulaması olarak gönderilen bir web sayfasına eklenebilir.

Goodnotes kullanıma sunma sırası; Chrome, ardından Windows, Android ve son olarak da Linux gibi diğer platformlar PWA'ya dayanır.

Amaç, Goodnotes'u PWA olarak yayınlamak ve bunu her platformun mağazasında listeleyebilmekti. iOS için halihazırda kullanılan programlama dili ve web'de Swift kodunu yürütmek için WebAssembly kullanılan Swift'e ek olarak projede aşağıdaki teknolojiler de kullanıldı:

  • TypeScript: Web teknolojileri için en sık kullanılan programlama dili.
  • React ve webpack: Web için en popüler çerçeve ve paketleyici.
  • PWA ve hizmet çalışanları: Ekip, uygulamamızı diğer iOS uygulamaları gibi çalışan bir çevrimdışı uygulama olarak gönderebilir ve siz de mağazadan veya tarayıcıdan yükleyebilirsiniz. Bu nedenle, bu projenin sağladığı önemli olanaklar.
  • PWABuilder: Goodnotes'un, uygulamamızı Microsoft Store'dan dağıtabilmesi amacıyla PWA'yı yerel bir Windows ikili programı haline getirmek için kullandığı ana proje.
  • Güvenilir Web Etkinlikleri: Şirketin, PWA'mızı arka planda yerel bir uygulama olarak dağıtmak için kullandığı en önemli Android teknolojisi.

Swift, Wasm, React ve PWA'dan oluşan Goodnotes teknoloji yığını.

Aşağıdaki şekilde, klasik TypeScript ve React kullanılarak ve SwiftWasm ile vanilla JavaScript, Swift ve WebAssembly kullanılarak uygulananlar gösterilmektedir. Projenin bu bölümünde, ekibin gerektiğinde düzenleyici ekranımızdaki DOM'u Swift kodumuzdan işlemek için Swift ve WebAssembly'ye yönelik bir JavaScript birlikte çalışabilirlik kitaplığı olan JSKit ya da tarayıcıya özgü bazı API'ler bile kullanılıyor.

Mobil ve masaüstünde, Wasm tarafından kullanılan belirli çizim alanlarını ve React tarafından yönlendirilen kullanıcı arayüzü alanlarını gösteren uygulama ekran görüntüleri.

Wasm'ı ve web'i neden kullanmalısınız?

Wasm, Apple tarafından resmi olarak desteklenmese de Goodnotes mühendislik ekibinin bu yaklaşımın en iyi karar olduğunu düşünmesinin nedenleri şunlardır:

  • 100 binden fazla kod satırının yeniden kullanılması.
  • Platformlar arası uygulamalara katkıda bulunurken aynı zamanda temel ürünü geliştirmeye devam edebilme.
  • Yinelemeli bir geliştirme süreci kullanarak her platforma mümkün olan en kısa sürede ulaşmanın gücü.
  • Tüm iş mantığını kopyalamadan aynı belgeyi oluşturma kontrolüne sahip olma ve uygulamalarımızda farklılıklar yaratma.
  • Her platformda aynı anda yapılan tüm performans iyileştirmelerinden (ve her platformda uygulanan tüm hata düzeltmelerinden) yararlanır.

100.000'den fazla kod satırının yeniden kullanılması ve oluşturma hattımızı uygulayan iş mantığı büyük önem taşır. Aynı zamanda, Swift kodunu diğer araç zincirleriyle uyumlu hale getirmek, gerektiğinde bu kodu ileride farklı platformlarda yeniden kullanmalarını sağlar.

Yinelemeli ürün geliştirme

Ekip, kullanıcılara olabildiğince hızlı bir şekilde ürün sunmak için yinelemeli bir yaklaşım izledi. Goodnotes, ürünün kullanıcılar tarafından paylaşılan herhangi bir belgeyi alıp herhangi bir platformdan okuyabilen salt okunur sürümüyle başladı. Sadece bir bağlantı kullanarak, iPad'lerinden yazdıkları notlara erişip bunları okuyabilirler. Platformlar arası sürümleri iOS sürüme eşdeğer hale getirmek için bir sonraki aşamaya özellik düzenleme eklendi.

Salt okunur moddan tam özellikli ürüne geçişi simgeleyen iki uygulama ekran görüntüsü.

Salt okunur ürünün ilk sürümünün geliştirilmesi altı ay sürdü. Sonraki dokuz ay ise ilk bir grup düzenleme özelliğine ve oluşturduğunuz tüm dokümanları ya da birinin sizinle paylaştığı tüm dokümanları kontrol edebileceğiniz kullanıcı arayüzü ekranına ayrıldı. Buna ek olarak, SwiftWasm Araç Zinciri sayesinde iOS platformunun yeni özelliklerinin platformlar arası projeye kolayca taşınması mümkün oldu. Örneğin, yeni bir kalem türü oluşturuldu ve binlerce kod satırını yeniden kullanarak platformlar arası kolayca uygulamaya alındı.

Bu projeyi oluşturmak inanılmaz bir deneyimdi ve Goodnotes bu projeden çok şey öğrendi. Bu nedenle aşağıdaki bölümlerde web geliştirme, WebAssembly ve Swift gibi dillerin kullanımıyla ilgili ilginç teknik noktalara odaklanacağız.

Başlangıçtaki engeller

Bu projede çalışmak, birçok farklı açıdan son derece zordu. Ekibin tespit ettiği ilk engel SwiftWasm araç zinciriyle ilgiliydi. Araç zinciri, ekip için çok önemli bir etken oldu, ancak tüm iOS kodları Wasm ile uyumlu değildi. Örneğin, IO veya kullanıcı arayüzüyle ilgili kod (ör. görünümlerin uygulanması, API istemcileri veya veritabanına erişim) yeniden kullanılabilir durumda değildi. Bu nedenle ekibin, bu bölümleri platformlar arası çözümden yeniden kullanabilmek için uygulamanın belirli bölümlerini yeniden düzenlemeye başlaması gerekiyordu. Ekibin oluşturduğu PR'lerin çoğu soyut bağımlılıkları yeniden düzenledi. Böylece ekip daha sonra bağımlılık ekleme veya benzer başka stratejiler kullanarak bunları yenisiyle değiştirebiliyordu. iOS kodu başlangıçta Wasm'da uygulanabilecek ham iş mantığıyla giriş/çıkış ve kullanıcı arayüzünden sorumlu kod ile birlikte çalışıyordu. Wasm'ın da desteklemediği için bu mantık, Wasm'da uygulanmayan kullanıcı arayüzüyle ilişkilendirildi. Bu nedenle, Swift iş mantığı platformlar arasında yeniden kullanılmaya hazır olduğunda IO ve kullanıcı arayüzü kodunun TypeScript'te yeniden uygulanması gerekiyordu.

Performans sorunları çözüldü

Goodnotes editör üzerinde çalışmaya başladıktan sonra ekip, düzenleme deneyimiyle ilgili bazı sorunlar belirledi ve yol haritamıza zorlu teknoloji kısıtlamaları eklendi. İlk sorun performansla ilgiliydi. JavaScript, tek iş parçacıklı bir dildir. Yani bir çağrı yığını ve bir bellek yığını vardır. Kodu sırayla yürütür ve sonrakine geçmeden önce bir kod parçasını yürütmeyi bitirmesi gerekir. Eş zamanlıdır, ancak bazı durumlarda zararlı olabilir. Örneğin, bir işlevin yürütülmesi zaman alıyorsa veya bir şeyi beklemesi gerekiyorsa bu süre zarfında her şeyi dondurur. Mühendislerin de tam olarak şunu çözmesi gerekiyordu. Kod tabanımızdaki, oluşturma katmanı veya diğer karmaşık algoritmalarla ilgili bazı belirli yolların değerlendirilmesi, ekip için bir sorundu. Bunun nedeni, bu algoritmaların eşzamanlı olması ve bunları yürütmek ana iş parçacığının engellenmesiydi. Goodnotes ekibi bunları daha hızlı hale getirmek için yeniden yazdı ve eşzamansız hale getirmek için bazılarını yeniden düzenledi. Ayrıca, uygulamanın algoritmayı yürütmeyi durdurup işleme daha sonra devam etmesini sağlayan bir getiri stratejisi de sundular. Böylece, tarayıcı kullanıcı arayüzünü güncelleyip karelerin atlanmasının önüne geçildi. Bu, ana iOS iş parçacığı kullanıcı arayüzünü güncellerken iş parçacıklarını kullanabildiği ve bu algoritmaları arka planda değerlendirebildiği için iOS uygulaması için bir sorun değildi.

Mühendislik ekibinin çözmesi gereken bir başka çözüm de, DOM'ye bağlı HTML öğelerine dayalı bir kullanıcı arayüzünü, tam ekran tuvale dayalı bir doküman kullanıcı arayüzüne taşımaktı. Proje, herhangi bir web sayfasının yaptığı gibi HTML öğeleri kullanarak DOM yapısının bir parçası olarak bir belgeyle ilgili tüm notları ve içerikleri göstermeye başladı, ancak bir noktada tarayıcının DOM güncellemeleri üzerinde çalıştığı süreyi azaltarak alt segment cihazlarda performansı artırmak için tam ekran tuvale geçiş yapıldı.

Mühendislik ekibi, aşağıdaki değişiklikleri projenin başında yapmış olsalardı karşılaşılan bazı sorunları azaltabilecek şeyler olarak belirledi.

  • Ağır algoritmalar için web çalışanlarını sık sık kullanarak ana iş parçacığını daha fazla boşaltın.
  • En baştan itibaren JS-Swift birlikte çalışma kitaplığı yerine dışa aktarılan ve içe aktarılan işlevleri kullanın. Böylece, Wasm bağlamından çıkmanın performans üzerindeki etkisini azaltabilirler. Bu JavaScript birlikte çalışabilirlik kitaplığı, DOM'a veya tarayıcıya erişim sağlamak için faydalıdır ancak dışa aktarılan yerel Wasm işlevlerden daha yavaştır.
  • Uygulamanın, ana iş parçacığını boşaltabilmesi ve not yazarken uygulamaların performansını en üst düzeye çıkarmak için Canvas API'nin tüm kullanımını bir web çalışanına taşıyabilmesi için kodun arka planda OffscreenCanvas kullanımına izin verdiğinden emin olun.
  • Wasm ile ilgili tüm yürütmeleri bir web çalışanına, hatta web çalışanları havuzuna taşıyarak uygulamanın ana iş parçacığı iş yükünü azaltabilir.

Metin düzenleyici

İlginç bir diğer sorun ise metin düzenleyici adlı özel araçla ilgiliydi. Bu aracın iOS uygulaması, arka planda RTF kullanan küçük bir araç seti olan NSAttributedString'e dayanır. Ancak, bu uygulama SwiftWasm ile uyumlu değildir. Bu nedenle, platformlar arası ekip önce RTF dilbilgisine dayalı özel bir ayrıştırıcı oluşturmak ve daha sonra RTF'yi HTML'ye dönüştürerek düzenleme deneyimini uygulamak (veya tam tersi) zorlandı. Bu sırada iOS ekibi, bu aracın yeni uygulaması üzerinde çalışmaya başladı. Bu araç, RTF kullanımını özel bir modelle değiştirdi. Böylece uygulama, stildeki metinleri aynı Swift kodunu paylaşan tüm platformlar için kolay bir şekilde gösterebilecek.

Goodnotes metin düzenleyici.

Bu zorluk, kullanıcının ihtiyaçlarına göre yinelenen bir şekilde çözüldüğü için proje yol haritasındaki en ilginç noktalardan biriydi. Bu, kullanıcı odaklı bir yaklaşım kullanılarak çözülmüş bir mühendislik problemiydi. Ekibin, metni oluşturabilmesi için kodun bir kısmını yeniden yazması gerekiyordu. Böylece ikinci bir sürümde metin düzenlemeyi etkinleştirdiler.

Yinelemeli sürümler

Projenin son iki yılda geçirdiği evrim inanılmaz. Ekip, projenin salt okunur bir sürümü üzerinde çalışmaya başladı ve aylar sonra birçok düzenleme özelliğine sahip yepyeni bir sürümü kullanıma sundu. Ekip, üretimdeki kod değişikliklerini sık sık yayınlamak için özellik işaretlerini yoğun bir şekilde kullanmaya karar verdi. Ekip her sürüm için yeni özellikleri etkinleştirebilir ve kullanıcının haftalar sonra göreceği yeni özellikleri uygulayan kod değişikliklerini yayınlayabilir. Ancak ekip, daha iyi hale getirilebileceğini düşünüyor. Dinamik özellik işaretleme sistemi kullanıma sokmanın, işaret değerlerini değiştirmek için yeniden dağıtım ihtiyacını ortadan kaldıracağından işleri hızlandıracağını düşünüyorlar. Goodnotes'un proje dağıtımını ürün sürümüne bağlaması gerekmeyeceğinden, bu yöntem Goodnotes'a daha fazla esneklik sağlar ve yeni özelliğin dağıtımını da hızlandırır.

Çevrimdışı iş

Ekibin üzerinde çalıştığı temel özelliklerden biri çevrimdışı destekti. Belgelerinizi düzenleyebilme ve değiştirebilme, bu tür tüm uygulamalardan bekleyebileceğiniz bir özelliktir. Ancak Goodnotes ortak çalışmayı desteklediği için bu basit bir özellik değildir. Yani, farklı kullanıcılar tarafından farklı cihazlarda yapılan tüm değişiklikler, kullanıcılardan herhangi bir çakışmayı çözmelerini istemeden her cihaza yansıtılmalıdır. Goodnotes, arka planda CRDT kullanarak bu sorunu uzun zaman önce çözdü. Bu Anlaşmazlıksız Çoğaltılan Veri Türleri sayesinde Goodnotes, herhangi bir kullanıcı tarafından herhangi bir dokümanda yapılan tüm değişiklikleri birleştirebiliyor ve değişiklikleri herhangi bir birleştirme çakışması olmadan birleştirebiliyor. IndexedDB'nin kullanılması ve web tarayıcılarında kullanılabilen depolama alanı, web'de ortak çalışmaya dayalı çevrimdışı deneyimi mümkün kıldı.

Çevrimdışı çalışan Goodnotes uygulaması.

Üstelik, Goodnotes web uygulamasını açmak, Wasm ikili boyutu nedeniyle yaklaşık 40 MB ön indirme maliyetiyle sonuçlanır. Goodnotes ekibi başta uygulama paketinin kendisi ve kullandıkları API uç noktalarının çoğu için normal tarayıcı önbelleğine güvenirdi. Ancak gerideyken, daha güvenilir Cache API'si ve hizmet çalışanları daha önce kâr elde edebilirdi. Ekip, karmaşık olduğu düşünüldüğü için başlangıçta bu görevden kaçınmayı tercih etmişti, ancak sonunda Workbox'ın bunu daha az korkutucu hale getirdiğini fark etti.

Web'de Swift'i kullanmayla ilgili öneriler

Yeniden kullanmak istediğiniz çok sayıda kod içeren bir iOS uygulamanız varsa inanılmaz bir yolculuğa başlamak üzere olduğunuz için hazır olun. Başlamadan önce ilginizi çekebileceğiniz bazı ipuçları var.

  • Hangi kodu yeniden kullanmak istediğinizi kontrol edin. Uygulamanızın iş mantığı sunucu tarafında uygulandıysa muhtemelen kullanıcı arayüzü kodunuzu yeniden kullanmak istersiniz ve Wasm bu konuda size yardımcı olmaz. Ekip, WebAssembly ile tarayıcı uygulamaları oluşturmaya yönelik SwiftUI uyumlu bir çerçeve olan Tokamak'ı kısaca inceledi. Tokamak, uygulamanın ihtiyaçlarına yeteri kadar olgunlaşamadı. Ancak uygulamanızda güçlü iş mantığı veya algoritmalar istemci kodunun bir parçası olarak uygulanmışsa Wasm en iyi arkadaşınız olacaktır.
  • Swift kod tabanınızın hazır olduğundan emin olun. Kullanıcı arayüzü mantığınızla iş mantığınız arasında güçlü bir ayrım oluşturan kullanıcı arayüzü katmanına veya belirli mimarilere yönelik yazılım tasarım kalıpları, kullanıcı arayüzü katmanı uygulamasını yeniden kullanamayacağınız için çok kullanışlı olacaktır. IO ile ilgili tüm kodlara bağımlılık eklemeniz ve sağlamanız gerektiğinden net mimari veya altıgen mimari ilkeleri de temel olacaktır. Ayrıca, uygulama ayrıntılarının soyutlama olarak tanımlandığı ve bağımlılığı ters çevirme ilkesinin yoğun olarak kullanıldığı bu mimarileri izlediğinizde yöntem çok daha kolay olacaktır.
  • Wasm, kullanıcı arayüzü kodu sağlamaz. Bu nedenle, web için kullanmak istediğiniz kullanıcı arayüzü çerçevesine karar verin.
  • JSKit, Swift kodunuzu JavaScript ile entegre etmenize yardımcı olur. Ancak, hotspot kullanıyorsanız JS-Swift köprüsünü geçmek maliyetli olabilir ve bunu dışa aktarılan işlevlerle değiştirmeniz gerekebilir. JSKit'in arka planında nasıl çalıştığı hakkında daha fazla bilgiyi resmi belgelerden ve Gizli bir cevher olan Swift'te Dinamik Üye Arama yayınından edinebilirsiniz.
  • Mimarinizi yeniden kullanıp kullanamayacağınız, uygulamanızın izlediği mimariye ve kullandığınız eşzamansız kod yürütme mekanizması kitaplığına bağlıdır. MVVP veya composable mimari gibi kalıplar, uygulamayı Wasm ile kullanamayacağınız UIKit bağımlılıklarıyla birleştirmeden görünüm modellerinizi ve kullanıcı arayüzü mantığının bir kısmını yeniden kullanmanıza yardımcı olur. RXSwift ve diğer kitaplıklar Wasm ile uyumlu olmayabilir. Bu nedenle Goodnotes'un Swift kodunda OpenCombine, eşzamansız/bekleyen akışlar ve akışları kullanmanız gerekeceğinden bunu unutmayın.
  • Wasm ikili dosyasını gzip veya brotli kullanarak sıkıştırın. Klasik web uygulamaları için ikili programın boyutunun oldukça büyük olacağını unutmayın.
  • Wasm'i PWA olmadan kullanabiliyor olsanız bile, web uygulamanızın manifest dosyası olmasa veya kullanıcının uygulamayı yüklemesini istemiyor olsanız bile en azından bir hizmet çalışanı eklediğinizden emin olun. Hizmet çalışanı, Wasm ikili programını ve tüm uygulama kaynaklarını ücretsiz olarak kaydedip sunar. Böylece kullanıcının projenizi her açtığında bunları indirmesi gerekmez.
  • İşe alım sürecinin beklenenden daha zor olabileceğini unutmayın. Swift konusunda deneyimi olan güçlü web geliştiricileri veya web’de deneyimi olan Swift geliştiricileri ile çalışmanız gerekebilir. Her iki platform hakkında da bilgi sahibi olan genel mühendisler bulabilirseniz

Sonuçlar

Zorluklarla dolu bir ürün üzerinde çalışırken karmaşık bir teknoloji yığını kullanarak web projesi oluşturmak inanılmaz bir deneyimdir. Zor olacak ama buna değecek. Goodnotes, bu yaklaşımı kullanmadan iOS uygulamasının yeni özellikleri üzerinde çalışırken Windows, Android, ChromeOS ve web için bir sürüm yayınlayamazdı. Bu teknoloji yığını ve Goodnotes'un mühendislik ekibi sayesinde Goodnotes artık her yerde ve ekip sonraki görevler üzerinde çalışmaya devam etmeye hazır! Bu proje hakkında daha fazla bilgi edinmek isterseniz Goodnotes ekibinin NSSpain 2023'te yaptığı konuşmayı izleyebilirsiniz. Web için Goodnotes'u denemeyi unutmayın!