I need to change this generic class that accepts an array to something that is not an array
class GenericDataSource<T> : NSObject {
var data: DynamicValue<[T]> = DynamicValue([])
//var data: DynamicValue<T>?
}
When I remove the square brackets I get an error
typealias CompletionHandler = (() -> Void)
class DynamicValue<T> {
var value : T {
didSet {
self.notify()
}
}
private var observers = [String: CompletionHandler]()
init(_ value: T) {
self.value = value
}
public func addObserver(_ observer: NSObject, completionHandler: #escaping CompletionHandler) {
observers[observer.description] = completionHandler
}
public func addAndNotify(observer: NSObject, completionHandler: #escaping CompletionHandler) {
self.addObserver(observer, completionHandler: completionHandler)
self.notify()
}
private func notify() {
observers.forEach({ $0.value() })
}
deinit {
observers.removeAll()
}
}
You need an init in GenericDataSource
class GenericDataSource<T> {
var data: DynamicValue<T>
init(_ value: T) {
data = DynamicValue(value)
}
}
or make data optional
class GenericDataSource<T> : NSObject {
var data: DynamicValue<T>?
}
And then use them like
let gds = GenericDataSource("Hello")
or for the optional variant
let gds = GenericDataSource<String>()
gds.data = DynamicValue("Hello")
Related
In a protocol, I'd like to create a single instance from functions so I use a container to store the static instances like this:
protocol MyProtocol {
func networkService() -> NetworkService
}
extension MyProtocol {
func networkService() -> NetworkService {
if Singletons.networkService == nil {
Singletons.networkService = NetworkService(abc: 123)
}
return Singletons.networkService!
}
}
private enum Singletons {
static var networkService: NetworkService?
}
Later on, a type can conform to it and replace the default implementation, but also requires a single instance:
struct MyType: MyProtocol {
private static var networkService: NetworkService?
func networkService() -> NetworkService {
if Self.networkService == nil {
Self.networkService = NetworkService(abc: 555)
}
return Self.networkService!
}
}
What I'm hoping is to encapsulate this ceremony of creating the singleton by using a Property Wrapper, but on the type. I'd like to do something like this:
protocol MyProtocol {
func networkService() -> NetworkService
}
extension MyProtocol {
func networkService() -> NetworkService {
#Singleton
NetworkService(abc: 123)
}
}
////
struct MyType: MyProtocol {
func networkService() -> NetworkService {
#Singleton
NetworkService(abc: 555)
}
}
Is there a way to achieve this or something similar?
Here is my first attempt:
struct Single {
private static var instances = [String: Any]()
static func make<T>(_ instance: () -> T) -> T {
let key = String(describing: type(of: T.self))
guard let value = instances[key] as? T else {
let resolved = instance()
instances[key] = resolved
return resolved
}
return value
}
}
protocol NetworkService {}
struct NetworkDefaultService: NetworkService {
let id = UUID().uuidString
init() {
print("Network Default: \(id)")
}
}
struct NetworkMockService: NetworkService {
let id = UUID().uuidString
init() {
print("Network Mock: \(id)")
}
}
protocol LocationService {}
class LocationDefaultService: LocationService {
let id = UUID().uuidString
init() {
print("Location Default: \(id)")
}
}
protocol NonSingleService {}
struct NonSingleDefaultService: NonSingleService {
let id = UUID().uuidString
init() {
print("Non-Single Default: \(id)")
}
}
protocol Context {
func networkService() -> NetworkService
func locationService() -> LocationService
func nonSingleService() -> NonSingleService
}
extension Context {
func networkService() -> NetworkService {
Single.make {
NetworkDefaultService()
}
}
func locationService() -> LocationService {
Single.make {
LocationDefaultService()
}
}
}
struct AppContext: Context {
func networkService() -> NetworkService {
Single.make {
NetworkMockService()
}
}
func nonSingleService() -> NonSingleService {
NonSingleDefaultService()
}
}
let context = AppContext()
context.networkService()
context.networkService()
context.locationService()
context.locationService()
context.nonSingleService()
context.nonSingleService()
This prints:
Network Mock: 48CBDE3A-26D2-4767-A6AA-F846F8863A52
Location Default: 4846953B-93F6-4025-A970-DA5B47470652
Non-Single Default: 957979D8-9F3E-428E-BD87-B9F45D56B755
Non-Single Default: 816D2886-D606-4558-A842-295C833AE4C8
From within a property wrapper in Swift, can you someone refer back to the instance of the class or struck that owns the property being wrapped? Using self doesn't obviously work, nor does super.
I tried to pass in self to the property wrapper's init() but that doesn't work either because self on Configuration is not yet defined when #propertywrapper is evaluated.
My use case is in a class for managing a large number of settings or configurations. If any property is changed, I just want to notify interested parties that something changed. They don't really need to know which value just, so use something like KVO or a Publisher for each property isn't really necessary.
A property wrapper looks ideal, but I can't figure out how to pass in some sort of reference to the owning instance that the wrapper can call back to.
References:
SE-0258
enum PropertyIdentifier {
case backgroundColor
case textColor
}
#propertyWrapper
struct Recorded<T> {
let identifier:PropertyIdentifier
var _value: T
init(_ identifier:PropertyIdentifier, defaultValue: T) {
self.identifier = identifier
self._value = defaultValue
}
var value: T {
get { _value }
set {
_value = newValue
// How to callback to Configuration.propertyWasSet()?
//
// [self/super/...].propertyWasSet(identifier)
}
}
}
struct Configuration {
#Recorded(.backgroundColor, defaultValue:NSColor.white)
var backgroundColor:NSColor
#Recorded(.textColor, defaultValue:NSColor.black)
var textColor:NSColor
func propertyWasSet(_ identifier:PropertyIdentifier) {
// Do something...
}
}
The answer is no, it's not possible with the current specification.
I wanted to do something similar. The best I could come up with was to use reflection in a function at the end of init(...). At least this way you can annotate your types and only add a single function call in init().
fileprivate protocol BindableObjectPropertySettable {
var didSet: () -> Void { get set }
}
#propertyDelegate
class BindableObjectProperty<T>: BindableObjectPropertySettable {
var value: T {
didSet {
self.didSet()
}
}
var didSet: () -> Void = { }
init(initialValue: T) {
self.value = initialValue
}
}
extension BindableObject {
// Call this at the end of init() after calling super
func bindProperties(_ didSet: #escaping () -> Void) {
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if var child = child.value as? BindableObjectPropertySettable {
child.didSet = didSet
}
}
}
}
You cannot do this out of the box currently.
However, the proposal you refer to discusses this as a future direction in the latest version:
https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
For now, you would be able to use a projectedValue to assign self to.
You could then use that to trigger some action after setting the wrappedValue.
As an example:
import Foundation
#propertyWrapper
class Wrapper {
let name : String
var value = 0
weak var owner : Owner?
init(_ name: String) {
self.name = name
}
var wrappedValue : Int {
get { value }
set {
value = 0
owner?.wrapperDidSet(name: name)
}
}
var projectedValue : Wrapper {
self
}
}
class Owner {
#Wrapper("a") var a : Int
#Wrapper("b") var b : Int
init() {
$a.owner = self
$b.owner = self
}
func wrapperDidSet(name: String) {
print("WrapperDidSet(\(name))")
}
}
var owner = Owner()
owner.a = 4 // Prints: WrapperDidSet(a)
My experiments based on : https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
protocol Observer: AnyObject {
func observableValueDidChange<T>(newValue: T)
}
#propertyWrapper
public struct Observable<T: Equatable> {
public var stored: T
weak var observer: Observer?
init(wrappedValue: T, observer: Observer?) {
self.stored = wrappedValue
}
public var wrappedValue: T {
get { return stored }
set {
if newValue != stored {
observer?.observableValueDidChange(newValue: newValue)
}
stored = newValue
}
}
}
class testClass: Observer {
#Observable(observer: nil) var some: Int = 2
func observableValueDidChange<T>(newValue: T) {
print("lol")
}
init(){
_some.observer = self
}
}
let a = testClass()
a.some = 4
a.some = 6
The answer is yes! See this answer
Example code for calling ObservableObject publisher with a UserDefaults wrapper:
import Combine
import Foundation
class LocalSettings: ObservableObject {
static var shared = LocalSettings()
#Setting(key: "TabSelection")
var tabSelection: Int = 0
}
#propertyWrapper
struct Setting<T> {
private let key: String
private let defaultValue: T
init(wrappedValue value: T, key: String) {
self.key = key
self.defaultValue = value
}
var wrappedValue: T {
get {
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
}
set {
UserDefaults.standard.set(newValue, forKey: key)
}
}
public static subscript<EnclosingSelf: ObservableObject>(
_enclosingInstance object: EnclosingSelf,
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, T>,
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Setting<T>>
) -> T {
get {
return object[keyPath: storageKeyPath].wrappedValue
}
set {
(object.objectWillChange as? ObservableObjectPublisher)?.send()
UserDefaults.standard.set(newValue, forKey: object[keyPath: storageKeyPath].key)
}
}
}
Using a Playground and given these definitions:
import Foundation
protocol MoneyTrakObject {
var key: String { get set }
init()
}
extension MoneyTrakObject {
static func objectFromDB<T: MoneyTrakObject>(for key: String, queue: DispatchQueue? = nil, completion: #escaping (T) -> Void) -> String? {
// after data is retrieved, call completion closure
let valueObject = T()
completion(valueObject)
return "dbToken"
}
}
protocol Transaction: MoneyTrakObject {
var amount: Int { get set }
}
struct BasicTransaction: Transaction {
var key = UUID().uuidString
var amount = 0
init() {}
}
struct RecurringTransaction: Transaction {
var key = UUID().uuidString
var amount = 0
init() {}
}
I would expect that I could do this:
let token1 = BasicTransaction.objectFromDB(for: "") { (transaction) in
// use transaction
}
let token2 = RecurringTransaction.objectFromDB(for: "") { (transaction) in
// use transaction
}
However I get the Generic parameter 'T' could not be inferred error when calling the static method and I'm not sure why.
I do not see why you need the generic constraint. If you change the extension of your protocol to this:
extension MoneyTrakObject {
static func objectFromDB(for key: String, queue: DispatchQueue? = nil, completion: #escaping (Self) -> Void) -> String? {
// after data is retrieved, call completion closure
let valueObject = Self()
completion(valueObject)
return "dbToken"
}
}
Your code compiles just fine. Self is a placeholder for the actually implementing type.
Well... the only place where T is used is inside the completion handler argument. When you write this:
let token1 = BasicTransaction.objectFromDB(for: "") { (transaction) in
// use transaction
}
The compiler has no idea what type transaction is and hence cannot specialize the generic function. Provide some type info like this:
let token1 = BasicTransaction.objectFromDB(for: "") { (transaction: Transaction) in
// use transaction
}
let token2 = BasicTransaction.objectFromDB(for: "") { (transaction: BasicTransaction) in
// use transaction
}
I have some trouble in understanding my specific request, in Swift.
There is a class named Origin and multiple its subclasses.
How I can update my block-method written ONLY in Origin class?
class Origin: NSObject {
func mod(_ block: (() throws -> Void)) {
try! block()
}
}
I need use mod from all Origin subclasses, and I need to have this usage effect:
var originSubclassObject = OriginSubclass()
originSubclassObject.mod {
.age = 12 //age is OriginSubclass property
.name = "Bob" //name is OriginSubclass property
}
So, you see, I need extract OriginSubclass properties for using in mod-block. I need to create usage exactly likes in usage effect code (extract mod-caller properties from ".").
Thanks all for help!
You could consider a protocol with a default implementation, e.g.:
protocol Modifiable { }
extension Modifiable {
func mod(_ block: ((Self) throws -> Void)) {
try! block(self)
}
}
class Origin: Modifiable { }
class OriginSubclass: Origin {
var name: String?
var age: Int?
}
And then:
let originSubclassObject = OriginSubclass()
originSubclassObject.mod { object in
object.age = 12
object.name = "Bob"
}
Or
let originSubclassObject = OriginSubclass()
originSubclassObject.mod {
$0.age = 12
$0.name = "Bob"
}
Or, if that base class was only there for the mod method, you could lose it entirely:
protocol Modifiable { }
extension Modifiable {
func mod(_ block: ((Self) throws -> Void)) {
try! block(self)
}
}
class MyObject: Modifiable {
var name: String?
var age: Int?
}
And
let myObject = MyObject()
myObject.mod { object in
object.age = 12
object.name = "Bob"
}
Or
let myObject = MyObject()
myObject.mod {
$0.age = 12
$0.name = "Bob"
}
I'm trying to implement a class in Swift 2 for a single setting of any type that uses NSUserDefaults under the covers.
Problem: How do I define a class for storing and retrieving any type of object, including Dictionary?
I have a solution that works with AnyObject which consists of a generic protocol (Settable) and a generic class (Setting). SettingsStore is a wrapper around NSUserDefaults.
// MARK: Settable Protocol
public protocol Settable {
typealias T
init(key: String, defaultValue: T, settingsStore: SettingsStore)
var value: T { get set }
func loadCurrentValue()
}
// MARK: Settings Class
public class Setting<T: AnyObject>: Settable {
private let key: String
private let defaultValue: T
private let settingsStore: SettingsStore
private var currentValue: T?
public required init(key: String, defaultValue: T, settingsStore: SettingsStore) {
self.key = key
self.defaultValue = defaultValue
self.settingsStore = settingsStore
}
public var value: T {
get {
if self.currentValue == nil {
self.loadCurrentValue()
}
return self.currentValue!
}
set {
self.currentValue = newValue
self.settingsStore.setObject(newValue.toAnyObject(), forKey: self.key)
}
}
public func loadCurrentValue() {
let optionalValue: T? = self.settingsStore.objectForKey(key) as? T
if let value = optionalValue {
self.currentValue = value
} else {
self.currentValue = self.defaultValue
}
}
}
This allows me to create a setting like this:
let specialId: Setting<String>
init() {
self.specialId = Setting<String>(
key: "specialId",
defaultValue: "<somevalue>",
settingsStore: self.settingsStore)
}
The problem with this is that it doesn't work with value types, such as String, Bool, Int, Double, Array, or Dictionary because they are all value types and value types don't conform to the AnyObject protocol.
I've solved the problem for some of these using a protocol and extensions based on NSString and NSNumber, but a solution Dictionary is proving to be elusive (I don't need a solution for Array at the moment so I haven't spent time trying to solve that one).
// Change definition of Setting class like this:
public class Setting<T: AnyObjectRepresentable>: Settable {
...
}
public protocol AnyObjectRepresentable {
func toAnyObject() -> AnyObject
static func fromAnyObject(value: AnyObject) -> Self?
}
extension AnyObjectRepresentable where Self: AnyObject {
public func toAnyObject() -> AnyObject {
return self
}
public static func fromAnyObject(value: AnyObject) -> AnyObject? {
return value
}
}
extension String: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSString(string: self)
}
public static func fromAnyObject(value: AnyObject) -> String? {
let convertedValue = value as? String
return convertedValue
}
}
extension Bool: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSNumber(bool: self)
}
public static func fromAnyObject(value: AnyObject) -> Bool? {
let convertedValue = value as? Bool
return convertedValue
}
}
// Add extensions for Int and Double that look like the above extension for Bool.
I tried two different approaches for Dictionary. The first one is similar to the String approach:
extension Dictionary: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
let value = self as NSDictionary
return value
}
public static func fromAnyObject(value: AnyObject) -> Dictionary? {
let convertedValue = value as? Dictionary
return convertedValue
}
}
Xcode gives me the following error on the first line of the toAnyObject() method implementation:
'Dictionary' is not convertible to 'NSDictionary'
Next I tried extending NSDictionary directly:
extension NSDictionary: AnyObjectRepresentable {
public func toAnyObject() -> AnyObject {
return NSDictionary(dictionary: self)
}
public static func fromAnyObject(value: AnyObject) -> NSDictionary? {
let convertedValue = value as? NSDictionary
return convertedValue
}
}
Xcode gives me the following error on the declaration of fromAnyObject():
Method 'fromAnyObject' in non-final class 'NSDictionary' must return Self to conform to protocol 'AnyObjectRepresentable'
I'm at my wits. Is this solvable?
Thanks,
David
UPDATED 2015-09-15 16:30
For background, here is the definition and an implementation of SettingsStore:
public protocol SettingsStore {
func objectForKey(key: String) -> AnyObject?
func setObject(value: AnyObject?, forKey key: String)
func dictionaryForKey(key: String) -> [String:AnyObject]?
}
public class UserDefaultsSettingsStore {
private let userDefaults: NSUserDefaults
public init() {
self.userDefaults = NSUserDefaults.standardUserDefaults()
}
public init(suiteName: String) {
self.userDefaults = NSUserDefaults(suiteName: suiteName)!
}
}
extension UserDefaultsSettingsStore: SettingsStore {
public func objectForKey(key: String) -> AnyObject? {
return self.userDefaults.objectForKey(key)
}
public func setObject(value: AnyObject?, forKey key: String) {
self.userDefaults.setObject(value, forKey: key)
self.userDefaults.synchronize()
}
public func dictionaryForKey(key: String) -> [String : AnyObject]? {
return self.userDefaults.dictionaryForKey(key)
}
}
If you substitute AnyObject with Any, I think you'll get the results you're looking for. Specifically, replace this line:
public class Setting<T: AnyObject>: Settable {
with this line
public class Setting<T: Any>: Settable {