Lightweight Framework for using Core Data with Value Types

Related tags

Guides CoreValue
Overview


Features

  • Uses Swift Reflection to convert value types to NSManagedObjects
  • iOS and Mac OS X support
  • Use with structs
  • Works fine with let and var based properties
  • Swift 5.0

Rationale

Swift introduced versatile value types into the iOS and Cocoa development domains. They're lightweight, fast, safe, enforce immutability and much more. However, as soon as the need for CoreData in a project manifests itself, we have to go back to reference types and @objc.

CoreValue is a lightweight wrapper framework around Core Data. It takes care of boxing value types into Core Data objects and unboxing Core Data objects into value types. It also contains simple abstractions for easy querying, updating, saving, and deleting.

Usage

The following struct supports boxing, unboxing, and keeping object state:

struct Shop: CVManagedPersistentStruct {

    // The name of the CoreData entity
    static let EntityName = "Shop"

    // The ObjectID of the CoreData object we saved to or loaded from
    var objectID: NSManagedObjectID?
    
    // Our properties
    let name: String
    var age: Int32
    var owner: Owner?

    // Create a Value Type from an NSManagedObject
    // If this looks too complex, see below for an explanation and alternatives
    static func fromObject(_ o: NSManagedObject) throws -> XShop {
        return try curry(self.init)
            <^> o <|? "objectID"
            <^> o <| "name"
            <^> o <| "age"
            <^> o <|? "owner"
    }
}

That's it. Everything else it automated from here. Here're some examples of what you can do with Shop then:

// Get all shops (`[Shop]` is required for the type checker to get your intent!)
let shops: [Shop] = Shop.query(self.context, predicate: nil)

// Create a shop
let aShop = Shop(objectID: nil, name: "Household Wares", age: 30, owner: nil)

// Store it as a managed object
aShop.save(self.context)

// Change the age
aShop.age = 40

// Update the managed object in the store
aShop.save(self.context)

// Delete the object
aShop.delete(self.context)

// Convert a managed object into a shop (see below)
let nsShop: Shop? = try? Shop.fromObject(aNSManagedObject)

// Convert a shop into an nsmanagedobject
let shopObj = nsShop.mutatingToObject(self.context)

Querying

There're two ways of querying objects from Core Data into values:

// With Sort Descriptors
public static func query(context: NSManagedObjectContext, predicate: NSPredicate?, sortDescriptors: [NSSortDescriptor]) -> Array
    
// Without sort descriptors
public static func query(context: NSManagedObjectContext, predicate: NSPredicate?) -> Array

If no NSPredicate is given, all objects for the selected Entity are returned.

Usage in Detail

CVManagedPersistentStruct is a typealias for the two primary protocols of CoreValue: BoxingPersistentStruct and UnboxingStruct.

Let's see what they do.

BoxingPersistentStruct

Boxing is the process of taking a value type and returning an NSManagedObject. CoreValue really loves you and that's why it does all the hard work for you via Swift's Reflection feature. See for yourself:

struct Counter: BoxingStruct
    static let EntityName = "Counter"
    var count: Int
    let name: String
}

That's it. Your value type is now CoreData compliant. Just call aCounter.toObject(context) and you'll get a properly encoded NSManagedObject!

If you're interested, have a look at the internalToObject function in CoreValue.swift, which takes care of this.

Boxing in Detail

Keen observers will have noted that the structure above actually doesn't implement the BoxingPersistentStruct protocol, but instead something different called BoxingStruct, what's happening here?

By default, Value types are immutable, so even if you define a property as a var, you still can't change it from within except by declaring your function mutable. Swift also doesn't allow us to define properties in protocol extensions, so any state that we wish to assign on a value type has to be via specific properties on the value type.

When we create or load an NSManagedObject from CoreData, we need a way to store the connection to the original NSManagedObject in the value type. Otherwise, calling save again (say after updating the value type) would not update the NSManagedObject in question, but instead insert a new NSManagedObject into the store. That's obviously not what we want.

Since we cannot implicitly add any state whatsoever to a protocol, we have to do this explicitly. That's why there's a separate protocol for persistent storage:

