🎯 PredicateKit allows Swift developers to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions.

Overview

🎯 PredicateKit

GitHub Workflow Status (branch) GitHub release (latest SemVer)

PredicateKit is an alternative to NSPredicate allowing you to write expressive and type-safe predicates for CoreData using key-paths, comparisons and logical operators, literal values, and functions.

Contents

Motivation

CoreData is a formidable piece of technology, however not all of its API has caught up with the modern Swift world. Specifically, fetching and filtering objects from CoreData relies heavily on NSPredicate and NSExpression. Unfortunately, a whole range of bugs and runtime errors can easily be introduced using those APIs. For instance, we can compare a property of type String to a value of type Int or even use a non-existant property in a predicate; these mistakes will go un-noticed at compile time but can cause important errors at runtime that may not be obvious to diagnose. This is where PredicateKit comes in by making it virtually impossible to introduce these types of errors.

Concretely, PredicateKit provides

  • a type-safe and expressive API for writing predicates. When using PredicateKit, all properties involved in your predicates are expressed using key-paths. This ensures that the usage of inexistant properties or typos are caught at compile time. Additionally, all operations such as comparisons, functions calls, etc. are strongly-typed, making it impossible to write invalid predicates.
  • an improved developer experience. Enjoy auto-completion and syntax highlighting when writing your predicates. In addition, PredicateKit is just a lightweight replacement for NSPredicate, no major change to your codebase is required, no special protocol to conform to, no configuration, etc. Simply import PredicateKit, write your predicates and use the functions NSManagedObjectContext.fetch(where:) or NSManagedObjectContext.count(where:) to execute them.

Installation

Carthage

Add the following line to your Cartfile.

1.0.0 ">
github "ftchirou/PredicateKit" ~> 1.0.0

CocoaPods

Add the following line to your Podfile.

pod 'PredicateKit', ~> '1.0.0'

Swift Package Manager

Update the dependencies array in your Package.swift.

dependencies: [
  .package(url: "https://github.com/ftchirou/PredicateKit", .upToNextMajor(from: "1.0.0"))
]

Quick start

Fetching objects

To fetch objects using PredicateKit, use the function fetch(where:) on an instance of NSManagedObjectContext passing as argument a predicate. fetch(where:) returns an object of type FetchRequest on which you call result() to execute the request and retrieve the matching objects.

Example
let notes: [Note] = try managedObjectContext
  .fetch(where: \Note.text == "Hello, World!" && \Note.creationDate < Date())
  .result()

You write your predicates using the key-paths of the entity to filter and a combination of comparison and logical operators, literal values, and functions calls.

See Writing predicates for more about writing predicates.

Fetching objects as dictionaries

By default, fetch(where:) returns an array of subclasses of NSManagedObject. You can specify that the objects be returned as an array of dictionaries ([[String: Any]]) simply by changing the type of the variable storing the result of the fetch.

Example
let notes: [[String: Any]] = try managedObjectContext
  .fetch(where: \Note.text == "Hello, World!" && \Note.creationDate < Date())
  .result()

Configuring the fetch

fetch(where:) returns an object of type FetchRequest. You can apply a series of modifiers on this object to further configure how the objects should be matched and returned. For example, sorted(by: \Note.creationDate, .descending) is a modifier specifying that the objects should be sorted by the creation date in the descending order. A modifier returns a mutated FetchRequest; a series of modifiers can be chained together to create the final FetchRequest.

Example
let notes: [Note] = try managedObjectContext
  .fetch(where: (\Note.text).contains("Hello, World!") && \Note.creationDate < Date())
  .limit(50) // Return 50 objects matching the predicate.
  .offset(100) // Skip the first 100 objects matching the predicate.
  .sorted(by: \Note.creationDate) // Sort the matching objects by their creation date.
  .result()

See Request modifiers for more about modifiers.

Fetching objects with the @FetchRequest property wrapper

PredicateKit extends the SwiftUI @FetchRequest property wrapper to support type-safe predicates. To use, simply initialize a @FetchRequest with a predicate.

Example
var body: some View { List(notes, id: \.self) { Text($0.text) } } } ">
import PredicateKit
import SwiftUI

struct ContentView: View {

