class in playground vs in program file - swift

I have a question about something simple that works in Playground but not in a Project: (Playground code below)
In the project where the class is in a separate swift file the code correction won't show me the the person.lastName and if I fully type it give me an error.... hmm, very strange - might be a beginner error or?
How would I have to state in in the program file and the separate swift file to work?
Thanks,
Roman
import UIKit
class human {
var firstName = ""
var lastName = ""
}
let person = human()
person.lastName = "Smith"
person.firstName = "Peter"
print (person.firstName)
print (person.lastName)

This is why I hate playgrounds. They are not really Swift. In real Swift, all executable code must be inside a function (e.g. a method of some class or struct or enum); you can't have lines like person.lastName = "Smith" just hanging in space like that.
So in a real iOS project you'd need to write something more like this:
class Human {
var firstName = ""
var lastName = ""
}
func test() {
let person = Human()
person.lastName = "Smith"
person.firstName = "Peter"
print (person.firstName)
print (person.lastName)
}
And even then nothing would happen until you actually call test(), and you can't do that except in a function. This is why people usually test code in the viewDidLoad of a view controller.
class Human {
var firstName = ""
var lastName = ""
}
class ViewController : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let person = Human()
person.lastName = "Smith"
person.firstName = "Peter"
print (person.firstName)
print (person.lastName)
}
}

Related

Swift - search entire struct for string