struct Counter: BoxingPersistentStruct
    let EntityName = "Counter"

    var objectID: NSManagedObjectID?

    var count: Int
    let name: String
}

The main difference here is the addition of objectID. Once this property is there, BoxingPersistentStruct's bag of wonders (.save, .delete, .mutatingToObject) can be used.

What's the usecase of the BoxingStruct protocol then, you may ask. The advantage is that BoxingStruct does not require your value type to be mutable, and does not extend it with any mutable functions by default, keeping it a truly immutable value type. It still can use .toObject to convert a value type into an NSManagedObject, however it can't modify this object afterwards. So it is still useful for all scenarios where you're only performing insertions (like a cache, or a log) or where any modifications are performed in bulk (delete all), or where updating will be performed on the NSManagedObject itself (.valueForKey, .save).

Boxing and Sub Properties

A word of advice: If you have value types in your value types, like:

struct Employee: BoxingPersistentStruct {
    let EntityName = "Employee"
    var objectID: NSManagedObjectID?
    let name: String
}

struct Shop: BoxingPersistentStruct {
    let EntityName = "Counter"
    var objectID: NSManagedObjectID?
    let employees: [Employee]
}

Then you have to make sure that all value types conform to the same boxing protocol, either BoxingPersistentStruct or BoxingStruct. The type checker cannot check this and report this as an error.

Ephemeral Objects

Most protocols in CoreValue mark the NSManagedObjectContext as an optional, which means that you don't have to supply it. Boxing will still work as expected, only the resulting NSManagedObjects will be ephemeral, that is, they're not bound to a context, they can't be stored. There're few use cases for this, but it is important to note that not supplying a NSManagedObjectContext will not result in an error.

UnboxingStruct

In CoreValue, boxed refers to values in an NSManagedObject container. I.e. NSNumber is boxing an Int, NSOrderedSet an Array, and NSManagedObject itself is boxing a value type (i.e. Shop).

UnboxingStruct can be applied to any struct or class that you intend to initialize from a NSManagedObject. It only has one requirement that needs to be implemented, and that's fromObject which takes an NSManagedObject and should return a value type. Here's a very simple and unsafe example:

struct Counter: UnboxingStruct
    var count: Int
    let name: String
    static func fromObject(_ object: NSManagedObject) throws -> Counter {
        return Counter(
	    count: object.valueForKey("count")!.integerValue,
	    name: object.valueForKey("name") as! String
	)
    }
}

Even though this example is not safe, we can observe several things from it. First, the implementation overhead is minimal. Second, the method can throw an error. That's because unboxing can fail in a multitude of ways (wrong value, no value, wrong entity, unknown entity, etc). If unboxing fails in any way, we throw an NSError. The other benefit of unboxing, that it allows us to take a shortcut (which CoreValue deviously copied from Argo). Utilizing several custom operators, the unboxing process can be greatly simplified:

struct Counter: UnboxingStruct
    var count: Int
    let name: String
    static func fromObject(_ object: NSManagedObject) throws -> Counter {
        return try curry(self.init) <^> object <| "count" <^> object <| "name"
    }
}

This code takes the automatic initializer, curries it and maps it over multiple incarnations of unboxing functions (<|) until it can return a Counter (or throw an error).

But what about these weird runes? Here's an in-detail overview of what's happening here:

Unboxing in Detail

curry(self.init)

Convert (A, B) -> T into A -> B -> C so that it can be called step by step

<^> Map the following operations over the A -> B -> fn that we just created

object <| "count" First operation: Take object, call valueForKey with the key "count" and assign this as the value for the first type of the curryed init function A

object <| "name" Second operation: Take object, call valueForKey with the key "count" and assign this as the value for the second type of the curryed init function B

Other Operators

Custom Operators are observed as a critical Swift feature, and rightly so. Too many of those make a codebase difficult to read and understand. The following custom operators are the same as in several other Swift Frameworks (see Runes and Argo). They're basically a verbatim copy from Haskell, so while that doesn't make them less custom or even official, they're at least unofficially agreed upon.