  @SwiftUI.FetchRequest(predicate: \Note.text == "Hello, World!")
  var notes: FetchedResults

  
   var body: 
   some View {
    
   List(notes, 
   id: \.
   self) {
      
   Text(
   $0.
   text)
    }
  }
}
  

You can also initialize a @FetchRequest with a full-fledged request with modifiers and sort descriptors.

Example
var body: some View { List(notes, id: \.self) { Text($0.text) } } } ">
import PredicateKit
import SwiftUI

struct ContentView: View {

  @SwiftUI.FetchRequest(
    fetchRequest: FetchRequest(predicate: (\Note.text).contains("Hello, World!"))
      .limit(50)
      .offset(100)
      .sorted(by: \Note.creationDate)
  )
  var notes: FetchedResults

  
   var body: 
   some View {
    
   List(notes, 
   id: \.
   self) {
      
   Text(
   $0.
   text)
    }
  }
}
  

Both initializers accept an optional parameter animation that will be used to animate changes in the fetched results.

Example
var body: some View { List(notes, id: \.self) { Text($0.text) } } } ">
import PredicateKit
import SwiftUI

struct ContentView: View {

  @SwiftUI.FetchRequest(
    predicate: (\Note.text).contains("Hello, World!"),
    animation: .easeInOut
  )
  var notes: FetchedResults

  
   var body: 
   some View {
    
   List(notes, 
   id: \.
   self) {
      
   Text(
   $0.
   text)
    }
  }
}
  

Fetching objects with an NSFetchedResultsController

In UIKit, you can use fetchedResultsController() to create an NSFetchedResultsController from a configured fetch request. fetchedResultsController has two optional parameters:

  • sectionNameKeyPath is a key-path on the returned objects used to compute section info
  • cacheName is the name of a file to store pre-computed section info.
Example
let controller: NSFetchedResultsController 
   = managedObjectContext
  .
   fetch(
   where: \Note.
   text 
   == 
   "Hello, World!" 
   && \Note.
   creationDate 
   < 
   Date())
  .
   sorted(
   by: \Note.
   creationDate, .
   descending)
  .
   fetchedResultsController(
   sectionNameKeyPath: \Note.
   creationDate)
  

Counting objects

To count the number of objects matching a predicate, use the function count(where:) on an instance of NSManagedObjectContext.

Example
let count = try managedObjectContext.count(where: (\Note.text).beginsWith("Hello"))

Documentation

Writing predicates

Predicates are expressed using a combination of comparison operators and logical operators, literal values, and functions.

Comparisons

Basic comparisons

A comparison can be expressed using one of the basic comparison operators <, <=, ==, >=, and > where the left hand side of the operator is a key-path and the right hand side of the operator is a value whose type matches the value type of the key-path on the left hand side.

Example
< Date() // Matches all notes where the number of views is at least 120. let predicate = \Note.numberOfViews >= 120 ">
class Note: NSManagedObject {
  @NSManaged var text: String
  @NSManaged var creationDate: Date
  @NSManaged var numberOfViews: Int
  @NSManaged var tags: [String]
}

// Matches all notes where the text is equal to "Hello, World!".
let predicate = \Note.text == "Hello, World!"

// Matches all notes created before the current date.
let predicate = \Note.creationDate < Date()

// Matches all notes where the number of views is at least 120.
let predicate = \Note.numberOfViews >= 120

String comparisons

If the property to compare is of type String, comparisons can be additionally expressed with special functions such as beginsWith, contains, or endsWith.

// Matches all notes where the text begins with the string "Hello".
let predicate = (\Note.text).beginsWith("Hello")

// Matches all notes where the text contains the string "Hello".
let predicate = (\Note.text).contains("Hello")

// Matches all notes where the text matches the specified regular expression.
let predicate = (\Note.text).matches(NSRegularExpression(...))

Any of the following functions can be used in a string comparison predicate.

  • beginsWith
  • contains
  • endsWith
  • like
  • matches

These functions accept a second optional parameter specifying how the string comparison should be performed.

// Case-insensitive comparison.
let predicate = (\Note.text).beginsWith("Hello, World!", .caseInsensitive)

// Diacritic-insensitive comparison.
let predicate = (\Note.text).beginsWith("Hello, World!", .diacriticInsensitive)