To search for a string included in a struct I use:
let results = myArray.filter( {$0.model.localizedCaseInsensitiveContains("bu")} )
But say the struct has several properties that I'd like to search - or maybe I'd even like to search all of them at one time. I can only filter primitive types so leaving 'model' out won't work.
Solution -------------------------
While I really liked the idea of using key paths as Matt suggested below, I ended up adding a function to my struct that made my view controller code much cleaner:
struct QuoteItem {
var itemIdentifier: UUID
var quoteNumber: String
var customerName: String
var address1: String
func quoteItemContains(_ searchString: String) -> Bool {
if self.address1.localizedCaseInsensitiveContains(searchString) ||
self.customerName.localizedCaseInsensitiveContains(searchString) ||
self.quoteNumber.localizedCaseInsensitiveContains(searchString)
{
return true
}
return false
}
Then, in my controller, quotes is an array of QuoteItem that I can search by simply writing:
searchQuoteArray = quotes.filter({ $0.quoteItemContains(searchString) })
This sounds like a job for Swift key paths. Just supply the key paths for the String properties you want to search.
struct MyStruct {
let manny = "Hi"
let moe = "Hey"
let jack = "Howdy"
}
let paths = [\MyStruct.manny, \MyStruct.moe, \MyStruct.jack]
let s = MyStruct()
let target = "y"
let results = paths.map { s[keyPath:$0].localizedCaseInsensitiveContains(target) }
// [false, true, true]
I hope i understood you correct. I think with this piece of code you can achieve what you want:
struct ExampleStruct {
let firstSearchString: String
let secondSearchString: String
}
let exampleOne = ExampleStruct(firstSearchString: "Hello", secondSearchString: "Dude")
let exampleTwo = ExampleStruct(firstSearchString: "Bye", secondSearchString: "Boy")
let exampleArray = [exampleOne, exampleTwo]
let searchString = "Hello"
let filteredArray = exampleArray.filter { (example) -> Bool in
// check here the properties you want to check
if (example.firstSearchString.localizedCaseInsensitiveContains(searchString) || example.secondSearchString.localizedCaseInsensitiveContains(searchString)) {
return true
}
return false
}
for example in filteredArray {
print(example)
}
This prints the following in Playgrounds:
ExampleStruct(firstSearchString: "Hello", secondSearchString: "Dude")
Let me know if it helps.

Swift Unwrapping optional class variable yields failure

In my code I have two classes: Reviews and BlogPosts:
class Review {
var author = ""
var stars = 0
}
class BlogPost {
var title = ""
var body = ""
var author = ""
var review: Review?
}
The review variable in BlogPost is optional, since not all blog posts might have a review.
I have a function which prints the amount of stars of a post:
func checkForPostStars(post: BlogPost) {
if let review = post.review {
print("\"\(post.title)\" has: \(review.stars) stars")
} else {
print("There is no review for the post.")
}
}
I then create two blog posts. The first one has no review, meaning that the function should print "There is no review for the post". For the other review I add an author and a star amount, but when I run the function it will still print "There is no review for the post".
var firstPost = BlogPost()
firstPost.title = "Famous developer has died!"
firstPost.body = "Lorem ipsum"
firstPost.author = "Riccardo Perego"
var secondPost = BlogPost()
secondPost.title = "iOS 12 is finally out!"
secondPost.body = "Lorem ipsum"
secondPost.author = "Riccardo Perego"
secondPost.review?.author = "John"
secondPost.review?.stars = 4
checkForPostStars(post: firstPost)
checkForPostStars(post: secondPost)
I have discovered that I can fix the issue by adding secondPost.review = Review() nevertheless, I would like the compiler to do it automatically as soon as it sees that I set a value for stars or author. Thanks.
The problem lies within these 2 lines:
secondPost.review?.author = "John"
secondPost.review?.stars = 4
The review is not initialized. It's like you would set some value to nil and expect it's properties to live even if it doesn't work... That's why the ? is there.
You should add constructor to the Review:
class Review {
var author: String
var stars: Int
init(author: String = "", stars: Int = 0) {
self.author = author
self.starts = stars
}
}
Also it is good practice not to assign the variables on class level scope, rather use it in initialiser.
Simply the problem is that you do not create Review instance, so you cannot add properties to it... you should handle it like this:
secondPost.review = Review(authoer: "John", stars: 4)
Also, for performance reasons, you should make the Review object struct instead of class...
So if you create a struct instead, Swift figures out the initialiser for you and life's even better:
struct Review {
var author: String
var stars: Int
}

No Class/Struct Autocomplete in Swift

I currently have two structs and a class in my sample code. One struct is for Tiger, one is for Balloon and on is for Lion. Each are below:
struct Tiger {
var name = ""
var age = 0
var breed = ""
var image = UIImage(named: "")
func chuff() {
println("\(name): Chuff Chuff")
}
func chuffNumberOfTimes(numberOfTimes:Int) {
for (var i = 0; i < numberOfTimes; i++) {
self.chuff()
}
}
func ageInTigerYearsFromAge(regularAge:Int) -> Int {
return regularAge * 3
}
func randomFact() -> String {
let randomFactNumber = Int(arc4random_uniform(UInt32(3)))
var randomFact:String
switch randomFactNumber {
case 0:
randomFact = "Tigers are 10 feet tall."
case 1:
randomFact = "Tigers are amazing."
case 2:
randomFact = "Tigers have 10 feet."
default:
randomFact = ""
}
return randomFact
}
}
struct Balloon {
var number = 0
var image = UIImage(named: "")
}
class Lion {
var name = ""
var age = 0
var isAlphaMale = false
var image = UIImage(named: "")
var subSpecies = ""
}
Each one is in its own file named identically to the struct/class & ".swift". However, the only one that autocompletes itself while typing in ViewController.swift is the Tiger struct. For instance, if I were to set myTiger = Tiger( it would suggest name: String, age: Int etc. How can I get the other two to do the same? I really like defining the variables inline instead of having 5 lines of code to define all of the variables.
Is anyone familiar with how that works or why the Tiger struct would do it while the Balloon and Lion structs don't?
Classes don't get the initializers for free as the Struct does, if you want to have an initializer for a class you have to create it:
class Lion {
var name = ""
var age = 0
var isAlphaMale = false
var image = UIImage(named: "")
var subSpecies = ""
init(name: String, age:Int, isAlphaMale: Boolean, image: UIImage, subSpecies: String){
self.name = name
self.age = age
self.isAlphaMale = isAlphaMale
self.image = image
self.subSpecies = subSpecies
}
}
The Balloon should work automatically, probably a clean and build in your code so xcode can be updated will fix the problem.
From Apple Documentation:
Memberwise Initializers for Structure Types
All structures have an automatically-generated memberwise initializer,
which you can use to initialize the member properties of new structure
instances. Initial values for the properties of the new instance can
be passed to the memberwise initializer by name:
copied from a previus part of the document to add context
struct Resolution {
var width = 0
var height = 0
}
let vga = Resolution(width: 640, height: 480)
Unlike structures, class
instances do not receive a default memberwise initializer.
With the class above you will have the initializer as in the screenshot below:
As you can see in the screenshot Balloon works perfectly fine as well
No need to enter once to start work, xcode is not perfect and sometimes it needs a clean and a new build to make it be in sync with your classes and structs.

Unable to add object to a NSMutableArray

I'm attempting to load a mutable array and got stuck.
Essentially what I want to do is to be able to add any class object to a global array. In this example I merely have name & address.
Here's a playground snippet:
import Foundation
var gDownloaders:NSMutableArray?
class downloader {
var name:String?
var address:String?
init(name:String, address:String) {
self.name = name
self.address = address
}
}
let one = downloader(name: "Ric Lee", address: "901 Edgewood")
let two = downloader(name: "Richard Brauer", address:"1010 Red Oak")
let three = downloader(name: "Meredith Lind", address: "410 Sunset Blvd")
gDownloaders?.addObject(one)
gDownloaders?.addObject(two)
gDownloaders?.addObject(three)
println(gDownloaders)
BTW: Xcode insist that I have the '?'/gDownloaders.
All I'm getting is nil for gDownloaders; even though each of the three objects are bona fide with data.
What am I doing wrong?
...should I use the Array vs NSMutableArray class instead?
You never initialize gDownloaders. You declare what type the variable gDownloaders should have, but you don't put anything in that variable.
Try:
var gDownloaders:NSMutableArray? = NSMutableArray()
In swift, in a playground try this:
import Foundation
class downloader {
var name:String?
var address:String?
init(name:String, address:String) {
self.name = name
self.address = address
}
}
var gDownloaders = [downloader]()
let one = downloader(name: "Ric Lee", address: "901 Edgewood")
let two = downloader(name: "Richard Brauer", address:"1010 Red Oak")
let three = downloader(name: "Meredith Lind", address: "410 Sunset Blvd")
gDownloaders.append(one)
gDownloaders.append(two)
gDownloaders.append(three)
println(gDownloaders[0].name!)

Optional chaining and binding

Inside the optional binding when I assign the variable ammo (and ammo2) I am pretty sure that I should be using ! to unbox the optional, but on my first attempt I put ? by mistake and was a little confused why it still worked, can anyone cast some light onto whats going on there?
let soldierA = Soldier(name: "Brian")
soldierA.weapon = Weapon()
soldierA.weapon!.grenadeLauncher = GrenadeLauncher()
let soldierB = Soldier(name: "Gavin")
soldierB.weapon = Weapon()
let soldierC = Soldier(name: "Berty")
soldierC.weapon = Weapon()
soldierC.weapon!.grenadeLauncher = GrenadeLauncher()
soldierC.weapon!.grenadeLauncher!.ammo = 234
let missionTeam = [soldierA, soldierB, soldierC]
for eachSoldier in missionTeam {
if let launcherAvailable = eachSoldier.weapon?.grenadeLauncher? {
var ammo = eachSoldier.weapon!.grenadeLauncher!.ammo // PRETTY SURE THIS IS RIGHT
var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo // SHOULD THIS WORK, IT DOES?
println("SOLDIER: \(eachSoldier.name), Weapon has launcher AMMO: \(ammo)")
} else {
println("SOLDIER: \(eachSoldier.name), Weapon does not have launcher ")
}
}
.
// CLASSES
class Soldier {
var name: String
var weapon: Weapon?
init(name: String) {
self.name = name
}
}
class Weapon {
var ammo = 500
var grenadeLauncher: GrenadeLauncher?
}
class GrenadeLauncher {
var ammo = 20
}
EDIT
Thank you, I was getting confused about how this works, but I now see what is happening. Here is the modified eachSoldier section again, using optional binding with optional chaining...
for eachSoldier in missionTeam {
if let weapon = eachSoldier.weapon? {
if let launcher = eachSoldier.weapon?.grenadeLauncher? {
println("SOLDIER: \(eachSoldier.name) Weapon has launcher with \(launcher.ammo) ammo")
} else {
println("SOLDIER: \(eachSoldier.name) Weapon does not have launcher ")
}
} else {
println("SOLDIER: \(eachSoldier.name) does not have weapon ")
}
}
soldierC.weapon = Weapon()
soldierC.weapon!.grenadeLauncher = GrenadeLauncher()
soldierC.weapon!.grenadeLauncher!.ammo = 234
it is correct in the current pattern.
var ammo = eachSoldier.weapon!.grenadeLauncher!.ammo
implicitly unwraps the weapon and its grenadeLauncher; it does not care of whether or not they have been inited before, therefore it could lead a direct crash if your code tries to unwrap when any of them is still a nil value.
var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo
tries to access the weapon and its grenadeLauncher; if the object does not exist, they will be left alone, therefore nothing happens but the ammo2 will be nil only, and application can proceed.
therefore your flow could be similar to that:
for eachSoldier in missionTeam {
var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo
if ammo2 != nil {
println("SOLDIER: \(eachSoldier.name), Weapon has launcher AMMO: \(ammo2)")
} else {
println("SOLDIER: \(eachSoldier.name), Weapon does not have launcher ")
}
}
In addition to what #holex has stated, I would like to say that your case called Optional Chaining, in which if you use ? instead of ! on an optional variable (or constant), that means you are checking if the variable (or the constant) is not nil. In other words, it has a value.
The lovely thing about optional chaining is that you can apply it to many levels.
For example:
Let's say you have these two classes:
class Student{
var subjects: [Subject]?
}
class Subject{
var name: String?
}
and you created a variable:
var william = Student()
At any time, you can print the name of the first subject as this:
print(william.subjects?[0].name)
Notice that the result of that print statement is nil, while if you unwrapped it like this:
print(william.subjects![0].name)
You would get a run time error