<| is not the only operator needed to encode objects. Here's a list of all supported operators:

Operator Description
<^> Map the following operations (i.e. combine map operations)
<| Unbox a normal value (i.e. var shop: Shop)
<|| Unbox a set/list of values (i.e. var shops: [Shops])
<|? Unbox an optional value (i.e. var shop: Shop?)

CVManagedStruct

Since most of the time you probably want boxing and unboxing functionality, CoreValue includes two handy typealiases, CVManagedStruct and CVManagedPersistentStruct which contain Boxing and Unboxing in one type.

RawRepresentable Enum support

By extending RawRepresentable, you can use Swift enums right away without having to first make sure your enum conforms to CVManagedStruct.

enum CarType: String {
    case pickup
    case sedan
    case hatchback
}

extension CarType: Boxing, Unboxing {}

struct Car: CVManagedPersistentStruct {
    static let EntityName = "Car"
    var objectID: NSManagedObjectID?
    var name: String
    var type: CarType

    static func fromObject(_ o: NSManagedObject) throws -> Car {
        return try curry(self.init)
            <^> o <|? "objectID"
            <^> o <| "name"
            <^> o <| "type"
    }
}

Docs

Have a look at CoreValue.swift, it's full of docstrings.

Alternatively, there's a lot of usage in the Unit Tests.

Here's a more complex example of CoreValue in use:

struct Employee: CVManagedPersistentStruct {

    static let EntityName = "Employee"

    var objectID: NSManagedObjectID?

    let name: String
    var age: Int16
    let position: String?
    let department: String
    let job: String

    static func fromObject(_ o: NSManagedObject) throws -> Employee {
        return try curry(self.init)
            <^> o <| "objectID"
            <^> o <| "name"
            <^> o <| "age"
            <^> o <|? "position"
            <^> o <| "department"
            <^> o <| "job"
    }
}

struct Shop: CVManagedPersistentStruct {
    static let EntityName = "Shop"
    
    var objectID: NSManagedObjectID?

    var name: String
    var age: Int16
    var employees: [Employee]
    
    static func fromObject(_ o: NSManagedObject) throws -> Shop {
        return try curry(self.init)
            <^> o <| "objectID"
            <^> o <| "age"
            <^> o <| "name"
            <^> o <|| "employees"
    }
}

// One year has passed, update the age of our shops and employees by one
let shops: [Shop] = Shop.query(self.managedObjectContext, predicate: nil)
for shop in shops {
    shop.age += 1
    for employee in shop.employees {
        employee.age += 1
    }
    shop.save()
}

CVManagedUniqueStruct and REST / Serialization / JSON

All the examples we've seen so far resolve around a use case where data is contained within your app. This means that the unique identifier of an NSManagedObject or struct is dicated by the NSManagedObjectID unique identifier which CoreData generates. This is fine as long as you don't plan to interact with outside data. If your data is loaded from external sources (i.e. JSON from a Rest API) then it may already have a unique identifier. CVManagedUniqueStruct allows you to force CoreValue / CoreData to use this external unique identifier in NSManagedObjectID's stead. The implementation is easy. You just have to conform to the BoxingUniqueStruct protocol which requires the implementation of a var naming the unique id field and a function returning the current ID value:

/// Name of the Identifier in the CoreData (e.g: 'id')
static var IdentifierName: String { get }

/// Value of the Identifier for the current struct (e.g: 'self.id')
func IdentifierValue() -> IdentifierType

Here's a complete & simple example:

struct Author: CVManagedUniqueStruct {

    static let EntityName = "Author"

    static var IdentifierName: String = "id"

    func IdentifierValue() -> IdentifierType { return self.id }

    let id: String
    let name: String

    static func fromObject(_ o: NSManagedObject) throws -> Author {
        return try curry(self.init)
            <^> o <| "id"
            <^> o <| "name"
    }
}

Please not that CVManagedUniqueStruct adds an (roughly) O(n) overhead on top of NSManagedObjectID based solutions due to the way object lookup is currently implemented.

State

