iOS Development Sürecinde Rest Api İle Nasıl Çalışılır ?

Ferhan Akkan
7 min readSep 2, 2020

--

Merhabalar bu yazımda sizlere iOS uygulamalarınızı geliştirirken Rest Api ile nasıl çalışmanız gerektiğini anlatacağım. Mümkün olduğunca basit ve kullanışlı olacak şekilde anlatmayı hedeflemekteyim. Bundan dolayı her konuya adım adım detaylı yaklaşacağım. Bu oluşturduğumuz servis katmanını ufak düzenlemelerle birçok uygulamanızda kullanabilirsiniz.

iOS development’a yeni başlamış iseniz tahminimce Firebase’i kullanmışsınızdır ancak iş hayatında veya tam anlamıyla bi uygulama geliştirdiğiniz süreçte servis olarak Firebase size yeterli olmayacaktır. Bunun iki ana nedeni bulunmakta. Bunlardan ilki maddi yönü , uygulamanız çok fazla kullanıcıya ulaşır ise Firebase’in database ve storage’ı oldukça yüksek maliyetlere yol açacaktır. İkincisi ise Firebase database ve storage servisleri size yeterli esnekliği sağlayamamasıdır.

Peki bu durumda nasıl bi çözüme başvurmamız gerekiyor?

Bu noktada imdadımıza Restful Api’ler yetişiyor. Backend geliştiriler tarafından geliştirilmiş olan bu servisler uygulamanızın tüm isteklerine cevap verecek şekilde tasarlanıyor ve database’den alacağınız verinin en az eforla kullanmanızı sağlıyor. Bu servislere istek attığımız zaman bizlere JSON adı verilen format’ta cevap dönüyor.

Json formatını aşağıda görmektesiniz. Key value çifti ile karşımıza çıkmaktadır.

{ 
"name":"Ferhan",
"age":25,
"gender": "Male"
}

İşin kod kısmına geçmeden önce birkaç tane bilmeniz kelimesi ve görevlerini sizlere açıklamak istiyorum.

End Point: End point sizin servis tarafında ulaşmaya çalıştığınız noktadır. Yani www.benimservisim.com/ben link’i bizim end point’umuz olmaktadır. Bu kısmı iki aşama olarak düşünebilirsiniz. www.benimservisim.com/ bizim ana linkimiz. /ben ise serviste gitmek istediğimiz nokta.

Token: Uygulamanızda kayıt işlemi var ise kullanıcının mail ve şifresini telefondan tutmak güvenlik zafiyeti oluşturmaktadır. Bundan dolayı kullanıcı ilk kayıt olurken aldığımız bilgileri servise göndeririz ve servis bize bi adet Token gönderir. Bu token string yapısındadır. Kayıt işleminden sonra bir daha e-mail ve şifre ile ilişkimiz kalmayacaktır. İşlemlerimiz bu Token ile gerçekleştireceğiz. T.C. kimlik numarası gibi düşünebilirsiniz.

Link Üzerinden Parametre Gönderme: İlerleyen süreçte örnekler ile göstereceğim ancak ön bilgi olması açısından servisten veri almak için çağrı yaptığımız zamanlarda bi parametreyi direk link sonuna eklememiz gerekebilmektedir.

www.benimservisim.com/{Kullanıcının ID Numarasi}

Link Üzerinden Query İle Parametre Gönderme: Link’in sonuna direk parametreyi eklemek haricinde ise query formatında da veriler linke eklenebilmektedir.Key value ikilisi olarak değer yerleştirilmiştir.

http://localhost:8080/user/?userID={Kullanıcının ID Numarasi}

Genel olarak işin sözel kısmını tamamladık yavaş yavaş teknik kısma geçmeye başlıyoruz. Tahminimce aklınızdan geçen bir soruya cevap veriyim. Önceden URL session ile Rest Api ile çalıştıysanız her bir end point için ayrı bir istek mi yazmamız gerekecek diye düşünebilirsiniz. Ancak yazacağımız servis katmanımızı öyle bi şekilde tasarlayacağız ki tek bi çağrısı yapısı ile bütün ihtiyaçlarımız karşılayacağız.