// Normalized comparison.
let predicate = (\Note.text).beginsWith("Hello, World!", .normalized)

Membership checks

between

You can use the between function or the ~= operator to determine whether a property's value is within a specified range.

// Matches all notes where the number of views is between 100 and 200.
let predicate = (\Note.numberOfViews).between(100...200)

// Or
let predicate = \Note.numberOfViews ~= 100...200
in

You can use the in function to determine whether a property's value is one of the values in a specified list.

// Matches all notes where the text is one of the elements in the specified list.
let predicate = (\Note.text).in("a", "b", "c", "d")

When the property is of type String, in accepts a second parameter that determines how the string should be compared to the elements in the list.

// Case-insensitive comparison.
let predicate = (\Note.text).in(["a", "b", "c", "d"], .caseInsensitive)

Compound predicates

Compound predicates are predicates that logically combine one, two or more predicates.

AND predicates

AND predicates are expressed with the && operator where the operands are predicates. An AND predicate matches objects where both its operands match.

= 120 ">
// Matches all notes where the text begins with 'hello' and the number of views is at least 120.
let predicate = (\Note.text).beginsWith("hello") && \Note.numberOfViews >= 120

OR Predicates

OR predicates are expressed with the || operator where the operands are predicates. An OR predicate matches objects where at least one of its operands matches.

// Matches all notes with the text containing 'hello' or created before the current date.
let predicate = (\Note.text).contains("hello") || \Note.creationDate < Date()

NOT Predicates

NOT predicates are expressed with the unary ! operator with a predicate operand. A NOT predicate matches all objects where its operand does not match.

// Matches all notes where the text is not equal to 'Hello, World!'
let predicate = !(\Note.text == "Hello, World!")

Array operations

You can perform operations on properties of type Array (or expressions that evaluate to values of type Array) and use the result in a predicate.

Select an element in an array

first
// Matches all notes where the first tag is 'To Do'..
let predicate = (\Note.tags).first == "To Do"
last
// Matches all notes where the last tag is 'To Do'..
let predicate = (\Note.tags).last == "To Do"
at(index:)
// Matches all notes where the third tag contains 'To Do'.
let predicate = (\Note.tags).at(index: 2).contains("To Do")

Count the number of elements in an array

count
// Matches all notes where the number of elements in the `tags` array is less than 5.
let predicate = (\Note.tags).count < 5

// or

let predicate = (\Note.tags).size < 5

Combine the elements in an array

If the elements of an array are numbers, you can combine or reduce them into a single number and use the result in a predicate.

class Account: NSManagedObject {
  @NSManaged var purchases: [Double]
}
sum
// Matches all accounts where the sum of the purchases is less than 2000.
let predicate = (\Account.purchases).sum < 2000
average
// Matches all accounts where the average purchase is 120.0
let predicate = (\Account.purchases).average == 120.0
min
// Matches all accounts where the minimum purchase is 98.5.
let predicate = (\Account.purchases).min == 98.5
max
// Matches all accounts where the maximum purchase is at least 110.5.
let predicate = (\Account.purchases).max >= 110.5

Aggregate comparisons

You can also express predicates matching all, any, or none of the elements of an array.

all
// Matches all accounts where every purchase is at least 95.0
let predicate = (\Account.purchases).all >= 95.0
any
// Matches all accounts having at least one purchase of 20.0
let predicate = (\Account.purchases).any == 20.0
none
// Matches all accounts where no purchase is less than 50.
let predicate = (\Account.purchases).none <= 50

Predicates with one-to-one relationships

If your object has a one-to-one relationship with another one, you can target any property of the relationship simply by using the appropriate key-path.

Example
class User: NSManagedObject {
  @NSManaged var name: String
  @NSManaged var billingInfo: BillingInfo
}

class BillingInfo: NSManagedObject {
  @NSManaged var accountType: String
  @NSManaged var purchases: [Double]
}

// Matches all users with the billing account type 'Pro'
let predicate = \User.billingInfo.accountType == "Pro"

// Matches all users with an average purchase of 120
let predicate = (\User.billingInfo.purchases).average == 120.0

Predicates with one-to-many relationships

You can run aggregate operations on a set of relationships using the all(_:), any(_:), or none(_:) functions.