All CoreData Datatypes are supported, with the following exceptions:

  • Transformable
  • Unordered Collections / NSSet (Currently, only ordered collections are supported)

Fetched properties are not supported yet.

Installation (iOS and macOS)

CocoaPods

Add the following to your Podfile:

pod 'CoreValue'

You will also need to make sure you're opting into using frameworks:

use_frameworks!

Then run pod install with CocoaPods 1.01 or newer.

Carthage

Add the following to your Cartfile:

github "terhechte/CoreValue" ~> 0.3.0

Then run carthage update.

Follow the current instructions in Carthage's README for up to date installation instructions.

The import CoreValue directive is required in order to use CoreValue.

Manually

  1. Copy the CoreValue.swift and curry.swift file into your project.
  2. Add the CoreData framework to your project

There is no need for import CoreValue when manually installing.

Contact

Benedikt Terhechte

@terhechte

Appventure.me

Changelog

Version 0.4.0

  • Swift 5 Support thanks to DaGerry

Version 0.3.0

  • Swift 3 Support
  • Added CVManagedUniqueStruct thanks to tkohout

Version 0.2.0

  • Switched Error Handling from Unboxed to Swift's native throw. Big thanks to Adlai Holler for spearheading this!
  • Huge Performance improvements: Boxing is roughly 80% faster and Unboxing is roughly 90% faster
  • Improved support for nested collections thanks to Roman Kříž.
  • RawRepresentable support (see documentation above) thanks to tkohout

Version 0.1.6

  • Made CVManagedPersistentStruct public
  • Fixed issue with empty collections

Version 0.1.4

Included pull request from AlexanderKaraberov which includes a fix to the delete function

Version 0.1.3

Updated to most recent Swift 2.0 b4 changes

Version 0.1.2

Renamed NSManagedStruct and NSPersistentManagedStruct to CVManagedStruct and CVPersistentManagedStruct as NS is preserved prefix for Apple classes

Version 0.1.1

Added CocoaPods support

Version 0.1.0

Initial Release

Acknoledgements

CoreValue uses ideas and code from thoughtbot's Argo framework for JSON decoding. Most notably their curry implementation. Have a look at it, it is an awesome framework.

License

The CoreValue source code is available under the MIT License.

Open Tasks

  • test unboxing with custom initializers (init(...))
  • change the protocol composition so that the required implementations (entityname, objectID, fromObject) form an otherwise empty protocol so it is easier to see the protocol and implement the requirements
  • add travis build
  • support aggregation
  • add support for nsset / unordered lists
  • add support for fetched properties (could be a struct a la (objects, predicate))
  • support transformable: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdNSAttributes.html
  • add jazzy for docs and update headers to have proper docs
  • document multi threading support via objectID