Bundan sonraki aşama için öncelikle değinmem gereken bir konu var. Teknik olarak anlamanız gereken üç şey var. Bunlardan bi tanesi Generic Type nedir , ikinci de PromiseKit nedir ve üçüncüsü Alamofire nedir? Sizlere bunu basitçe aklımaya çalışacağım ama daha detaylı bilgiye ulaşmak için vereceğim linklere bakabilirsiniz.

Generic Nedir?

Genericler aynı işi birden fazla fonksyon ile yazmak yerine tek bir fonksyonla bir çok data type kabul etmesini sağlamaktadır. Yani bi fonksyona değişken tipine T (Generic’in Adı) tanımlarız ve bu sayede bu parametre üzerinden hem String hem de Int geçirebiliriz. Geçireceğiniz tipin bi sınırlaması yoktur. İlerleyen süreçte kullanırken hepsini adım adım açıklayacağım genede linkteki yazıyı okumanızı şiddetle tavsiye ediyorum.

Generic’leri detaylı bi şekilde incelemek için => https://medium.com/bili%C5%9Fim-hareketi/swift-generics-da126fd7376e

PromiseKit Nedir?

PromiseKit asenkron geliştirme için kullanmış olduğumuz bir 3cü parti kütüphanedir. Bildiğiniz üzere servis’e istek attığımız zaman bu isteğe dönecek olan verinin ne zaman tarafımıza ulaşacağını bilemeyiz. Bu 1 saniye de olabilir 5 saniyede. Bundan dolayı asenkron olarak geliştirme yapmamız gerekir. PromiseKit hem bu sorunu ortadan kaldırıyor ayrıca da birçok asenkron isteği birbirine bağdaşık yapmamızı sağlıyor. Örneklendirmek gerekirse bütün kullanıcıları servisten isteyip gelen listeden ilk kullanıcıyı silmek istediğimizide bu iki asenkron işlemi sorunsuz şekilde yapmamızı sağlıyor. Ayrıca syntax olarakta gayet başarılı.

PromiseKit ile alakalı daha detaylı açıklama ve bilgi için =>https://medium.com/@dimitarstefanovski/how-to-use-promisekit-for-swift-4bbb9e40f7cc

Alamofire Nedir?

Alamofire servis çağrılarını yapmamızı sağlayan bir 3cü parti kütüphanedir. URLSession ile de bu çağrıları yapabilmekteyiz ancak iOS topluluğu tarafından yazma ve okuma kolaylığı sağlandığı için oldukça fazla kullanılmaktadır. Geliştirme sürecimizde servis çağrısını alamofire aracılığı ile yapacağız. Link’teki yazıyı okumanızı şiddetle tavsiye ediyorum. İçerisinde alamofire basic kullanımları bulunmakta. Geliştireceğimiz servis katmanı ile arasındaki farkı görmeniz önem arz etmekte.

Alamofire ile alakalı daha detaylı açıklama ve bilgi için =>https://medium.com/%40ferhanakkan/alamofire-ile-api-%C3%A7a%C4%9Fr%C4%B1lar%C4%B1-nas%C4%B1l-yap%C4%B1l%C4%B1r-get-post-put-delete-kullan%C4%B1m%C4%B1-66cd4d773dfa

Kodumuzu İnceleyelim!!!

Öncelikle sizden ricam linkteki kodu indirmeniz ve pod install işlemini yapmanız. Ardından kodun içeriğine biraz göz gezdirmeniz. Çünkü kodu parça parça incelemem gerekecek.

Link=> https://github.com/ferhanakkan/HowToUseNetworkService

Genel olarak kod’a baktıysanız görmüş olacaksınız ki servis katmanı 3 aşamadan oluşmakta. Öncelikle ViewController’tarafında requestimizi oluşturuyoruz. Middle service kısmında ise dönecek olan Promise’ın Type’ını, url path’ini ve parametre gönderme işlemlerini gerçekleştiriyoruz. Core servis kısmımızda ise kurmuş olduğumuz yapı sayesinde olası her senaryo için hazır fonksyonlar ile request işlemimizi gerçekleştiriyoruz. Şimdi adım adım kodumuz nasıl işliyor ve işlemleri neden yaptığımızı açıklayacağım.

Core Service

İlk önce CoreService’imizin 8–38'inci satırı arasına göz atalım.(Aşağıdaki görseldeki satır değil direk kaynak koddaki satır!!!)

