Refactoring Pokemon Type Weekness

I can print data but can't assign it to a label in Swift

Jump to the Edit2 paragraph for the final answer!

Initial Answer:

I looks like you UI does not get updated after the controller fetches all the data.

Since all of you UI configuration code is inside the var pokemon / didSet, it's a good idea to extract it to a separate method.

private func updateView(with pokemon: Pokemon?, details: String?) {
guard let id = pokemon?.id, let data = pokemon?.image else { return }

navigationItem.title = pokemon?.name?.capitalized
infoLabel.text = pokemon?.description
infoView.pokemon = pokemon

if id == pokemon?.id {
imageView.image = UIImage(data: data)
infoView.configureLabel(label: infoView.skillLabel, title: "Skills", details: details ?? "")
}
}

and now you can easily call in the the didSet

var pokemon: Pokemon? {
didSet { updateView(with: pokemon, details: allNames) }
}

and fetchPokemons completion aswell

override func viewDidLoad() {
super.viewDidLoad()
configureViewComponents()
fetchPokemons { (names) in
guard var skillName = names as? String else { return }
self.pokemon?.skillName = skillName

self.allNames = skillName
print(self.allNames)
DispatchQueue.main.async {
self.updateView(with: self.pokemon, details: self.allNames)
}
}
}

It's super important to do any UI setup on the main queue.

Edit:

The fetch function may be causing the problems! you are calling handler multiple times:

func fetchPokemons(handler: @escaping (String) -> Void) {
controller.service.fetchPokes { (poke) in
DispatchQueue.main.async {
self.pokemon? = poke
guard let skills = poke.abilities else { return }
let names = skills.compactMap { $0.ability?.name?.capitalized }.joined(separator: ", ")
handler(names)
}
}
}

Edit2:

After looking at your codebase there are a couple of things you need to change:

1. fetchPokemons implementation

the handler of controller.service.fetchPokes gets called for every pokemon so we need to check if the fetched one is the current (self.pokemon) and then call the handler with properly formated skills.

func fetchPokemons(handler: @escaping (String) -> Void) {
controller.service.fetchPokes { (poke) in
guard poke.id == self.pokemon?.id else { return }
self.pokemon? = poke
let names = poke.abilities?.compactMap { $0.ability?.name?.capitalized }.joined(separator: ", ")
handler(names ?? "-")
}
}

2. update viewDidLoad()

now simply pass the names value to the label.

override func viewDidLoad() {
super.viewDidLoad()
configureViewComponents()
fetchPokemons { (names) in
self.pokemon?.skillName = names
self.infoView.configureLabel(label: self.infoView.skillLabel, title: "Skills", details: names)
}
}

3. Refactor var pokemon: Pokemon? didSet observer

var pokemon: Pokemon? {
didSet {
guard let pokemon = pokemon, let data = pokemon.image else { return }
navigationItem.title = pokemon.name?.capitalized
infoLabel.text = pokemon.description!
infoView.pokemon = pokemon
imageView.image = UIImage(data: data)
}
}

Should concrete implementation provide any public API not present in the interface it implements?

That is totally acceptable provided that the new methods aren't crucial to the operating of the class, and in particular to how it functions when someone thinks of it as the superclass or interface.

ArrayList provides good examples. It has methods that let you manage its internal memory, like ensureCapacity(int) or trimToSize(). Those are sometimes helpful if you know you're working with an ArrayList and need to be more precise about memory allocation, but they're not required for the basic operation of the ArrayList, and in particular, they're not required for having it operate as a general List.

In fact, interfaces themselves can add new methods in this way. Consider NavigableSet, which extends Set. It adds a whole bunch of methods that rely on the ordering of the set's elements (give me the first, the last, a subtree starting from here, etc). None of those methods are defined on Set, and even the fact that the elements are ordered isn't defined by the Set contract; but the Set methods all work just fine without the additional methods and ordering.

The advice to "code to the interface" is a good start, but it's a bit over-generalized. A refinement of that advice would be, "code to the most general interface that you need." If you don't need ArrayLists's methods (or its contract, such as its random-access performance), code to List; but if you do need them, then by all means use them.

Value of type '(Any) - ()' has no member 'currentTitle'

You have a typo:

else if Buttontr1.currentTitle == "O" && Buttonm.currentTitle == "O" && Buttonbl1.currentTitle == "O" 


Specifically, here:

Buttonm.currentTitle


To fix this, replace Buttonm with the correct name of the variable you want to reference (Buttonm1?).


Buttonm is defined as:

@IBAction func Buttonm(_ sender: Any) {

... hence your error.

How to make PHP more Javatized (aka strongly typed, more robust)?

There is a couple of ways of making your code more rigid.

  1. Use type hinting in methods to restrict argument type. This works for classes, interfaces, and arrays only. See example:

    class Foo { }
    class Bar {
    function quux(Foo $f) { }
    }

I use type hinting as much as I can to save myself from silly mistakes like passing null or primitive value whereas object is desired. Return values are unfortunately not strongly typed.

  1. Use access qualifiers: public, protected, private. While public members can be accessed by an outside entity, protected and private can be only accessed by instance of the same (base) class. Also consider using abstract and final qualifiers.

  2. Code against interfaces rather than classes, use composition and aggregation aggressively. Get used to use of dependency injection. Use well known design patterns where appropriate.

  3. Familiarise yourself with elements of Reflection and Aspect Oriented Programming. While there's very little PHP has to offer in AoP area, its reflection subsystem is quite good. You can use @annotations in doc-block comments to take advantage of metaprogramming. I use it a lot to annotate classes with location of unit tests. I also have an implementation of ACLs where controller methods are annotated with (to make long story short) requisite access level:

    /**
    * @AclActivity('view-services')
    * @AclActivity('edit-services')
    */
    public function editServiceAction() { ... }
  4. Finally, don't restrict yourself to PHP. Read about other languages, not only mainstream ones, but also obscure ones - these are full of brilliant ideas. Check out Lisp, Erlang, Rebol. Find unusual applications for existing languages, like server side JavaScript programming. Write your own language. Yes, it's going to suck, but you will learn a lot.

How to check and enable Action in a UIAlertController only if the textfield is not empty?

Using notification is a way but long work instead you can use simple delegate method or action method of textfield which is much easier as follow:

    weak var buttonActionToEnable: UIAlertAction?

alert.addTextField { (alertTextField1) in
alertTextField1.delegate = self
alertTextField1.placeholder = "Bench Press"
alertTextField1.text = textField.text

alertTextField1.addTarget(self, action: #selector(self.textFieldChanged), for: .editingChanged)
}

self.buttonActionToEnable = addAction
addAction.isEnabled = false

@objc func textFieldChanged(_ sender: Any) {

let textfield = sender as UITextField
self.buttonActionToEnable?.isEnabled = textfield.text.count > 0
}

Why should I use an IDE?

It really depends on what language you're using, but in C# and Java I find IDEs beneficial for:

  • Quickly navigating to a type without needing to worry about namespace, project etc
  • Navigating to members by treating them as hyperlinks
  • Autocompletion when you can't remember the names of all members by heart
  • Automatic code generation
  • Refactoring (massive one)
  • Organise imports (automatically adding appropriate imports in Java, using directives in C#)
  • Warning-as-you-type (i.e. some errors don't even require a compile cycle)
  • Hovering over something to see the docs
  • Keeping a view of files, errors/warnings/console/unit tests etc and source code all on the screen at the same time in a useful way
  • Ease of running unit tests from the same window
  • Integrated debugging
  • Integrated source control
  • Navigating to where a compile-time error or run-time exception occurred directly from the error details.
  • Etc!

All of these save time. They're things I could do manually, but with more pain: I'd rather be coding.

suggestions on how i can resolve this Semantic Issue error

You could enable ARC by going to Edit pressing Refactor and Convert to Objective-C ARC.

Swift init from unknown class which conforms to protocol

You will likely want to rethink this code in the future, to follow more Swift-like patterns, but it's not that complicated to convert, and I'm sure you have a lot of existing code that relies on behaving the same way.

The most important thing is that all the objects must be @objc classes. They can't be structs, and they must subclass from NSObject. This is the major reason you'd want to change this to a more Swifty solution based on Codable.

You also need to explicitly name you types. Swift adds the module name to its type names, which tends to break this kind of dynamic system. If you had a type Person, you would want to declare it:

@objc(Person)  // <=== This is the important part
class Person: NSObject {
required init(json: NSDictionary) { ... }
}

extension Person: JsonProtocol {
func convertToJSON() -> NSDictionary { ... }
}

This makes sure the name of the class is Person (like it would be in ObjC) and not MyGreatApp.Person (which is what it normally would be in Swift).

With that, in Swift, this code would be written this way:

if let className = obj[JSON_CLASS] as? String,
let jsonClass = NSClassFromString(className) as? JsonProtocol.Type {
arr.add(jsonClass.init(json: obj))
}

The key piece you were missing is as? JsonProtocol.Type. That's serving a similar function to +conformsToProtocol: plus the cast. The .Type indicates that this is a metatype check on Person.self rather than a normal type check on Person. For more on that see Metatype Type in the Swift Language Reference.

Note that the original ObjC code is a bit dangerous. The -initWithJSON must return an object. It cannot return nil, or this code will crash at the addObject call. That means that implementing JsonProtocol requires that the object construct something even if the JSON it is passed is invalid. Swift will enforce this, but ObjC does not, so you should think carefully about what should happen if the input is corrupted. I would be very tempted to change the init to an failable or throwing initializer if you can make that work with your current code.

I also suggest replacing NSDictionary and NSArray with Dictionary and Array. That should be fairly straightforward without redesigning your code.



Related Topics



Leave a reply



Submit