I’m doing a news management exercise. I have some classes:
Author
News (father)
NewsVideo (son) (this class has the same properties as the father, plus it has two properties)
NewsManagement
From the NewsManagement class, I have to create a function that inserts a news.
I have already created an empty array of type News.
NewsManagement class has only one property, an empty News array.
var newsCollection: [News] = []
class NewsManagement {
var news: [News] = []
init(news: [News]) {
self.news = news
}
func insertNews(title: String, text: String, date: Int, author: Autore, urlVideo: String?, videoLength: Double?) -> String? {
//these are the properties of the newsVideo class
guard let urlVideo = urlVideo, let videoLength = videoLength else {
return nil
}
self.news.append(News.init(title: String, text: String, date: Int, author: Author))
return "news addition”
}
}
var manager = NewsManagement.init(news: newsCollection)
manager.insertNews(title: "prima prova", text: "spero funzioni", date: 13, author: authors[3], urlVideo: "www.prova.it", videoLength: 4.5)
//authors is an array of instance of class Author
Unfortunately, it’s not working.
Do you know where the error is?
Or do you know another way?
I’ve been trying solutions for too long, but none of them work
Thank you!
Syntactic issues
There are a couple of syntax issues that prevent the code from compiling.
THere's a small typo with Autore instead of Author.
Then the bigger syntactical issue: replace the line which does not provide any value for the arguments:
self.news.append(News.init(title: String, text: String, date: Int, author: Author))
with a line that privides the named arguments:
self.news.append(News.init(title: title, text: text, date: date, author: author))
There is by the way no need to call explicitly init. And there's no need to use self when there is no ambiguity between a property and an argument. So you could further simplify:
news.append(News(title: title, text: text, date: date, author: author))
Not related: Design issues
The fact of providing all the parameters to create a News to the news manager is a design issue:
to add news, one must provide all the required parameters for all kind of news to the news manager.
the news manager has then not only the responsibility to manage the news collection, it suddenly has the additional responsibility to create each single News. As a consequence if a new kind of News is added (e.g. AudioNews) or if one of the news constructor would change the news manager would have to change too. More than one reason to change is a problem in view of the Single Responsibility Principle.
the news manager must also know the details of each constructor of each possible kind of news. ANd it has to know the rule which parameters create a which kind of news. So it needs to know much more about a lot of classes than really necessary. This could be seen as a problem in view of the Principle of Least Knowledge
Worse, the insertNews() has an interface that is cluttered with a lot of parameters that are not always needed. This is a problem in view of the interface segregation principle.
My advice: have News created separately using the right kind of news (e.g. VideoNews) with the appropriate constructor, and simplify NewsManager to insert, access and remove already existing news.
Related
Here I have a database, which I want to easily use for my table view. However, I can't reach the properties, because I don't know how to assign from a dictionary to a string. It tells me:
Cannot assign value of type 'Dictionary.Keys' to
type 'String'
import Foundation
struct Test {
var title: String
var tagPreview: Tagpreview
}
struct Tagpreview {
var tag: [String?:String?]
}
var cases = [
Test(title: "title1", tagPreview: Tagpreview(tag: ["tag1": "preview1"])),
Test(title: "title2", tagPreview: Tagpreview(tag: ["tag2": nil])),
Test(title: "title3", tagPreview: Tagpreview(tag: [nil: nil])),
Test(title: "title4", tagPreview: Tagpreview(tag: ["tag4": "preview4", "tag5": nil]))
]
I want to use the keys and the values from the dictionary in the second struct to populate the text labels later in a cell:
cell.titleLabel?.text = cases[indexPath.row].tag.preview.keys //ERROR
cell.textLabel?.text = cases[indexPath.row].tag.preview.values//ERROR
There is something about dictionaries that I can't find anywhere as well as a comprehensive solution for this issue.
Now, if you know another way how to populate them easily, I'd much appreciate that! Thank you a lot in advance and have a good day!
The error you are receiving is due to Dictionary.keys returning a collection of the Type you selected as key. In your case the call cases[indexPath.row].tag.preview.keys returns a Collection of String? (similar to [String?])
Now if you wish to access a specific value from this collection, you should be able to do so like this:
let someText = cases[indexPath.row].tagPreview.tag.keys.map{ $0 }[someIndex]
Note that the use of map(). It merely converts the Strings collection to an Array of Strings, whose index is Int, thus making it easier to access the individual elements (otherwise you'd need abit more general/cumbersome iteration API of Collection).
Just side comment, it seems a bit difficult to extract data and map it directly to the view, if you intend to use such mapping many places it may pay out to have some intermediate data types which are easier to use when presenting; it really depends on your preference and the overall problem.
The data I store is at the moment written in english. So I plan to make this content localized/translatable and would like to give this option to users.
Here are my user stories:
(1) As a user I want to select the preferred language of my content (e.g. german). If the text is not available in german, please give me the content in the fallback language (e.g. english).
(2) As a user I want to be able to translate a given model.
Given the following:
1) A model, e.g. Message with two properties: title and description. It can be expected these two properties are in english (by default).
2) A command, e.g. translateMessage which will receive a payload with: title, description and locale.
What's an accepted way to handle localization of those models?
A solution I come up with:
Let's say our Message will have the following properties (in Typescript):
{
title: string;
description: string;
}
I would extend it with a translations object. Where each key in there is a locale and has an object with translated strings.
{
title: string;
description: string;
translations: {
[key: string]: {
title: string;
description: string;
}
}
}
The readModel will be the same as above and it is up to the client to decide which strings to display.
The command translateMessage will receive the payload as described above and publish an event with that. The readModel will react to that event in a way, that it pushes the new payload data onto the translations object in that model.
For model creation, if the data is received in a different locale than the default one, then the event handler will put those translated strings prefix with the locale they were written in, into the root, e.g. model.title can be something like [de] Nachrichtentitel. The model would look like this:
{
title: '[de] Nachrichtentitel'
description: '[de] Nachrichteninhalt'
translations: {
de: {
title: 'Nachrichtentitel'
description: 'Nachrichteninhalt'
}
}
}
You see any problems with that? Are there other already established best-practices on how to do that?
I'm using Siesta with an API that returns a very lightweight list response for root entities. For instance, for /entity this is what the response looks like:
{
count: 200,
results: [
{
url: "https://example.com/api/entity/1/",
name: "foo"
},
{
url: "https://example.com/api/entity/2/",
name: "bar"
},
{
url: "https://example.com/api/entity/3/",
name: "bat"
}]
}
The full object found at the url in the results has an avatar property that I'd really like to show in my table view for this list but I can't figure out how to make that happen with the Siesta framework. Is it possible to fetch more details from the underlying /entity/1 endpoint as part of loading the resource for the /entity list?
In Siesta’s view of the world, one url ⟺ one resource. There is thus a “summary list” resource, /entity, plus a separate “entity detail” resource for each row, /entity/1 etc. It doesn’t matter that they happen to share some of the same data; Siesta itself doesn’t make any effort to merge, synchronize, prepopulate one resource from the other. Separate URLs, separate resources.
The rule of thumb is, “If you need data from a resource, observe that resource.” Since you want to use both summary info from /entities and detail info from /entities/n, you observe both resources.
Here is a sketch of an approach you might use:
Get your table view showing just the info from /entities, no avatars. You can use RepositoryListViewController from the example project as a starting point.
Make each table cell accept a summary model, and observe its corresponding detail resource:
class EntityTableViewCell: UITableViewCell, ResourceObserver {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var avatar: RemoteImageView!
private var summary: EntitySummary?
private var detailResource: Resource?
func showEntity(summary: EntitySummary) {
self.summary = summary
detailResource?.removeObservers(ownedBy: self)
detailResource = MyApi.resource(absoluteURL: summary?.url)
detailResource.addObserver(self).loadIfNeeded()
}
Now populate the cell in resourceChanged(), mixing and matching from the summary and detail as you see fit:
func resourceChanged(resource: Resource, event: ResourceEvent) {
let detail: EntityDetail? = detailResource?.typedContent()
nameLabel.text = detail?.name ?? summary?.name
avatar.imageURL = detail?.avatar
}
You might also want to stop observing when the cell moves out of view:
override func prepareForReuse() {
showEntity(nil)
}
}
(This sketch assumes that you have separate EntitySummary and EntityDetail models. You might also have a single Entity model with the detail-only fields optional, or you might just be using raw JSON dictionaries. The approach is the same regardless.)
Here’s what happens when a cell scrolls into view:
Your cellForRowAtIndexPath calls showEntity(_:), passing an EntitySummary it got from the /entities resource.
The cell starts observing /entities/n.
This immediate triggers resourceChanged(). The detail resource has no data yet, so your cell immediately gets populated with summary info only.
Eventually the detail resource loads. If your cell is still observing it, then resourceChanged() gets called again, and this time it sees the detail info.
Note that in #4, if your cell got scrolled out of view and reused before that detail resource loaded, then your cell will no longer be observing it — and thus the late-arriving response will not clobber the reused cell’s contents.
I have a hierarchical set of data that I want to retrieve information from Firebase. Below is how my data looks:
However, my issue is this: Upon looking at how the data is structured, when I want to grab the name or object id of an attendee, I have to perform the following code:
func getAttendees(child: NSString, completion: (result: Bool, name: String?, objectID: String?) -> Void){
var attendeesReference = self.eventsReference.childByAppendingPath((child as String) + "/attendees")
attendeesReference.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) -> Void in
//Get the name/object ID of the attendee one by one--inefficient?
let name = snapshot.value.objectForKey("name") as? String
let objectID = snapshot.value.objectForKey("objectID") as? String
if snapshot != nil {
println("Name: \(name) Object ID: \(objectID)")
completion(result: true, name: name, objectID: objectID)
}
}) { (error) -> Void in
println(error.description)
}
}
Now this goes through every attendee child and grabs the name and object id one by one. When the function completes, I store each value into a dictionary. Upon doing so, this function is called multiple times and can be very slow especially when going to/from a database so many times. Is there a more efficient way to do this? I have tried to look into FEeventType.Value but that seems to return everything within the attendees child, when all I really want are the name and objectID of each attendee stored into some sort of dictionary. Any help would be appreciated. Thanks!
One of the golden rules of Firebase is to only nest your data when you always want to retrieve all of it. The reason for this rule is that Firebase always returns a complete node. You cannot partially retrieve some of the data in that node and not other data.
The Firebase guide on structuring data, says this about it:
Because we can nest data up to 32 levels deep, it's tempting to think that this should be the default structure. However, when we fetch data at a location in our database, we also retrieve all of its child nodes. Therefore, in practice, it's best to keep things as flat as possible, just as one would structure SQL tables.
You should really read that entire section of the docs, since it contains some pretty good examples.
In your case, you'll need to modify your data structure to separate the event attendees from the event metadata:
events
-JFSDFHdsf89498432
eventCreator: "Stephen"
eventCreatorId: 1764137
-JOeDFJHFDSHJ14312
eventCreator: "puf"
eventCreatorId: 892312
event_attendees
-JFSDFHdsf89498432
-JSAJKAS75478
name: "Johnny Appleseed"
-JSAJKAS75412
name: "use1871869"
-JOeDFJHFDSHJ14312
-JaAasdhj1382
name: "Frank van Puffelen"
-Jo1asd138921
name: "use1871869"
This way, you can retrieve the event metadata without retrieving the attendees and vice versa.
I just started studying F# today and have begun working my way through the F# tutorials at http://www.tryfsharp.org/Learn/getting-started#data-structures
In the section above three snippets of code are provided to explain records and option types:
type Book =
{ Name: string;
AuthorName: string;
Rating: int option;
ISBN: string }
let unratedEdition =
{ Name = "Expert F#";
AuthorName = "Don Syme, Adam Granicz, Antonio Cisternino";
Rating = None;
ISBN = "1590598504" }
let printRating book =
match book.Rating with
| Some rating ->
printfn "I give this book %d star(s) out of 5!" rating
| None -> printfn "I didn't review this book"
I thought that I would be able to apply the printRating like so
printRating unratedEdition
but I get the following error
stdin(63,13): error FS0001: This expression was expected to have type
FSI_0005.Book
but here has type
FSI_0009.Book
I'm kinda stuck as to what I am doing wrong here. Any obvious reason that I am totally missing?
Glad that you figured out how to solve the problem and continue with the tutorials!
I think the automatic loading and evaluation of code snippets in Try F# is a bit confusing. The problem is that you first evaluate first snippet, which defined Book and unratedEdition. Then, you evaluate second snippet which re-defines Book - now, to F# interactive, this is a different type hiding the previous definition - together with printRating which is a function working on the new version of Book. When you call:
printRating unratedEdition
You are calling printRating which is a function that takes the new Book with a value of the old Book type as an argument (because unratedEdition is defined from an earlier interaction; it does not automatically get updated to the new Book type and the two types are not compatible).
You can understand this if you evaluate the following three snippets one by one:
// Snippet #1: Define first version of the 'Book' type and a value of
// this type named 'unratedEdition'
type Book =
{ Name: string; AuthorName: string; Rating: int option; ISBN: string }
let unratedEdition =
{ Name = "Expert F#"; Rating = None; ISBN = "1590598504";
AuthorName = "Don Syme, Adam Granicz, Antonio Cisternino"; }
// Snippet #2: Now, we re-define the 'Book' type (we could also add/remove
// fields to make it actually different, but even without that, this still
// defines a new type hiding the original one). We also define a function that
// operates on the new 'Book' type
type Book =
{ Name: string; AuthorName: string; Rating: int option; ISBN: string }
let printRating book =
match book.Rating with
| Some rating ->
printfn "I give this book %d star(s) out of 5!" rating
| None -> printfn "I didn't review this book"
// Snippet #3: This will not work, because we are calling function taking new
// 'Book' with old 'Book' as an argument. To make this work, you need to evaluate
// one (or the other) definition of Book, then evaluate 'unratedEdition' and then
// 'printRating' (so that the value and function operate on the same 'Book' type)
printRating unratedEdition
Note that the editor will complain that the above code is not valid, because it defines Book twice, so you can really only get this problem (easily) in Try F# which erases the content of the editor when loading a new snippet
Well I solved my own problem by running all the above code in one shot. i.e. Posting all 3 snippets plus my
printRating unratedEdition
into the REPL together and then hitting RUN. Previously I was using the "load and run" for each individual snippet. I guess it must be some issue with the REPL, or my limited understanding of how a REPL works.
EDIT**
I found myself running into this problem a number of times throughout the tutorial. So, if you have an error, and don't know why, try inserting all the relevant code into the REPL, THEN hit run. This has solved every issue I have run into so far.