Instantly share code, notes, and snippets.
Created
October 20, 2015 15:54
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save dennislysenko/ebc5092e85f5727eafa4 to your computer and use it in GitHub Desktop.
Genome+MappableCoreDataObject Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// SocialAccount.swift | |
// Riff | |
// | |
// Created by Dennis Lysenko on 10/19/15. | |
// Copyright © 2015 Riff Digital. All rights reserved. | |
// | |
import Foundation | |
import CoreData | |
import Flex | |
import MagicalRecord | |
import SwiftyJSON | |
import Genome | |
// MARK: Genome+MappableCoreDataObject | |
typealias MappableCoreDataInitializer = (map: Map, context: NSManagedObjectContext) throws -> MappableObject | |
protocol MappableCoreDataObject: MappableObject { | |
static func initializer() -> MappableCoreDataInitializer | |
} | |
extension MappableCoreDataObject { | |
static func initializer() -> Initializer { | |
// return try initializer(context: NSManagedObjectContext.MR_defaultContext()) | |
fatalError("no soup for you") | |
// Not sure how to resolve this | |
} | |
static func mappedInstance(js: Genome.JSON, context: NSManagedObjectContext) throws -> Self { | |
let map = Map(json: js, context: [:]) | |
let initialization = initializer() // auto maps to the MappableCoreDataInitializer() | |
var instance = try initialization(map: map, context: context) | |
assert(instance is Self, | |
"Unfortunately, in order to support flexible subclassing, while also not enforcing a specific initializer, I need to be lenient with my type specifications in MappableObject protocol. Otherwise, an error is thrown 'You are unable to 'use Self in a non-parameter, non-result type position`. This means that although `Initializer` is of type `Map -> MappableObject`, it should really be considered `Map -> Self`!" | |
) | |
try instance.sequence(map) | |
return instance as! Self | |
} | |
} | |
// MARK: - Example Implementation | |
class SocialAccount: NSManagedObject, MappableCoreDataObject { | |
static func initializer() -> MappableCoreDataInitializer { | |
return findOrCreateInstance | |
} | |
private static func findOrCreateInstance(map: Map, context: NSManagedObjectContext) throws -> MappableObject { | |
let id: NSNumber = try <~map["id"] | |
if let existingAccount = SocialAccount.MR_findFirstByAttribute("id", withValue: id, inContext: context) { | |
return existingAccount | |
} | |
let account = SocialAccount.MR_createEntityInContext(context) | |
account.id = id.longLongValue | |
return account | |
} | |
func sequence(map: Map) throws { | |
try self.accountID <~> map["account_id"] | |
try self.accountName <~> map["account_name"] | |
try self.socialNetwork <~> map["social_network"].transformFromJson{ (input: String) -> SocialNetwork! in | |
if let network = SocialNetwork(rawValue: input) { | |
return network | |
} | |
throw RequestProcessingError.ParseError(message: "Invalid social network \(input)") | |
}.transformToJson { $0.rawValue } | |
} | |
// MARK: - Non-Genome stuff | |
var socialNetwork: SocialNetwork! { | |
get { | |
return SocialNetwork(rawValue: self.rawSocialNetwork) | |
} | |
set { | |
self.rawSocialNetwork = newValue.rawValue | |
} | |
} | |
class func loadAllFromServer(completion: (SimpleResult) -> ()) { | |
guard ActiveUser.loggedIn else { | |
return | |
} | |
RiffNetworking.getAsync("/v1/users/me/social_accounts.json", body: DictionaryBody(["access_token": ActiveUser.accessToken])) { (result) -> () in | |
switch result { | |
case .Success(let response) where response.status == 200: | |
guard let accounts = response.asJSON?["social_accounts"].array else { | |
return completion(.Failure(RequestProcessingError.ParseError(message: "No social_accounts array on response: \(response)"))) | |
} | |
for accountJSON in accounts { | |
VinylRecord.saveWithBlock({ (context) -> () in | |
try SocialAccount.mappedInstance(accountJSON, context: context) | |
}, completion: { (result) -> () in | |
if case .Failure(let error) = result { | |
Rlog.e(self, error) | |
} | |
}) | |
} | |
completion(.Success) | |
case .Success(let response): // unsuccessful response status | |
Rlog.e(self, "failed result \(result)") | |
completion(.Failure(RequestProcessingError.UnsuccessfulResponse(response: response))) | |
case .Failure(let error): | |
completion(.Failure(error)) | |
} | |
} | |
} | |
static func create(socialNetwork socialNetwork: SocialNetwork, accountID: String, accountName: String, accessToken: String, completion: (Result<SocialAccount>) -> ()) { | |
RiffNetworking.postAsync("/v1/users/me/social_accounts.json", body: JSON(["access_token": ActiveUser.accessToken, "social_account": ["account_id": accountID, "account_name": accountName, "social_network": socialNetwork.rawValue]])) { (result) -> () in | |
switch result { | |
case .Success(let response) where response.status == 200 && response.asJSON != nil: | |
let json = response.asJSON!["social_account"] | |
VinylRecord.saveWithBlock { context, handler in | |
do { | |
let account = try SocialAccount.mappedInstance(json, context: context) | |
handler.onCompletion { result in completion(.Success(account)) } | |
} catch let error { | |
handler.onCompletion { _ in completion(.Failure(error)) } | |
} | |
} | |
case .Success(let response) where response.asJSON != nil: | |
completion(.Failure(RequestProcessingError.UnsuccessfulResponse(response: response))) | |
case .Success(let response): | |
completion(.Failure(RequestProcessingError.ParseError(message: "Response was not JSON \(response)"))) | |
case .Failure(let error): completion(.Failure(error)) | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment