Veri Yönetimi – CoreData

Üniversite yıllarında bitirme projemi Pocket PC’ler üzerine yapmıştım. Belki mobil uygulamaya ilgimin kaynağı o dönemlere dayanıyordur. Bu arkadaşlar üzerinden Windows CE (compact edition) çalışmaktaydı. Client Windows, Server Windows olunca, bu cihazlardan direk olarak server’daki bir SqlServer veritabanına erişmekte de bir sorun yoktu. Bende iOS’a ilk başladığım dönemlerde bu beklentiye girmiştim. Sanırım benzer beklentiye giren arkadaşlar çok ki bununla ilgili bir beklenti var. CoreData öncesinde bununla ilgili konuşalım biraz. Mobil uygulama geliştirirken veriyi saklamak için 2 alternatifiniz var. Cihaz üzerinde veriyi saklayabilirsiniz veya bunu bir server’a taşımanız gerekli. Cihaz üzerinde veriyi saklamak için nasıl alternatiflerimiz var bunu konuşacağız. Server’daki bir veritabanına bir veriyi aktarmak için bilmeniz gereken şu; bir mobil cihazdan connectionString kullanarak bir uzak veritabanına erişme şansınız bulunmamakta. Bunun içinde araya bir webservice oluşturuyor ve bu web servisler üzerinden veri alış/verişini yapıyoruz.

iOS destekli cihazlarda veri saklayabilmek için farklı yöntemler var.

  • NSUserDefaults içerisinde dictionary formatında verileri saklayabilirsiniz. Fakat bu bir veritabanı mantığıyla büyük boyutlu verileri saklamak için kullanabileceğiniz bir yapı değildir. Örneğin kullanıcının login olurken kullandığı email adresini sonraki login işlemlerinde hatırlatmak istiyorsanız bu bilgiyi UserDefaults içerisinde saklayabilirsiniz.
  • Daha güvenli olarak benzer şekilde verileri saklamak için KeyChain’i kullanabilirsiniz.
  • XML veya Json tarzı bir formatla dosya içerisinde verileri oluşturup, cihaz üzerinde veriyi saklayabilirsiniz. Ama yine veritabanı mantığıyla veri sorgulayabileceğiniz bir yapı olmayacaktır.
  • Ve gelelim konumuza. Cihaz üzerinde bir veritabanına ihtiyacınız var ise… Bunu gerçekleştirmek için db olarak Sqlite kullanmak gerekmektedir. 2 farklı alternatifimiz var bu durumda.
    • Sqlite db’sini projeniz içerisine yerleştirip, libsqlite3 library’sini kullanarak verileri sorgulayıp, saklayabilirsiniz. Sql kullanmak çok esnek olmanızı sağlayacaktır. Fakat libsqlite bir C kütüphanesi. Objective-C ile çalışırken bile veri tipi uyumsuzlukları söz konusu olmaktaydı. Swift’te ise bu durum daha yorucu bir hal almış durumda. C içerisindeki bir çok veri tipi UnsafePointer<> olarak Swift içerisinde kullanılmak durumunda.
    • Nihayet asıl anlatmak istediğime gelebildim. CoreData temelde SqlLite veritabanını kullanmaktadır fakat direk olarak SqlLite ile pek işiniz yok doğrusu. XCode veritabanı tablolarınızı tanımlamanızı ve yönetmenizi sağlamaktadır. Adım adım bu işlemleri nasıl gerçekleştirebileceğimizi anlatalım.

CoreData ile Çalışmaya Başlamak

Projeye yeni başlıyorsunuz ve başlarken CoreData kullanmaya karar vermiş durumdasınız. Bu durumda yeni proje oluştururken aşağıdaki adımda “Use Core Data” seçimini yapmalısınız. Bu durum da başlangıç için gerekli nesneler projeye dahil edilecektir. Projeniz oluştuğu zaman Projeİsmi.xcdatamodeld isminde bir dosyanın oluştuğunu göreceksiniz.

XCDATAMODEL veri modelinizi yani tablolarımızı oluşturacağınız yapıdır. Veritabanı ile Model birbirinden bağımsız yapılar, bunların arasındaki ayrıma dikkat etmeniz gerekmektedir. Veritabanınızı aslında Xcode içerisindeki model üzerinden yönetiyorsunuz. Modelinize bir Entity eklediğiniz zaman bunun eşdeğeri olarak bir tablo veritabanında oluşturuluyor. Siz Modelinize bir kayıt ekledikten veya bir modelin herhangi bir alanını güncelledikten sonra bu direk veritabanınıza yansımayacaktır. Nasıl veritabanına uygulanacağını konunun ilerleyen bölümlerinde konuşacağız.

Şimdi veritabanına ilk tablomuzu ekleyelim. Bunu gerçekleştirebilmek için proje dosyaları içerisinde göreceğiniz .xcdatamodeld uzantılı dosyayı seçiniz. Açılan ekranında alt bölümünde AddEntity üzerinden işlemi devam ettirerek veri modellerinizi ekleyebilirsiniz. Örneğin bir User tablosu eklemek için;

  • AddEntity sonrasında sol üst bölümdeki Entities altına Entity isminde yeni bir tablo eklenecektir. Üzerine tıklayarak ismini değiştirip User yapabilirsiniz.
  • Tablo alanlarını eklemek için orta alanda Attributes bölümünde eklemeler yapabilirsiniz. Oluşturulan basit bir tablonun son halini alttaki resimde görebilirsiniz.

Bu şekilde oluşturduğunuz her bir Entity karşılığında Sqlite üzerinde bir tablo oluşturulacaktır, Document dosyası içerisinde bulabilirsiniz.

Şimdi bu tablo üzerinde temel işlemleri nasıl gerçekleştirebileceğimizi konuşalım. Öncelikle db’ye erişim için kullanacağımız NSPersistentContainer nesnemizi oluşturacak kodu implement edelim. Aşağıdaki kodu AppDelegate.swift içerisine ekleyelim.

 

lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "Blog")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            fatalError("Hata alındı: \(error), \(error.userInfo)")
        }
    })
    return container
}()
func saveContext () {
    let context = persistentContainer.viewContext
    if context.hasChanges {
        do {
            try context.save()
        } catch {
            let nserror = error as NSError
            fatalError("Hata alındı: \(nserror), \(nserror.userInfo)")
        }
    }
}

Appdelegate üzerinde yukarıda yaptığımız eklentiler için bazı açıklamalar yapmak istiyorum.
** NSPersistentContainer nesnesi veritabanınıza karşılık gelmekte. CoreData projenizin ismiyle aynı oluşturduğu için veritabanını “Blog” olarak kullandık. Siz bu bölümü kendi proje isminize uygun olarak düzenlemelisiniz.

** saveContext methodu modeliniz üzerinde herhangi bir ekleme, silme vb. işlemler yapmanız durumunda işlemlerin veritabanına yansıması için çağırmanız gereken methoddur. Dikkat ederseniz saveContext içerisinde öncelikle hasChanges kontrolü yapılarak, model üzerinde herhangi bir değişiklik olup olmadığı kontrol edilmektedir. Siz birden fazla Model üzerinde farklı işlemler yapıp saveContext methodunu çağırdığınız zaman, tüm yapılan değişiklikler veritabanına uygulanacaktır.

İkinci adımda projenize DBManager.swift nesnesini ekleyelim. Veritabanı üzerinde yapacağınız tüm işlemleri bu nesne içerisinde tanımlayabilirsiniz. Bu eklemeler sonrasında DBManager nesnesinin son hali aşağıdaki gibi olacaktır.

import CoreData
class DBManager: NSObject {
    class func insertUser(user : User){
        let appDelegate = (UIApplication.shared.delegate as! AppDelegate)
        let context = appDelegate.persistentContainer.viewContext

        if let entity = NSEntityDescription.entity(forEntityName: "User", in: context) {

            let user = User(entity: entity, insertInto: context)
            user.name = "Sefa"
            user.surname = "Ayçiçek"
            user.birthDate = Date()
            user.username = "username"
            user.pasword = "123"
            appDelegate.saveContext()
        }
    }
}

