Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Identifiable #14

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AppwiseCore.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Pod::Spec.new do |s|
:type => 'MIT',
:file => 'LICENSE'
}
s.ios.deployment_target = '10.0'
s.ios.deployment_target = '13.0'
s.swift_version = '5.0'

# files
Expand Down
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ All notable changes to this project will be documented in this file.
### Deprecations

* Core: deprecate our `Cancelled` error type in favour of Swift's built-in `CancellationError` type.
* Core: due to unuse, we're marking `OptionalIdentifiable` as deprecated.
* Core: renamed our own `Identifiable` protocol to `TaggedIdentifiable`.
* CoreData: due to incorrectness, we're deprecating `responseInsert` (this is not `responseInsert`, see below).

### Improvements

Expand All @@ -18,14 +21,15 @@ All notable changes to this project will be documented in this file.
* Core: add `requestVoid` method to network client.
* Core: default error parser now ignores `.explicitlyCancelled` (for cancelled requests).
* Core: result "cancellation" now uses Swift's built-in `CancellationError` type if available.
* Core: `TaggedIdentifiable` protocol now conforms to Swift's own `Identifiable` protocol.
* XcodeGen: added a template for shared frameworks.

### Bug Fixes

* Fastlane: ensure translations export can handle "empty" targets.
* Scripts: fix Xcode warnings for build steps without input/output files.
* Bump dependencies (Sentry to 8).
* CoreData: ensure `requestInsert` imports objects on the context's thread. Note: because of limitations, we're deprecating `responseInsert`.
* CoreData: ensure `requestInsert` imports objects on the context's thread.

### Internal

Expand Down
50 changes: 32 additions & 18 deletions Sources/Core/Identity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,42 @@ import Foundation
/// Protocol used to mark a given type as being identifiable, meaning
/// that it has a type-safe identifier, backed by a raw value, which
/// defaults to String.
public protocol _Identifiable {
public protocol _TaggedIdentifiable {
// swiftlint:disable:previous type_name

// Note: once we remove `OptionalIdentifiable`, unify this protocol with `TaggedIdentifiable` below.

/// The backing raw type of this type's identifier.
associatedtype RawIdentifier
associatedtype RawIdentifier: Hashable

/// The object type (hack needed to support subclasses)
associatedtype IdentifierObjectType: _Identifiable = Self
associatedtype IdentifierObjectType: _TaggedIdentifiable = Self

/// Shorthand type alias for this type's identifier.
typealias TaggedID = Identifier<IdentifierObjectType>

// swiftlint:disable type_name
/// Shorthand type alias for this type's identifier.
typealias ID = Identifier<IdentifierObjectType>
///
/// - Warning: Prefer `TaggedID` to avoid ambiguity errors.
typealias ID = TaggedID
// swiftlint:enable type_name
}

/// Protocol used to mark a given type as being identifiable, meaning
/// that it has a type-safe identifier, backed by a raw value, which
/// defaults to String.
public protocol Identifiable: _Identifiable {
public protocol TaggedIdentifiable<RawIdentifier>: _TaggedIdentifiable, Swift.Identifiable {
// swiftlint:disable identifier_name
/// The ID of this instance.
var id: ID { get }
// swiftlint:enable identifier_name
}

/// Protocol used to mark a given type as being identifiable, meaning
/// that it has a type-safe identifier, backed by a raw value, which
/// defaults to String.
public protocol OptionalIdentifiable: _Identifiable {
// swiftlint:disable identifier_name
/// The ID of this instance.
var id: ID? { get }
var id: TaggedID { get }
// swiftlint:enable identifier_name
}

/// A type-safe identifier for a given `Value`, backed by a raw value.
/// When backed by a `Codable` type, `Identifier` also becomes codable,
/// and will be encoded into a single value according to its raw value.
public struct Identifier<Value: _Identifiable>: RawRepresentable {
public struct Identifier<Value: _TaggedIdentifiable>: RawRepresentable {
/// The raw value that is backing this identifier.
public let rawValue: Value.RawIdentifier

Expand Down Expand Up @@ -111,7 +108,12 @@ extension Identifier: CustomDebugStringConvertible {

extension Identifier: Equatable where Value.RawIdentifier: Equatable {}

extension Identifier: Hashable where Value.RawIdentifier: Hashable {}
extension Identifier: Hashable where Value.RawIdentifier: Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(String(describing: Value.self))
hasher.combine(rawValue)
}
}

extension Identifier: Comparable where Value.RawIdentifier: Comparable {
public static func < (lhs: Identifier<Value>, rhs: Identifier<Value>) -> Bool {
Expand All @@ -132,3 +134,15 @@ extension Identifier: Codable where Value.RawIdentifier: Codable {
try container.encode(rawValue)
}
}

// MARK: - Deprecated

@available(*, deprecated, renamed: "TaggedIdentifiable")
public typealias Identifiable = TaggedIdentifiable

@available(*, deprecated, message: "Will be removed in next major version")
public protocol OptionalIdentifiable: _TaggedIdentifiable {
// swiftlint:disable identifier_name
var id: TaggedID? { get }
// swiftlint:enable identifier_name
}
8 changes: 4 additions & 4 deletions Sources/CoreData/Repository/SingleObjectRepository.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@
import CoreData

public protocol SingleObjectRepository {
associatedtype ObjectType: NSFetchRequestResult, _Identifiable
associatedtype ObjectType: NSFetchRequestResult, _TaggedIdentifiable

var objectID: Identifier<ObjectType> { get }
var objectID: ObjectType.TaggedID { get }
var context: NSManagedObjectContext { get }
var object: ObjectType? { get }

init(objectID: Identifier<ObjectType>, context: NSManagedObjectContext)
init(objectID: ObjectType.TaggedID, context: NSManagedObjectContext)
func refresh(then handler: @escaping (Result<ObjectType, Error>) -> Void)
}

// MARK: - Default implementations

public extension SingleObjectRepository {
init(objectID: Identifier<ObjectType>) {
init(objectID: ObjectType.TaggedID) {
self.init(objectID: objectID, context: DB.shared.view)
}

Expand Down