Passing data from child to parent, with an init in the child - ios

I am trying to pass data from my child view to the parent view. I also pass some data from the parent to the child.
struct Parent: View {
...
var body: some View {
OneColumnList(changeStage: changeStage, icons: icons, options: options)
...
struct Child: View {
var changeStage: (Int) -> ()
var icons: [String]
var options: [String]
init(icons: [String], options: [String]) {
self.icons = icons
self.options = options
}
...
I currently get this error on the child:
Return from initializer without initializing all stored properties
I guess I need to initialise changeStage, but I can't work out how to do it. I'd appreciate any help!

I think you need to enhance your init method of the Child.
E.g.
init(changeState: #escaping (Int) -> (), icons: [String], options: [String]) {
self.changeState = changeState
self.icons = icons
self.options = options
}
Then you can handle the state changes in your Parent, like for example
Child(changeState: { myInt in print(myInt) }, icons: icons, options: options)

changeState according to its type should be a completion block, that receives one Int argument and returns Void. Try adding this to the init:
self.changeState = { someNumber in }

Related

How to see all "ancestors" of a recursive data model?

I am receiving a JSON:
{
"categories":
[
{
"category_name": "example name",
"children":
[
{
"category_name": "example name"
},
{
...
As can be seen, the data is a recursive format. I was able to write the code for decoding it into my custom type which is:
struct Name: Codable {
let cat: String
let children: [cat]?
}
Now, for any cat, I would like to know the "path" of it. As in, I'd like know what are all the super(ancestor) categories. So, for the category "tablets", I would like to be able to traverse what the drill down structure looks like, which in this case could look like:
Electronics -> Computers -> Laptops and Tablets -> Tablets
How do I structure my code or data model to be able to retrieve this information for any category?
First, you'll want to add the path to Category so you have somewhere to store the data. For convenience, I'll also add a CategoriesResponse just to handle the top-level structure, but it's not really important:
struct CategoriesResponse: Decodable {
var categories: [Category]
}
struct Category {
let path: [String]
let categoryName: String
let children: [Category]
}
(I'm assuming what you want are just the names of the parent categories. If you want references of some kind, that's possible, but the data structures get a little more complicated. This basic approach will still work, though. Let me know if you need something like that, and I can expand the answer.)
And of course standard CodingKeys stuff:
private enum CodingKeys: String, CodingKey {
case categoryName = "category_name"
case children
}
The meat of the solution is that you need an init that can accept a KeyedDecodingContainer (rather than a Decoder) and a path, and handle decoding everything else.
// For each element, decode out of the container by hand rather than recursing into init(from: Decoder)
private init(from container: KeyedDecodingContainer<CodingKeys>, path: [String]) throws {
// Track our own path up to this point
self.path = path
// Unload the usual stuff
self.categoryName = try container.decode(String.self, forKey: .categoryName)
// Construct the children, one element at a time (if children exists)
var children: [Category] = []
if container.contains(.children) {
// Extract the array of children
var childrenContainer = try container.nestedUnkeyedContainer(forKey: .children)
while !childrenContainer.isAtEnd {
// Extract the child object
let childContainer = try childrenContainer.nestedContainer(keyedBy: CodingKeys.self)
// For each child, extend the path, decode
let child = try Category(from: childContainer, path: path + [self.categoryName])
// And append
children.append(child)
}
}
self.children = children
}
And finally, you need a Decodable implementation just to kick it all off:
extension Category: Decodable {
// Top level decoder to kick everything off
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
try self.init(from: container, path: [])
}
}
With that, it should work as expected using a standard Decoder:
let categories = try JSONDecoder().decode(CategoriesResponse.self, from: json).categories
Here is a basic recursive solution that adds the path of elements to an array with the root element as the first and the targeted element as the last.
It uses contains() so Category needs to conform to Equatable or it could be changed to use `contains(where:) instead like
contains(where: { $0.categoryName == target.categoryName })
if it is a more practical solution.
func extractChain(for target: Category, parent: Category, chain: inout [Category]) {
guard let children = parent.children else {
chain = []
return
}
chain.append(parent)
if children.contains(target) {
chain.append(target)
return
}
for category in children where category.children != nil {
extractChain(for: target, parent: category, chain: &chain)
if !chain.isEmpty { return }
}
chain = [] // No match, clear the array
}
I've only made some basic tests, one with a match the 3rd level down and one with no match so some further testing is probably needed.
You can use class and ancestor like this :
class Category: Codable {
let category_name: String
let children: [Category]?
var ancestor : Category?
func setupAncestor(ancestor: Category?) {
self.ancestor = ancestor
if let children = children {
for child in children {
child.setupAncestor(ancestor: self)
}
}
}
func ancestors() -> [Category] {
if let ancestor = ancestor {
var ancestorList = [ancestor]
ancestorList.append(contentsOf: ancestor.ancestors())
return ancestorList
}
return []
}
func ancestorsNames() -> [String] {
if let ancestor = ancestor {
var ancestorList = [ancestor.category_name]
ancestorList.append(contentsOf: ancestor.ancestorsNames())
return ancestorList
}
return []
}
func displayAncestors() {
for ancestor in ancestors() {
print("Ancestor : \(ancestor.category_name)")
}
}
func display(_ level: Int) {
print("Level \(level)")
print("Category : \(category_name) \(ancestorsNames())")
//displayAncestors()
if let children = children {
for child in children {
child.display(level+1)
}
}
}
}
struct Categories : Codable {
let categories: [Category]
func setupAncestors() {
for category in categories {
category.setupAncestor(ancestor: nil)
}
}
}
let json = "..."
let jsonData = json.data(using: .utf8)
let decoder = JSONDecoder()
do {
let categories = try decoder.decode(Categories.self, from: jsonData!)
categories.setupAncestors()
for category in categories.categories {
category.display(0)
}
} catch {
print("Json decode error : \(error)")
}
You can change the order and/or return only categoryName in the list
EDIT : corrected the code

Swift closures - order of code execution in regards to value assignment

I would like to know in details the proper order of execution when working on data in a completion handler. I have the following function definition on my Event class:
#nonobjc public class func fetchEvents(completion: #escaping ([Event]) -> () = { _ in }) -> [Event] {
let fetch = self.fetchEventRequest()
var entities: [Event] = []
context.perform {
entities = try! fetch.execute()
completion(entities)
}
return entities
}
And I call the above function from my view controller as such:
events = MyEvents.fetchEvents() { entities in
if (entities.count == 0) {
self.events = Event.getEventsFromAPI()
}
print(events.count) - // returns 0 at this point
self.eventsTableView.reloadData()
}
Upon calling self.eventsTableView.reloadData() my events variable is still empty. I would assume that since the completion handler executes after successful execution of the code inside the function, the events var would have already been assigned the returned value, no?
Here is a summary of your function in question:
...
context.perform {
entities = try! fetch.execute()
completion(entities)
}
return entities
Swift sees it like this:
Call this context.perform function
Call return entities
Process the output of context.perform inside the closure
Because of the order, it just returns the value and considers its work done. Your closure will likely complete, assuming it stays in scope, but otherwise nothing comes of it.
The easiest thing would be to change your function from:
#nonobjc public class func fetchEvents(completion: #escaping ([Event]) -> () = { _ in }) -> [Event]
to:
#nonobjc public class func fetchEvents(completion: #escaping ([Event]) -> () = { _ in })
The difference here is the removal of the [Event] return.
Then, inside your function remove the return entities line.
You now have an asynchronous function, so whatever you use to call it will have to use a closure for the [Event] value.
self.events = Event.getEventsFromAPI() becomes something like:
Event.getEventsFromAPI() = { [weak self] events in
self?.events = events
}

How to create an extension of Collection that removes null values?

I have this code, but it shows error:
extension Collection {
func removingOptionals() -> [Element] {
var result = [Element](); // Error: cannot call value of non-function type '[Self.Element.Type]'
self.forEach({ (element) in if let el = element { result.append(el); } });
return result;
}
}
If I removed the (), the error becomes: Expected member name or constructor call after type name.
This code is supposed to transform [String?] into [String] by discarding all the null values. Or any other optional data types.
How can I do this?
You can use flatMap {} for this, instead of creation own function. Here is example of usage:
let strings: [String?] = ["One", nil, "Two"]
print(strings.flatMap { $0 })
And result will be ["One", "Two"]
You can continue to use the flatMap behavior of the Optional as the other answer shows, but it's going to be deprecated on the next Swift iteration.
If you want to add the extension to the collection type, you need to be a create a type to box the Optional (You can't extend Collection if the type is generic, like Optional).
protocol OptionalType {
associatedtype Wrapped
func map<U>(_ f: (Wrapped) throws -> U) rethrows -> U?
}
extension Optional: OptionalType {}
extension Collection where Iterator.Element: OptionalType {
func removeNils() -> [Iterator.Element.Wrapped] {
var result: [Iterator.Element.Wrapped] = []
result.reserveCapacity(Int(self.count))
for element in self {
if let element = element.map({ $0 }) {
result.append(element)
}
}
return result
}
}

Can swift closures be set to a default value when used as a parameter in a function?

A pretty handy feature of Swift functions is that function parameters can have default values:
func someFunction(parameterWithDefault: Int = 42) {
//if no arguments are passed to the function call,
//value of parameterWithDefault is 42
}
If a parameter is a closure, is there a way to make it have a default value? See the example below:
func sendBody(
body: NSData? = nil,
success: (data: NSData) -> Void,
failure: (data: NSData?) -> Void) {
}
Is there a way to not force the developer to pass a value for success or failure when calling sendBody?
Yes, functions are just values, so you can supply them as defaults
// just to show you can do it with inline closures or regular functions
func doNothing<T>(t: T) -> Void { }
func sendBody(
body: NSData? = nil,
success: (data: NSData) -> Void = { _ in return },
failure: (data: NSData?) -> Void = doNothing
)
{ }
Alternatively, you could make them optional, that way you can detect if the caller passed one:
func sendBody(
body: NSData? = nil,
success: ((NSData) -> Void)? = nil,
failure: ((NSData?) -> Void)? = nil
)
{ success?(NSData()) }
sendBody(success: { _ in print("ah, yeah!") })
Also worth noting if you’re doing this: if the caller uses the trailing closure syntax, this will be the last closure in the argument list. So you want the last one to be the one the user is most likely to want to supply, which is probably the success closure:
func sendBody(
body: NSData? = nil,
success: ((NSData) -> Void)? = nil,
failure: ((NSData?) -> Void)? = nil
)
{
if success != nil { print("passed a success closure") }
if failure != nil { print("passed a failure closure") }
}
// this prints "passed a failure closure"
sendBody { data in
print("which closure is this?")
}
Other than this, the order in the function declaration doesn’t matter to the caller – defaulted arguments can be supplied in any order.
You could do something like this,
let defaultSuccess: NSData -> Void = {
(data: NSData) in
}
let defaultFailure: NSData? -> Void = {
(data: NSData?) in
}
func sendBody( body: NSData? = nil, success: (data: NSData) -> Void = defaultSuccess, failure: (data: NSData?) -> Void = defaultFailure) {
}
Then, you may be able to call either one of these methods. Notice sendBody which is called with default parameters.
sendBody()
sendBody(body: , success: , failure: )
You can also call with all the variants like passing just one of the argument in the above method, for that you have to call it with named parameter.
sendBody()
sendBody(body:)
sendBody(failure: )
sendBody(success:)
sendBody(body: , success: , failure: )
How to set a default value for a function parameter. Swift 4 and (probably) 5.
func someFunction(age: Int, doSomething:#escaping () -> Void = {}){
//do work here
//
doSomething()
}
Then you can do this
someFunction(age: 18) {
print("hello")
}
someFunction(age: 19)
You may or may not need to use the #escaping keyword. See Swift #escaping and Completion Handler for that.
My preferred way to specify public facing closures - in particular completion closures which you might want to store somewhere for later - is to define a typealias for them, like this:
public typealias FooCompletion = (String) -> Void
Then in the public facing function you can easily make it optional like this:
var onCompletion: FooCompletion? = nil
public func foo(completion: FooCompletion? = nil) {
// Store completion for later
onCompletion = completion
}
The completion parameter is optional, so it's allowed to be nil, and the default value is nil, meaning the caller doesn't have to specify it. Also, because you use the type in more than one place, if you need to change its definition during development there's only one place to do so. It's easy to call too:
private func someBackgroundThing() {
var completionString = "done"
...
onCompletion?(completionString)
}

Custom object lost property data after NSArray filter

I want to query a custom object from an NSArray, here is a function I wrote:
func retrieveObject (objects: [CustomClass], identifier : String) -> CustomClass {
var retrievedObject : [CustomClass] = objects.filter({
return $0.identifier == identifier
})
return retrievedObject.first!
}
When I use it, the resulted object seems to have lost most of the property values in that object:
let obj : CustomClass = self.retrieveObject(objectList as! [CustomClass], "one")
println("\(obj.propertyA)")
The result shows "", while printing the object from the original object list shows value:
println("\(objectList.first!.propertyA)")
What seems to be the issue?
More Information:
The objectList above is a result of an asynchronous web request, let's assume that the objects in it are problem-free because they return the correct property value when printed.
Code of one step above before the array filter:
private var objectList : [AnyObject]!
private var object : CustomClass
self.webRequest(request, onSuccess: {(objects: [AnyObject]!) -> Void in
self.objectList = objects
self.object = self.retrieveObject(self.objectList, identifier: "one")
//I tried passing both self.objectList and objects
})
Problem Solved
This is not an issue with Swift or whatever. This is a data issue. The above code works fine.
Not sure, there doesn't seem to be anything wrong with the code you've provided us. I've recreated it in playgrounds and seems to print normally.
Code:
class CustomClass {
let identifier: String
let propertyA = "Printing!"
init(identifier: String) {
self.identifier = identifier
}
}
let objectList = [CustomClass(identifier: "one")]
func retrieveObject (objects: [CustomClass], identifier: String) -> CustomClass {
return objects.filter { $0.identifier == identifier }.first!
}
let object = retrieveObject(objectList, "one")
println("\(object.propertyA)") // Prints "Printing!"
println("\(objectList.first!.propertyA)") // Prints "Printing!"
EDIT:
Simplified it a bit
Except for line:
var retrievedObject : [CustomClass] = objects.filter({
return $0.identifier == identifier
})
that should be:
var retrievedObject : [CustomClass] = objects.filter { $0.identifier == identifier;};
In your version, you pass a strange value for filter argument (because of misplaced parenthesis) that does not match awaited filter argument type: (T) -> Bool
I tested Jacobson's code and I confirm it is working.

Resources