Example
class Account: NSManagedObject {
  @NSManaged var name: String
  @NSManaged var profiles: Set
   
}


   class 
   Profile: 
   NSManagedObject {
  
   @NSManaged 
   var name: 
   String
  
   @NSManaged 
   var creationDate: 
   String
}


   // Matches all accounts where all the profiles have the creation date equal to the specified one.

   
   let predicate 
   = (\Account.
   profile).
   all(\.
   creationDate) 
   == date


   // Matches all accounts where any of the associated profiles has a name containing 'John'.

   
   let predicate 
   = (\Account.
   profiles).
   any(\.
   name).
   contains(
   "John"))


   // Matches all accounts where no profile has the name 'John Doe'

   
   let predicate 
   = (\Account.
   profiles).
   none(\.
   name) 
   == 
   "John Doe"
  

Sub-predicates

When your object has one-to-many relationships, you can create a sub-predicate that filters the "many" relationships and use the resuult of the sub-predicate in a more complex predicate. Sub-predicates are created using the global all(_:where:) function. The first parameter is the key-path of the collection to filter and the second parameter is a predicate that filters the collection.

all(_:where:) evaluates to an array; that means you can perform any valid array operation on its result such as size, first, etc.

Example
// Matches all the accounts where the name contains 'Account' and where the number of profiles whose
// name contains 'Doe' is exactly 2.
let predicate = (\Account.name).contains("Account") 
  && all(\.profiles, where: (\Profile.name).contains("Doe")).size == 2)

Request modifiers

You can configure how matching objects are returned by applying a chain of modifiers to the object returned by NSManagedObjectContext.fetch(where:).

Example
let notes: [Note] = try managedObjectContext
  .fetch(where: (\Note.text).contains("Hello, World!") && \Note.creationDate < Date())
  .limit(50) // Return 50 objects matching the predicate.
  .offset(100) // Skip the first 100 objects matching the predicate.
  .sorted(by: \Note.text) // Sort the matching objects by their creation date.
  .result()

limit

Specifies the number of objects returned by the fetch request.

Usage
managedObjectContext.fetch(where: ...)
  .limit(50)
NSFetchRequest equivalent

fetchLimit

offset

Specifies the number of initial matching objects to skip.

Usage
managedObjectContext.fetch(where: ...)
  .offset(100)
NSFetchRequest equivalent

fetchOffset

batchSize

Specifies the batch size of the objects in the fetch request.

Usage
managedObjectContext.fetch(where: ...)
  .batchSize(80)
NSFetchRequest equivalent

fetchBatchSize

prefetchingRelationships

Specifies the key-paths of the relationships to prefetch along with objects of the fetch request.

Usage
managedObjectContext.fetch(where: ...)
  .prefetchingRelationships(\.billingInfo, \.profiles)
NSFetchRequest equivalent

relationshipKeyPathsForPrefetching

includingPendingChanges

Specifies whether changes unsaved in the managed object context are included in the result of the fetch request.

Usage
managedObjectContext.fetch(where: ...)
  .includingPendingChanges(true)
NSFetchRequest equivalent

includesPendingChanges

fromStores

Specifies the persistent stores to be searched when the fetch request is executed.

Usage
let store1: NSPersistentStore = ...
let store2: NSPersistenStore = ...

managedObjectContext.fetch(where: ...)
  .fromStores(store1, store2)
NSFetchRequest equivalent

affectedStores

fetchingOnly

Specifies the key-paths to fetch.

Usage
managedObjectContext.fetch(where: ...)
  .fetchingOnly(\.text, \.creationDate)
NSFetchRequest equivalent

propertiesToFetch

returningDistinctResults

Specifies whether the fetch request returns only distinct values for the key-paths specified by fetchingOnly(_:).

Usage
managedObjectContext.fetch(where: ...)
  .fetchingOnly(\.text, \.creationDate)
  .returningDistinctResults(true)
NSFetchRequest equivalent

returnsDistinctResults

groupBy

Specifies the key-paths of the properties to group the result by, when the result of the request is of type [[String: Any]].

Usage
let result: [[String: Any]] = managedObjectContext.fetch(where: ...)
  .groupBy(\.creationDate)
NSFetchRequest equivalent

propertiesToGroupBy

refreshingRefetchedObjects