Yukarıdaki insertUser methodu içerisinde bir User nesnesi oluşturup kaydedilmesini sağladık. Methodun bir User parametresi alarak işlem yapmasını sağlayabilirsiniz. Hazır insert etmişken bir sonraki adıma geçerek eklenmiş tüm kayıtları çekebilmek için getAllUsers methodunu ekleyelim. Bu methodu bir öncekiyle aynı şekilde DBManager sınıfına eklemelisiniz.

Burada önemli bir nokta veri üzerinde filtreleme yapmaktır. Standart sql sorguları yaparak bunu yapamayacağız fakat alternatif olarak NSPredicate sınıfı üzerinden sorgulamalar yapabilmektedir. Bunun için getUsers methodu NSPredicate tipinde bir optional parametre almaktadır. Tüm kullanıcıları almak için bu parametreyi nil olarak gönderebilirsiniz.

class func getUsers(filter : NSPredicate?)->[User]?{
    let appDelegate = (UIApplication.shared.delegate as! AppDelegate)
    let context = appDelegate.persistentContainer.viewContext
    let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
    let request: NSFetchRequest = User.fetchRequest()
    request.entity = entity
    request.predicate = filter
    do {
        if let results = try context.fetch(request as! NSFetchRequest) as? [User] {
            return results
        }
    } catch let error {
    }
    return nil
}

Son adımda ViewController nesnenizin viewDidLoad methodunu aşağıdaki şekilde değiştirerek denemeler yapabilirsiniz.

override func viewDidLoad() {
    super.viewDidLoad()

    DBManager.insertUser()
    let allUserList = DBManager.getUsers(filter: nil) // Tüm kullanıcılar

    let user = "Sefa"

    let pred = NSPredicate(format: "(name = %@)", user) //. İsmi Sefa olan kullanıcılar
    let filteredUserList = DBManager.getUsers(filter: pred)
}

Temel anlamda CoreData kullanarak neler ve nasıl yapabileceğinizi anlatmaya çalıştım. Sanırım bu işlemler üzerine tek eksik kalan bölüm bir update / delete olabilir. Siz herhangi bir nesne üzerinde değişiklik yaptıktan sonra saveContext methodunu tetiklerseniz, yaptığınız bu değişiklikler db’ye yansıyacaktır. Örnek olması açısında üst bölümler aldığımız allUserList array’i içersindeki tüm User nesnelerinin password alanlarını 123 olacak şekilde update edelim. Kod aşağıdaki şekilde olacaktır.

let appDelegate = (UIApplication.shared.delegate as! AppDelegate)
if let allUserList = DBManager.getUsers(filter: nil) {
    for user in allUserList {
        user.pasword = "123"
    }
}
appDelegate.saveContext()

Benzer şekilde allUserList içerisindeki tüm User’ları veritabanından silmek için aşağıdaki kodu kullanabilirsiniz.

let appDelegate = (UIApplication.shared.delegate as! AppDelegate)
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
if let allUserList = DBManager.getUsers(filter: nil) {
    for user in allUserList {
        context.delete(user)
    }
}
appDelegate.saveContext()

Anlatmak istediklerimin sonuna geldim sanırım. İçeriği zenginleştirmek isterseniz veya sorularınız var ise yorum olarak paylaşınız lütfen.

Share on FacebookShare on Google+Tweet about this on TwitterShare on LinkedIn
Bu yazıyı beğendiyseniz daha fazla kişiye ulaşmasını sağlamak için paylaşabilirsiniz.

2 comments

Hocam Merhaba,

CoreData içerisine kaydedilen verileri nasıl görebiliriz? Mysql gibi hücre hücre mantıı mı var yoksa kapalı bir kutu mu?

Merhaba,
Yazının içinde de anlatmaya çalışmıştım aslında.CoreData’da db olarak sqlite kullanıyor. Yani veriler sqlite içerisinde tutuluyor ve bu erişilebilir bir dosya. .sqlite dosyasına erişmek için aşağıdaki kodu kullanarak path’i almanız gerekmektedir. Sonrasında dosyayı incelemek mümkün.

persistentContainer.persistentStoreCoordinator.persistentStores.first?.url

Comment to ilker Cevabı iptal et