Comments
  • Duplicate entries

    Duplicate entries

    Hello, I created this test for duplicate entries: Save StoredShopEmployee A. count of StoredShopEmployee is 1.

    StoredEmployeeShop 1 and StoredEmployeeShop 2 have a copy of StoredShopEmployee A. Save Shop 1 Save Shop 2. count of StoredShopEmployee is 3 instead of 1.

    It looks like when you save the shops is not checking if Employee A is already saved and it is creating new entries.

    var employeesCount : Int{
          let employees : [StoredShopEmployee] = try! StoredShopEmployee.query(context, predicate: nil)
          return  employees.count
      }
      
      func testDuplicateEntries() {
          let beforeCount = self.employeesCount
          
          var employee = StoredShopEmployee(objectID: nil, name: "John", age: 18, position: "Clerk", department: "All", job: "Clerk", shop:nil)
          
          try! employee.save(context)
      
          XCTAssertEqual(beforeCount + 1, self.employeesCount)
          
          var shop1 = StoredEmployeeShop(objectID: nil, name: "Carpet shop 1", employees: [employee])
          var shop2 = StoredEmployeeShop(objectID: nil, name: "Carpet shop 2", employees: [employee])
    
          try! shop1.save(context)
          try! shop2.save(context)
          
          XCTAssertEqual(beforeCount + 1, self.employeesCount)
      }
    
    opened by leandromperez 4
  • NS is a reserved prefix for Apple classes

    NS is a reserved prefix for Apple classes

    NS is a reserved prefix for Apple classes, using NS in your own classes is a bad practice. Any plans for fixing this?

    https://github.com/terhechte/CoreValue/blob/cd34a93606994522f84f48ab1f7230c8b96ac5ba/CoreValue/CoreValue.swift#L230-L232

    opened by 3lvis 3
  • Merge Pull Request: Migration to Swift 5, iOS 12.2

    Merge Pull Request: Migration to Swift 5, iOS 12.2

    Migration to Swift 5, iOS platform 12.2. Corrected all the issues (errors and warnings) that Xcode came up with.

    Hope this worx as I'm new to GitHub ;)

    opened by DaGerry 2
  • Addition of CVManagedUniqueStruct

    Addition of CVManagedUniqueStruct

    This is slightly bigger pull request. The idea is to - instead of NSManagedObjectID connect the struct using unique identifier. It is especially useful in connection with Argo for decoding JSON. If the struct with an existing id is in the CoreData it is pulled (using fetch request) and updated. Otherwise new object is created.

    This has the advantage of getting rid of NSManagedObjectID and all the mutating functions that are sometimes causing weird behaviour.

    The disadvantage is obviously slower performance as every struct have to find its corresponding nsmanagedobject based on identifier predicate. Based on tests, the performance is roughly worse by 1/3 of the original time (0.181 to 0.267), so I think it is manageable.

    I am using this for some time already in my projects and I am pretty happy with it so far. Let me know what you think

    opened by tkohout 2
  • Fix parsing values when valueMirror.children is empty

    Fix parsing values when valueMirror.children is empty

    Hey, I've found bug when relationship one-to-many does not contain any values it triggers "Could not decode value for field ...". And when I removed the question mark it was ok.

    But I think there should be some tests, so we can be sure that this is working properly.

    opened by samnung 2
  • Issue#19

    Issue#19

    @terhechte I think I just found the reason and fixed issue #19. I created new tests, and a playground to show what the problem was in a clear way... i think...

    opened by leandromperez 1
  • Updated Swift legacy version flag to NO for all targets

    Updated Swift legacy version flag to NO for all targets

    Hi there,

    Even though the source code is Swift 3 ready the Swift legacy project flag for all targets was unspecified, causing carthage builds to fail.

    I've updated all targets, ran all tests again and successfully managed to build the framework using carthage. All the required changes are included in this pull request.

    Cheers, Rog

    opened by rpassis 1
  • Fixed boxing of optional collections

    Fixed boxing of optional collections

    In current version, if collection is set as optional it does not box properly and it is not saved.

    I have updated the internalToObject function to fix the issue.

    opened by tkohout 1
  • Added RawRepresantable conformance to Boxing and Unboxing

    Added RawRepresantable conformance to Boxing and Unboxing

    Similarily as described here:

    https://github.com/thoughtbot/Argo/blob/master/Documentation/Decode-Enums.md

    Now it is possible to do for example:

    enum CarType:String{
        case Pickup = "pickup"
        case Sedan = "sedan"
        case Hatchback = "hatchback"
    }
    
    extension CarType: Boxing,Unboxing {}
    

    And CoreValue will save the enum raw value to the core data.

    Note that the rawValue type of enum have to conform to Boxing and Unboxing

    opened by tkohout 1
  • Add better suppport for nested collections in persistent structs

    Add better suppport for nested collections in persistent structs

    Hey, I've found that nested collections are not supported the same way I expected so I've made some changes to make it little bit better.

    Main problem was that when you save persistent struct twice it saves inserts objects in nested collection twice. So it ends up by having collection with twice more values than you expected :D

    opened by samnung 1
  • CVManagedPersistentStruct not found

    CVManagedPersistentStruct not found

    In version 0.1.3 the CVManagedPersistentStruct is not made public and it is not accessible from other module.

    Quick fix was for me to add to my code (_CVManagedPersistentStruct is public):

    public protocol CVManagedPersistentStruct : _CVManagedPersistentStruct { typealias StructureType = Self }

    Thanks, and keep up the good work

    opened by tkohout 1
  • Cannot invoke 'curry' with an argument list of type '((objectID: NSManagedObjectID?, name: String, employees: Array<StoredEmployee>) -> StoredCompany)

    Cannot invoke 'curry' with an argument list of type '((objectID: NSManagedObjectID?, name: String, employees: Array) -> StoredCompany)

    We have next two separated files:

    //  StoredCompany.swift
    
    import CoreData
    import CoreValue
    
    
    struct StoredCompany: CVManagedPersistentStruct {
        static let EntityName = "Company"
        var objectID: NSManagedObjectID?
    
        var name: String
        var employees: Array<StoredEmployee>
    
        static func fromObject(o: NSManagedObject) throws -> StoredCompany {
            return try curry(self.init)
                <^> o <|? "objectID"
                <^> o <| "name"
                <^> o <|| "employees"
        }
    
        mutating func save(context: NSManagedObjectContext) throws {
            try employees.saveAll(context)
    
            try defaultSave(context)
        }
    
    //  StoredEmployee.swift
    
    import CoreData
    import CoreValue
    
    
    struct StoredEmployee : CVManagedPersistentStruct {
        static let EntityName = "Employee"
        var objectID: NSManagedObjectID?
    
        let name: String
        let age: Int16
        let position: String?
        let department: String
        let job: String
    
        static func fromObject(o: NSManagedObject) throws -> StoredEmployee {
            return try curry(self.init)
                <^> o <|? "objectID"
                <^> o <| "name"
                <^> o <| "age"
                <^> o <|? "position"
                <^> o <| "department"
                <^> o <| "job"
        }
    }
    

    The compiler throws next error .../StoredCompany.swift:21:20: Cannot invoke 'curry' with an argument list of type '((objectID: NSManagedObjectID?, name: String, employees: Array<StoredEmployee>) -> StoredCompany)'

    It's happens only when struct StoredEmployee and struct StoredCompany are in separated files. Probably that's the reason that the tests doesn't catch it.

    Cheers! 🍻

    opened by emarashliev 4
  • Inverse Relationships

    Inverse Relationships

    Hi, I started using modified version of CoreValue on one of my projects and one problem with CoreValue became imminent.

    let employee = Employee(objectID: nil, name: "John Doe", age: 18, position: "Clerk", department: "Carpet", job: "Cleaner", shop:nil)
    var shop = Shop(objectID: nil, name: "Carpet shop", employees: [employee])
    
    try! shop.save(context)
    //Will crash in infinite loop
    let shops:[Shop] = Shop.query(context, predicate: nil)
    

    The Employee have inverse relationship with Shop. When I add employee to the Shop entity and save the corevalue will populate the inverse relationship. When I try to query the Shop program will crash in infinite loop.

    This is of course logical, because we are working with structs, but it gets quite limiting. The solution is to not to unbox the inverse relationship but I found out that sometimes I need to use both sides of relationship.

    I wonder if you are aware of the problem and if you have and idea how to solve it

    Thanks

    opened by tkohout 4
Releases(v0.4.0)
Owner
Benedikt Terhechte
Benedikt Terhechte
An Integer type that clamps its value to its minimum and maximum instead of over- or underflowing.

ClampedInteger An Integer type that clamps its value to its minimum and maximum instead of over- or underflowing. Examples let big = ClampedIntege

Berik Visschers 0 Jan 17, 2022
This to learn such as : Add Target , NSNotification Center Send/Get Data , Observer Override , resize Data By Byte , UIImagePicker Delegate , UIAlert Handle , Top ViewController , Get pickerController

Technicalisto How to Create UIButton Class to Pick Data Image Purpose Learn this topics With exact Task Add Target NSNotification Center Send/Get Data

Aya Baghdadi 1 Feb 20, 2022
Jared Watson 0 Jan 8, 2022
SampleProjectMVVM - Sample project using MVVM parsing data from Giphy.com

iOS Take Home Create an iOS app with two views, MainViewController and DetailVie

HG HrachGarabedian 0 Jan 27, 2022
Swift library of lightweight interfaces for prototyping, bridged to JS

Prototope Prototope is a lightweight, high-performance prototyping framework. Its goals are: making simple things very easy making complex things poss

Khan Academy 230 Jun 29, 2022
iOS native app demo with Xcode and Swift using MVVM architecture and Apple's Combine framework for functional reactive programming, all with UIKit

iOS (Swift 5): MVVM test with Combine and UIKit MVVM and functional reactive programming (FRP) are very used in iOS development inside companies. Here

Koussaïla BEN MAMAR 2 Dec 31, 2021
Exemplify a LazyVGrid in SwiftUI in a MVVM pattern with Mock Data and images in assets.

SwiftUI-LazyVGrid Exemplify a LazyVGrid in SwiftUI in a MVVM pattern with Mock Data and images in assets. Screenshots Samples IP & Credits All those b

Ethan Halprin 3 Aug 9, 2022
ClearScore - This application simulates a credit check on a user by making a call to an API and displaying the data

ClearScore Description This application simulates a credit check on a user by ma

Sashen Pillay 0 Jan 11, 2022
⚛️ A Reactive Data-Binding and Dependency Injection Library for SwiftUI x Concurrency.

SwiftUI Atom Properties A Reactive Data-Binding and Dependency Injection Library for SwiftUI x Concurrency ?? API Reference Introduction Examples Gett

Ryo Aoyama 199 Dec 17, 2022
Mini-application iOS native avec Xcode et Swift exploitant l'architecture MVVM et le framework Combine d'Apple pour la mise en place de la programmation réactive fonctionnelle, le tout avec UIKit.

iOS (Swift 5): Test MVVM avec Combine et UIKit L'architecture MVVM et la programmation réactive fonctionnelle sont très utlisées dans le développement

Koussaïla BEN MAMAR 2 Nov 5, 2022
A small app that uses the private FlightUtilities.framework to show information about any flight.

FlightUtilities A small app that uses the private FlightUtilities.framework to show information about any flight given the airline code, flight code a

Patrick Balestra 31 Dec 20, 2022
SignalKit is a reactive Swift framework with focus on clean and readable API.

Abstract SignalKit is a lightweight event and binding framework. The core of SignalKit is the Observable protocol. Each implementation of the Observab

Yanko Dimitrov 252 Dec 13, 2022
A framework to create proxies for XCBBuildService, which allows for custom Xcode build integrations.

XCBBuildServiceProxyKit XCBBuildServiceProxyKit is a framework that enables you to write a proxy for Xcode's XCBBuildService, which enables you to ext

Mobile Native Foundation 30 Dec 28, 2022
Model View Presenter Framework written in Swift.

BothamUI BothamUI is MVP (Model-View-Presenter) framework written in Swift. This project will help you setup all your presentation logic. BothamUI pro

Karumi 351 Sep 22, 2022
SpaceX rocket listing app using RxSwift and CLEAN Architecture with MVVM

Jibble SpaceX rocket listing app using RxSwift and CLEAN Architecture with MVVM Demo Features Reactive Bindings URL / JSON Parameter Encoding Filter Y

Ammad Akhtar 0 Dec 5, 2021
A fitness application using swift which can help user to hold a better sleep

Natcap - Good for your sleep This is a fitness application which can help user to hold a better sleep which depends on their previous sleeping habit.

Mill 0 Dec 13, 2021
Movies app written in Swift 5 using the Custom API created on the Mocky website

Movie App shows you collections of TV streaming and other movies. Movie app writ

null 8 Dec 7, 2022
MVVM-RXSWIFT-COMBINE- - Demo application populating posts from network request using

MVVM => RXSWIFT + COMBINE Demo application populating posts from network request

Amr Al-khayat 0 Jan 2, 2022
A sample project exploring MVVM pattern with SwiftUI/Combine, using Unsplash API (via Picsum.photos API)

CombineUnsplash A sample project exploring MVVM pattern with SwiftUI/Combine, using Unsplash API (via Picsum.photos API) with detail example. Resource

Vinh Nguyen 26 Dec 13, 2022