Specifies whether the property values of fetched objects will be updated with the current values in the persistent store.

Usage
managedObjectContext.fetch(where: ...)
  .shouldRefreshRefetchedObjects(false)
NSFetchRequest equivalent

shouldRefreshRefetchedObjects

having

pecifies the predicate to use to filter objects returned by a request with a groupBy(_:) modifier applied.

Usage
let result: [[String: Any]] = managedObjectContext.fetch(where: ...)
  .groupBy(\.creationDate)
  .having((\Note.text).contains("Hello, World!"))
NSFetchRequest equivalent

havingPredicate

includingSubentities

Specifies whether subentities are included in the result.

Usage
managedObjectContext.fetch(where: ...)
  .includingSubentities(true)
NSFetchRequest equivalent

includesSubentities

returningObjectsAsFaults

Specifies whether objects returned from the fetch request are faults.

Usage
managedObjectContext.fetch(where: ...)
  .returningObjectsAsFaults(true)
NSFetchRequest equivalent

returnsObjectsAsFaults

sorted

Specifies how the objects returned by the request should be sorted. This modifier takes one required parameter and 2 optional ones:

  • by: the key-path by which to sort the objects. (Required)
  • order: the order in which to sort the object. (Optional, defaults to .ascending)
  • comparator: a custom comparator to use to sort the objects. (Optional, defaults to nil)
Usage
managedObjectContext.fetch(where: ...)
  .sorted(by: \.text)
  .sorted(by: \.creationDate, .descending)

Debugging

In DEBUG mode, you can inspect the actual NSFetchRequests that are being executed by using the modifier inspect(on:) on a FetchRequest.

Example
struct Inspector: NSFetchRequestInspector {
  func inspect<Result>(_ request: NSFetchRequest
   ) {
    
   // Log or print the request here.

     }
}


   let notes: [Note] 
   = 
   try managedObjectContext
  .
   fetch(
   where: \Note.
   text 
   == 
   "Hello, World!")
  .
   sorted(
   by: \Note.
   creationDate, .
   descending)
  .
   inspect(
   on: 
   Inspector())
  .
   result()
  

Happy coding! ⚡️

