Desired Outcome
- Initialize MyClass with fixedValues as the dictionaryRepresentation argument.
- Set fixed values before executing the init line
Problem
- fixedValues isn't being set before the
self.init(... line is ran.
Attempted Solutions
- Returns instead of completions (posted state)
- Completions, DispatchGroups, Notify
- Semaphore wait for set
class WaitForValue {
private var semaphore = DispatchSemaphore(value: 0)
var valueToWaitFor: String? {
didSet {
if valueToWaitFor != nil {
semaphore.signal()
}
}
}
func waitForValue() {
_ = semaphore.wait(timeout: .distantFuture)
}
}
let waitForValueInstance = WaitForValue()
// This will block until valueToWaitFor is set to a non-nil value
DispatchQueue.global().async {
waitForValueInstance.waitForValue()
print("Value received: \(waitForValueInstance.valueToWaitFor ?? "nil")")
}
// Simulate setting the value after some delay
DispatchQueue.global().asyncAfter(deadline: .now() + 2) {
waitForValueInstance.valueToWaitFor = "Hello, World!"
}
My Code:
class MyClass {
convenience init?(_ object: Object) {
guard let values = object.getValues(forKeys: MyAttributes.allAttributes) else { return nil }
var fixedValues = values
MyClass.correctedValues(object: object, values: values) { correctedValues in
fixedValues = correctedValues
}
self.init(dictionary: fixedValues, type: object.type)
}
private static func correctedValues(object: Object, values: [String: Any], completion: @escaping([String: Any]) -> Void) {
var valuesDictionary = values
let fixes = objectFixes(object: object)
let valueObjects = values["children"] as? [Object] ?? []
var wrappedObjects = [WrappedObject]()
valueObjects.forEach {
let wrapped = WrappedObject($0)
wrappedObjects.append(wrapped)
}
fixedChildrenValues(originalValues: wrappedObjects, fixes: fixes) { correctedChildren in
valuesDictionary["children"] = correctedChildren
completion(valuesDictionary)
}
}
private static func fixedChildrenValues(originalValues: [WrappedObject], fixes: [String: [Attribute: String]], completion: @escaping ([[String: Any]]) -> Void) {
var fixedChildrenArray = [[String: Any]]()
for childValue in originalValues {
var childAttributes = childValue.getValues(forKeys: [
"children",
"identifier",
"props",
"name"
]) ?? [:]
// Apply fixes
if let matchingIdentifier = childAttributes["identifier"] as? String,
let fix = fixes[matchingIdentifier] {
fix.forEach { (key, value) in
childAttributes[key] = value
}
}
// Attempt to extract child elements and continue traversal
if let childrenObjects = childAttributes["children"] as? [Object] {
var wrappedObjects = [WrappedObject]()
for childObject in childrenObjects {
wrappedObjects.append(WrappedObject(childObject))
}
if !wrappedObjects.isEmpty {
fixedChildrenValues(originalValues: wrappedObjects, fixes: fixes) { correctedChildren in
childAttributes["children"] = correctedChildren
fixedChildrenArray.append(childAttributes)
}
} else {
fixedChildrenArray.append(childAttributes)
}
} else {
fixedChildrenArray.append(childAttributes)
}
}
completion(fixedChildrenArray)
}
private static func objectFixes(object: Object) -> [String: [Attribute: String]] {
var objectFixes = [String: [Attribute: String]]()
let objects = object.objects
for i in 0..<objects.count {
let obj = objects[i]
if !obj.isEnabled {
objectFixes[obj.identifier] = ["props": "false"]
}
}
return objectFixes
}
}
I'm really not sure what you are trying to do here.
I tried you code in a playground (I had to guess a lot of code tho) but it's working for me.
here is the code I have :
And here are the prints I get :
Which indicate that the fixedValue is set before self.init is called.
Also I'm not quite confident in the design pattern here. It seems to me that you should use a factory pattern instead of the convenience init.
But maybe I miss something.
Finally I don't see any async code (except in the WaitForValue class which doesn't seems to be used anywhere. Therefore I wouldn't use completion handler here, because it makes your code complicated to read, when it's simply linear.