İlk önce yüklemiş olduğumuz podlar olan Alamofire ve PromiseKit’i CoreService sınıfımıza import ediyoruz. Sonrasında servisimize ulaşacamamızı sağlayacak olan baseApiUrl değişkenine servisin linkini yazıyoruz. baseApiUrl değişkenini private yapma nedenimiz encapsulation’dan ötürü. Aynı şekilde birde ulaşmak istediğimiz endPoint’u tutacağımız değişkenimizi oluşturuyoruz. Gene aynı şekilde private olma nedeni encapsulation.

İşlerin biraz hareketlendiği nokta olan sessionManager’ımızı optional session olarak tanımlıyoruz. Bunu yapma nedenimiz cağrılar sırasında response’un kaç saniye içerisinde cevap gelmez ise başarısız olacağını belirlemek.

init() fonksyonu CoreService objesi oluşturulduğu anda tetiklenecek ve zaman aşımı ayarlarını yapmamızı ve bu ayarların sessionManager değişkenimizde tutmamızı sağlayacak.

Bir başka yapmamız gereken ayar da Http Header’ımızı oluşturmak. Http nedir derseniz internet haberleşme protokolüdür. Bütün çağrılarımızı bu protokol üzerinden yönetiriz. Sırf mobil cihazlar değil internet bağlantısı olan her cihaz bu protokol’ler üzerinden haberleşir. Bizim kısmımıza geri dönelim.

Http Header’ın içermesi gereken ilk özellik “Content-Type” : “application/json” çünkü uygulamamızın hangi formatta çağrıya yanıt kabul edeceğini belirtmektedir. Güncel bütün mobil uygulamalar için aynı şekildedir.

Sonrasında ise uygulamamızda kayıt işlemleri var ise Token’ımızı da Http Header içerisinde göndermemiz gerekmektedir. Genellikle iOS uygulamalarımız için Token’ı UserDefaults’un içerisinde tutmaktayız. Bundan dolayı headers() fonksyonu içerisinde ilk yaptığım işlem hafızada kayıtlı bir Token olup olmadığını kontrol etmek. Eğer ki Token var ise “Authorization”: “Bearer \(token)” ile Http Header’a bu Token’ı koyuyoruz.

Http Header ile olan son kısmımız ise tamamen opsiyonel. Bu kısmı kullanmanızı gerektirecek durum uygulamanızın çoklu dil desteği bi servisle birlikte çalışıyor olmasıdır. Gene UserDefaults ile önceden seçilmiş bi dil var mı diye kontrol ediyoruz. Yok ise default value’muzun değerini alıyor. “Accept-Language”: headerLang ile de seçmiş hangi dilde cağrıya cevap almak istediğimizi belirtiyoruz.

Esas cağrıyı yaptığımız request fonksyona gelmiş bulunmaktayız. Bu fonksyon girdi olarak 3 adet parametre almakta. Bunlar url yani çağrıyı yapacağımız fullpath, method yani post, get gibi işlemlerden hangisini yapacağımız ve son olarak opsiyonel olarak parametre girdisi almakta. Parametre girdisi [String:Any] tipinde verilerdir. Bazen get işlemi yaparken parametre göndermemiz gerekmediğinden dolayı opsiyonel olarak belirledim. (Get, Post vb. işlemlerin ne olduğunu bilmiyor iseniz Alamofire nedir kısmındaki link ile ulaşabileceğiniz yazımda anlatmıştım.)

Evet request fonksyonuna girdiğimizde karşımızda ilk olarak encodinga (Yanlış yazmısım ileride düzelteceğim) değişkenine opsiyonel ParameterEncoding tipinde nil olarak oluşturuyorum. Bunun nedeni bu değişkeni istediğim encoding type’ına dönüştürebilmek. İf ile çağrıyı hangi method ile yapacağımızı kontrol ediyorum. Eğer ki method tipim get ise parametreleri query olarak link’e eklemem gerekmekte (Direk linke’e parametreyi yazdığınızda encoding kısımları linkinizi etkilemez. Query ile direk linke yazmanın farkı burada oluşmakta.). Eğer ki method get değil ise bu durumda parametreleri JSON formatına çevireceğini söylüyoruz.