Comments
  • Mapping to NSPredicate and NSSortDescriptor

    Mapping to NSPredicate and NSSortDescriptor

    Using PredicateKit version 1.4.0

    I need to map the generated predicate or request back to CoreData classes.

    A short demo using the examples in readme file:

    let predicate: Predicate<Note> = \Note.text == "Hello, World!" && \Note.creationDate < Date()
    
    let controller: NSFetchedResultsController<Note> = managedObjectContext
        .fetch(where: predicate)
        .sorted(by: \Note.creationDate, .descending)
        .fetchedResultsController(sectionNameKeyPath: \Note.creationDate)
    
    controller.fetchRequest.predicate = ???
    

    I need help on using predicate: Predicate<Note> as NSPredicate

    I am asking for something similar to the function fileprivate func makePredicate<Root>(from predicate: Predicate<Root>) -> NSPredicate in NSFetchRequestBuilder.swift

    another example is using either NSPredicate or NSSortDescriptor on NSArray

    opened by omiz 5
  • Add new sortDescriptor to FetchRequest extensions

    Add new sortDescriptor to FetchRequest extensions

    Hey, thanks for this nice library!

    I am using iOS 15, which added a new property to the SwiftUI.FetchRequest, a sortDescriptor. It would be nice, if you could add this property in your extensions too, with @available(ios15)

    Reference: https://developer.apple.com/documentation/swiftui/fetchrequest/init(sortdescriptors:predicate:animation:)-462jp?changes=latest_major

    opened by hfhbd 4
  • Switch to Swift 5.5 SortDescriptor

    Switch to Swift 5.5 SortDescriptor

    The SortDescriptor in Foundation is not compatible with your SortCriterion, because Apples implementation simple access the keyPath.name, whether SortCriterion requires a keyPath in the constructor. At the end, the new SortDescriptor in Foundation is finally a typed SortDescriptor, but unfortunately, your SortCriterion is more typed :D If you want to switch to the new SortDescriptor, this change would break your API, and it will require Swift 5.5.

    Fixes #8.

    opened by hfhbd 3
  • Optional One to Many relationship support

    Optional One to Many relationship support

    Is it possible to have a predicate for an optional one to Many relationship?

    An example of what I'm trying to achieve is this I want to get the objects without a relation

    class NoteGroup: NSManagedObject {
        var notes: [Note]?
    }
    
    class Note: NSManagedObject {
        var group: NoteGroup?
    }
    
    let predicate = (\Note.group) == nil
    

    unfortunately that currently doesn't work since NoteGroup is not a primitive type so it can't be passed as a fetch where condition

    enhancement good first issue 
    opened by omiz 2
  • Type 'Optional<Wrapped>' does not conform to protocol 'AnyArrayOrSet'

    Type 'Optional' does not conform to protocol 'AnyArrayOrSet'

    PredicateKit fails to build in Xcode 12.5 beta, with the following error in Predicate.swift at line 622:

    Type 'Optional' does not conform to protocol 'AnyArrayOrSet'

    opened by joel-perry 2
  • Question on related blog post: Designing modern data access layers in Swift

    Question on related blog post: Designing modern data access layers in Swift

    I'm trying to work through your blog post (thanks, learning a lot):

    • https://faical.dev/articles/modern-swift-data-access-layers.html

    I'm getting stuck on compile error for this code:

    extension Predicate {
        func isIncluded() -> (T) -> Bool {
            switch self {
            case let .comparison(keyPath, .greaterThan, value):
                return { $0[keyPath: keyPath] > value } // error!!!
    ...
    

    I am getting error:

    Binary operator '>' cannot be applied to operands of type 'Any' and 'Primitive'
    

    Any idea why this is happening or how I could work around it?

    Thanks, Jesse

    opened by jessegrosjean 1
  • Update SwiftUI support tests for iOS 15+

    Update SwiftUI support tests for iOS 15+

    iOS 15 changed how an NSFetchRequest is stored in a SwiftUI View graph. This PR updates SwiftUISupportTests to accommodate that change.

    Will be merged once Xcode 13.3 is available on GitHub Actions. Until then, it's expected that CI will fail.

    opened by ftchirou 0
  • Add support for the @FetchRequest property wrapper

    Add support for the @FetchRequest property wrapper

    Enables consumers to use the SwiftUI property wrapper @FetchRequest with PredicateKit.

    Example
    import PredicateKit
    import SwiftUI
    
    struct ContentView: View {
      @SwiftUI.FetchRequest(predicate: \Note.text == "Hello, World!")
      var notes: FetchedResults<Note>
    
      var body: some View {
        List(notes, id: \.self) {
          Text($0.text)
        }
      }
    }
    
    opened by ftchirou 0
  • Object comparison

    Object comparison

    So let's assume that we have these Entities

    class Person {
        let id: String
        let name: String
        let pets: [Cat]
        
        init(id: String, name: String, pets: [Cat]) {
            self.id = id
            self.name = name
            self.pets = pets
        }
    }
    
    
    class Cat {
        let id: String
        let name: String
        let owner: Person?
        
        init(id: String, name: String, owner: Person?) {
            self.id = id
            self.name = name
            self.owner = owner
        }
    }
    

    If I would like to query the cats that are related to a person I would assume that this should be possible

    let person1 = Person(id: "SomeID", name: "SomeName", pets: [])
    
    \Cat.owner == person1
    

    but because the Person object does not inherit from Primitive I can not use that expression instead I'm forced to write it as

    \Cat.owner?.id == person1.id
    

    Suggested solution

    A fix for this issue could be done by extending the Primitive type to hold Identifiable but that might break the meaning of the Primitive type since Identifiable can be a complex type

    opened by omiz 0
Releases(1.5.0)
Owner
Faiçal Tchirou
Faiçal Tchirou
Arctanyn 0 Dec 24, 2022
A type-safe, fluent Swift library for working with Core Data

Core Data Query Interface (CDQI) is a type-safe, fluent, intuitive library for working with Core Data in Swift. CDQI tremendously reduces the amount o

null 31 Oct 26, 2022
A type-safe, fluent Swift library for working with Core Data

Core Data Query Interface (CDQI) is a type-safe, fluent, intuitive library for working with Core Data in Swift. CDQI tremendously reduces the amount o

null 31 Oct 26, 2022
QueryKit, a simple type-safe Core Data query language.

QueryKit QueryKit, a simple type-safe Core Data query language. Usage QuerySet<Person>(context, "Person")

QueryKit 1.5k Dec 20, 2022
iOS app built with UIKit and programatic auto-layouts to learn and apply knowledge. Offline storage using CoreData and Caching

PurpleImage Search Pixabay for images and save them offline to share and view To use: Clone the GitHub project, build and run in Xcode. The API is com

Frederico Kückelhaus 0 May 10, 2022
Simple IOS notes app written programmatically without storyboard using UIKit and CoreData

Notes Simple Notes app. Swift, UIKit, CoreData Description Simple IOS notes app

null 4 Dec 23, 2022
A Swift framework that wraps CoreData, hides context complexity, and helps facilitate best practices.

Cadmium is a Core Data framework for Swift that enforces best practices and raises exceptions for common Core Data pitfalls exactly where you make the

Jason Fieldman 123 Oct 18, 2022
A small set of utilities to make working with CoreData and Swift a bit easier.

SuperRecord =================== SUPPORTS SWIFT 2.0 from Version >= 1.4 ** SUPPORTS SWIFT 1.2 from Version <= 1.3 Both iOS and WatchOS A Swift CoreData

Michael Armstrong 372 Jul 19, 2022
A Swift framework that wraps CoreData, hides context complexity, and helps facilitate best practices.

Cadmium is a Core Data framework for Swift that enforces best practices and raises exceptions for common Core Data pitfalls exactly where you make them.

Jason Fieldman 123 Oct 18, 2022
CoreData based Swift ORM

Swift ORM Features Pure swift objects - no more subclasses of NSManagedObject Extensible attribute system - store any type in CoreData storage by impl

Prisma Labs 89 Dec 29, 2022
CoreData + UI/Unit Tests + Pure Swift

What is it? ???? It's a Todo iOS Application with offline support by the use of Core Data which has been developed as a code challenge. It's written p

iMamad 1 Jun 1, 2022
DataKernel is a minimalistic wrapper around CoreData stack to ease persistence operations.

DataKernel What is DataKernel? DataKernel is a minimalistic wrapper around CoreData stack to ease persistence operations. It is heavily inspired by Su

Denis 16 Jun 2, 2022
SugarRecord is a persistence wrapper designed to make working with persistence solutions like CoreData in a much easier way.

What is SugarRecord? SugarRecord is a persistence wrapper designed to make working with persistence solutions like CoreData in a much easier way. Than

Modo 2.1k Dec 29, 2022
App agenda with CoreData

iOS Agenda App with CORE DATA Ejemplo de código para una aplicación de agenda, gestionando el modelo de datos con CoreData. Built using XCode 13.0 (Sw

Manu Martinez 1 Oct 19, 2021
Aplikasi iOS To Do List dengan Storyboard & Local CoreData (Database Xcode)

ToDoList Aplikasi iOS ToDoList adalah sebuah aplikasi CRUD sederhana berbasis iOS yang digunakan untuk membuat sebuah list item. Aplikasi ini dibuat m

DK 7 Aug 1, 2022
Write amazing, strong-typed and easy-to-read NSPredicate.

PredicateFlow Write amazing, strong-typed and easy-to-read NSPredicate. This library allows you to write flowable NSPredicate, without guessing attrib

Andrea Del Fante 103 Aug 12, 2022
A minimalistic, thread safe, non-boilerplate and super easy to use version of Active Record on Core Data.

Skopelos A minimalistic, thread-safe, non-boilerplate and super easy to use version of Active Record on Core Data. Simply all you need for doing Core

Alberto De Bortoli 235 Sep 9, 2022
Quillow is an elegant book management app on the App Store that allows you to search, add and track the books you've consumed.

Quillow Quillow is an elegant book management app on the App Store that allows you to search, add and track the books you've consumed. Please use the

Daniyal Mohammed 3 Aug 29, 2022
Core Data Generator (CDG for short) is a framework for generation (using Sourcery) of Core Data entities from plain structs/classes/enums.

Core Data Generator Introduction Features Supported platforms Installation CDG Setup RepositoryType ModelType DataStoreVersion MigrationPolicy Basic U

Lotusflare 18 Sep 19, 2022