Request fonksyonunda fark ettiğiniz üzere Promise<T> dönmemiz gerekmekte. (Bu kısımları anlayabilmeniz için PromiseKit nedir kısmındaki link’i incelemeniz gerekmektedir.) Bundan dolayı return Promise<T> ile oluşturduğumuz Promise’in içerisinde çağrımızı yapıyoruz. Başarı durumunu yakalamak için seal.fullfil, başarızlık durumu için ise seal.reject ile yakalıyoruz.

Şimdi bakalım Promise içerisinde servis çağrımızı ne şekilde yapmışız.

Yazımızın önceki kısmında oluşturmuş olduğumuz sessionManager objesini kullanarak url, method, parametre, encoding tipi ve http headırımızı yerleştiriyoruz. .validate ile servisten dönen status code’un 200 ile 300 arasında ise başarılı kabul etmesi gerektiğini belirtiyoruz. .responseDecodable ile de servisten gelen cevabın decodable edilebilecek bi obje olduğunu garanti ediyoruz.

Çağrımıza cevap geldiği zaman ise öncelikle gelen veri boş mu dolu mu kontrolünü gerçekleştiriyoruz. Ardından data dolu ise başarılı mı başarısız bi çağrı mı bunu gözlemliyoruz. Çağrı başarılı ise seal.fullfil ile datayı Middle Service Katmanına gönderiyoruz. Eğer ki çağrı başarısız ise nedenini bulmak için gelen cevabı JSON olarak [String: Any] objesine çevirip içerisindeki mesajları ayıklamaya çalışıyoruz. Tabi bu işlemler sırasında olabilecek hata durumları için seal.reject’lerimizi yerleştirmeyi unutmuyoruz.

CoreService class’ımızın son kısmı ise yukarıda görmüş olduğunuz fonkysonlardan oluşmakta. Bu kısımı oluşturmamın sebebi kodun okunabilirliğini artırmak ve Middle Service tarafındaki yükü azaltmak. Her method tipi için bi fonksyon oluşturarak hem request fonksyonu hem de Middle Service kısmında iş yükünü azaltıyoruz. Ayrıca bu fonksyonlar içerisinde fullUrl’de elde etmiş oluyoruz.

Middle Service

Bu katmanın 2 ana görevi bulunmakta. Bunlardan ilki CoreService tarafından gelen çağrı cevabının hangi tip model’e göre decode edileceği. İkinci görevi ise parametreleri CoreService katmanına [String:Any] tipinde göndermemiz sağlamaktır. Yani elimizdeki custom objeleri dictionary tipine çevirme işlemini yapmaktır. Bu çevirme işlemi için customobje.dictonary’i kullanmaktayım. Tabiki bunun için ufak bi extension yazmamız gerekiyor.

Ayrıca bu katman için eklemek istediğim bi konu da getById kısmı Link Üzerinden Parametre Gönderme için bir örnek ve getByName de Link Üzerinden Query İle Parametre Gönderme’ye güzel örneklerdir.

ViewController

Bu kısımda çağrı işlemlerini başlatıyoruz. Görmüş olduğunuz üzere PromiseKit ile tek bir çağrı veya ard arda yapılmış çağrıların örnekleri bulunmakta.

Bu kısım için açıklayacağım tek kısım .then, .done kısımları. İlk olarak .then’i eğer ki ilk çağrı sonrasında başka çağrı yapacak isek kullanıyorum ve son çağrının sonuna .done kullanmamız gerekiyor.(getByNamePressed içindeki gibi.) Sadece tek bir çağrı yapacak isek o zaman ilk fonksyonun sonuna .done koymamız gerekmekte. (getAllPressed içindeki gibi.)

Bonus

Örneklerde kullanmış olduğum örnek endPoint’ler için kendi geliştirdiğim localde çalıştırabileceğiniz bi Rest Api bulunmakta. Bu local api’ye aşağıdaki link’ten nasıl kullanabileceğinizi açıklanmakta. Ayrıca projenin kaynak dosyalarında servisten dönen objeleri decode etmiş olduğum objelerim yer almakta. Hepsini incelemenizin size çok şey katacağına inanmaktayım.

Sormak istediğiniz veya daha fazla detaylı bilgi almak isterseniz her zaman LinkedIn üzerinden bana ulaşabilirsiniz. İyi çalışmalar.

--

--