swift first simple project - cannot call object method - ios

I read a few tutorial about swift, and have made simple counter app, but I want modify it to leave controller clean and logic move to external class:
In ViewController.swift i have
class ViewController: UIViewController {
var counter: Counter?
override func viewDidLoad() {
super.viewDidLoad()
counter = Counter(label: labelCounter)
counter.renderInit()
}
}
and I have Counter class:
class Counter: NSObject {
var label: UILabel?
init(label: UILabel) {
self.label = label
}
func renderInit() {
...
}
}
Unfortunatelly in controller on line counter.renderInit() I see error message:
'Counter?' does not have a member named 'renderInit'

Change counter.renderInit() to one of these:
counter?.renderInit()
counter!.renderInit()
Counter? is an optional type. You need to unwrap it. Doing ? will ignore the method if counter is nil, and ! will force it to unwrap and throw an error if it does not exist.
Check out this page in Swift's documentation for more on optionals.

ok, I move step forward and have another problem with call method object.
class Counter: NSObject {
var label: UILabel?
init(label: UILabel) {
self.label = label
}
func renderInit() {
label?.text = String(counter)
}
}
I got "Cannot assign to the result of this expression." (for line where I want assign number converted to string, to label text.
I read documentation and couple of tutorials, but with learning new thing it is good to look on example and to it in similar way. Unfortunatelly Swift is young language and there are not many examples. (I am Ruby language programmer).

Related

XCode8 / Swift will not let me set a textField's text equal to a String?

I am very new to iOS application programming (started learning just under a week ago!) so I'm sure what I'm asking is very obvious!
So I am working on an Application for my work study position and cannot for the life of me figure out what I'm doing wrong. Basically, all I want to do is check to make sure each field is filled in before allowing the user to carry on. I did a similar thing in another view controller to check to see if what was entered is equal to the password I set for the program (I am sure there is a better way to do this, but because I'm self learning and it's my first application, I'm not worried too much. I have a whole year to polish it!)
Here is the code for that (if you have suggestions that may improve it, feel free, but for the most part, I'm just trying to figure out why this worked and the other code is not)
#IBAction func enterPassword(_ sender: Any) {
pw = password.text!
let message = "Incorrect Password"
if(pw == "RISE")
{
performSegue(withIdentifier: "ToMenu", sender: self)
}
else {
incorrect.text = message
}
}
please note that the variable "pw" is a global variable and is declared outside of the class. Password and incorrect are both labels previously declared as well.
Here is the code that is in question. I do not understand why I am getting an error for this. it is "thread 1: exc_bad_instruction". I'm honestly not even sure what this error means so if you happen to know what it means, please enlighten me! I included all the code for the viewController with the issue.
import UIKit
var studName = " "
class CreateViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var ID: UITextField!
#IBOutlet weak var YR: UITextField!
#IBOutlet weak var test: UILabel!
#IBAction func endCreate(_ sender: Any) {
studName = name.text!
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
What am I doing wrong here? I am really uncertain and confused. I am pretty good at coding in Java and C++ so I never expected to have issues learning but it's totally different and is taking a lot longer than I expected (It took me about a week to learn C#...).
Thanks for any help!
EDIT:
(I need 10 reputation to post more than two links, so I deleted the error. lol I guess I'll go answer some questions or something)
Click here to see StoryBoard
Click here to see Connection to NAME (note I reconnected it and used all caps, error is still an issue)
if there is any other information that may help you understand, please let me know! Thanks!
I'm assuming here that you are experiencing the crash on this line
studName = name.text!
? (If not could you please include the stack trace to indicate where you are getting an issue?)
It looks like either name is nil, or text is nil both of which are being force unwrapped here. So either you haven't connected this text field properly in interface builder, or there is just no text in it.
My general rule is (and I'm sure people will disagree with this):
NEVER use !
Such a simple rule :)
I reason this that, force unwrapping an optional variable is never (very, very rarely) required, and implies an assumption of a value that was clearly intended to be allowed to be nil (otherwise it would not be optional). Typically, if you find yourself using a ! on one of your own variables, consider refactoring the variable to not be optional, otherwise make heavy use of optional binding and/or guard statements to avoid forcing things.
(My one main caveat for this is creating instance variables that require self as a parameter for initialisation. Where the instance variable is an implicitly unwrapped optional, but is set immediately after the call to super's initialiser, and never again)
Also, while it works and is valid code, it is not typical to use global variables for things like studName, this should be an instance variable in your controller.
Do feel free to shout if you have any other questions about this (or anything iOS/swift). If you're just getting started, I can't recommend http://web.stanford.edu/class/cs193p highly enough! A thorough rundown of all core iOS technologies and design patterns.
EDIT for comment
In this case you could avoid using this ! by making your variable optional, you could declare studName using
var studName: String? = " "
this makes it an optional variable which will allow you to assign a nil value to it.
If you are sure you don't want it to be nil, you could also do your assignment as
studName = name.text ?? " "
This will check the value of name.text and if it is not nil assign to studName, otherwise it will assign the value on the right " ". It essentially allows you to provide an default value for an assignment that is not optional. It is shorthand for:
if let text = name.text {
studName = text
} else {
studName = " "
}
Great start. Here is how I would write it:
#IBAction func enterPassword(_ sender: Any) {
//Make sure we trim off any white space or new lines and then optionally bind the text in the text field.
guard let passwordText = self.password.text?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) else {
self.incorrect.text = "Please enter a password."
return
}
//Make sure text is not a blank string eg: ""
guard !passwordText.isEmpty else {
self.incorrect.text = "Please enter a password."
return
}
//Make sure it is the password we want.
guard passwordText == "ThePassword" else {
self.incorrect.text = "Not the correct password."
return
}
//Success! Do other stuff here. Like perform a segue.
}
And just for good measure and a little fun. Here is a great extension on UITextField to provide a nice little shake animation if anything is invalid.
extension UITextField {
func shake(times T: Int, distance D: CGFloat) {
UIView.animate(withDuration: 0.06, animations: {
self.transform = CGAffineTransform(translationX: D, y: 0)
}) { (didComplete) in
UIView.animate(withDuration: 0.06, animations: {
self.transform = CGAffineTransform.identity
}, completion: { (didComplete) in
guard T > 0 else {
return
}
self.shake(times: T - 1, distance: D)
})
}
}
}
George Green is right. Force unwrapping in Swift is considered a code smell. Always optionally bind an optional value. Makes for a little more verbose code but keeps everything safe.
There you go... here I See the problem. I removed Outlet to the TextField and immediately I saw that error.
Please make sure you connect the IBOutlet properly and it works perfectly as shown below.
Hope this helps you.
Firstly,
Take an Outlet for the TextField as shown below
// MARK:- IBOutlets
#IBOutlet var passwordTextField: UITextField!`
Take a Button & assign IBAction for the the button, "passwordCheck" in this case.
You can compare TextField text to string as shown below
// MARK:- IBActions
#IBAction func passwordCheck(_ sender: Any) {
if passwordTextField.text == "RISE" {
print("perform Segue")
} else {
print("Do nothing")
}
}
Let me know if you have any doubts...

How to keep the reference to an array after manipulation?

How do I keep the reference to an array after an items is appended?
Updated code example, because the prior example didn't seem to be clear enough.
class ViewController: UIViewController {
var numbers = Numbers.singleton.numbers
override func viewDidLoad() {
print(numbers.count)
Numbers.singleton.add(1)
print(numbers.count) // prints 0
print(Numbers.singleton.numbers.count) // prints 1
}
}
class Numbers {
static let singleton = Numbers()
var numbers: [Int]!
private init() {
numbers = []
}
func add(number: Int) {
numbers.append(number)
}
}
Arrays in Swift don't have "references". They are structs, and a struct is a value type. Your (badly named) arrayRef is a separate copy, not a reference to self.array.
Moreover, there is no good reason to want to do what you (seem to) want to do. To have two simultaneous references to a mutable array would be unsafe, since the array can be changed behind your back. The Swift design is sensible; use it, don't subvert it.

What if I want to assign a property to itself?

If I attempt to run the following code:
photographer = photographer
I get the error:
Assigning a property to itself.
I want to assign the property to itself to force the photographer didSet block to run.
Here's a real-life example: In the "16. Segues and Text Fields" lecture of the Winter 2013 Stanford iOS course (13:20), the professor recommends writing code similar to the following:
#IBOutlet weak var photographerLabel: UILabel!
var photographer: Photographer? {
didSet {
self.title = photographer.name
if isViewLoaded() { reload() }
}
}
override func viewDidLoad() {
super.viewDidLoad()
reload()
}
func reload() {
photographerLabel.text = photographer.name
}
Note: I made the following changes: (1) the code was switched from Objective-C to Swift; (2) because it's in Swift, I use the didSet block of the property instead of the setPhotographer: method; (3) instead of self.view.window I am using isViewLoaded because the former erroneously forces the view to load upon access of the view property; (4) the reload() method (only) updates a label for simplicity purposes, and because it resembles my code more closely; (5) the photographer IBOutlet label was added to support this simpler code; (6) since I'm using Swift, the isViewLoaded() check no longer exists simply for performance reasons, it is now required to prevent a crash, since the IBOutlet is defined as UILabel! and not UILabel? so attempting to access it before the view is loaded will crash the application; this wasn't mandatory in Objective-C since it uses the null object pattern.
The reason we call reload twice is because we don't know if the property will be set before or after the view is created. For example, the user might first set the property, then present the view controller, or they might present the view controller, and then update the property.
I like how this property is agnostic as to when the view is loaded (it's best not to make any assumptions about view loading time), so I want to use this same pattern (only slightly modified) in my own code:
#IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
photographerLabel?.text = photographer.name
}
}
override func viewDidLoad() {
super.viewDidLoad()
photographer = photographer
}
Here instead of creating a new method to be called from two places, I just want the code in the didSet block. I want viewDidLoad to force the didSet to be called, so I assign the property to itself. Swift doesn't allow me to do that, though. How can I force the didSet to be called?
Prior to Swift 3.1 you could assign the property name to itself with:
name = (name)
but this now gives the same error: "assigning a property to itself".
There are many other ways to work around this including introducing a temporary variable:
let temp = name
name = temp
This is just too fun not to be shared. I'm sure the community can come up with many more ways to do this, the crazier the better
class Test: NSObject {
var name: String? {
didSet {
print("It was set")
}
}
func testit() {
// name = (name) // No longer works with Swift 3.1 (bug SR-4464)
// (name) = name // No longer works with Swift 3.1
// (name) = (name) // No longer works with Swift 3.1
(name = name)
name = [name][0]
name = [name].last!
name = [name].first!
name = [1:name][1]!
name = name ?? nil
name = nil ?? name
name = name ?? name
name = {name}()
name = Optional(name)!
name = ImplicitlyUnwrappedOptional(name)
name = true ? name : name
name = false ? name : name
let temp = name; name = temp
name = name as Any as? String
name = (name,0).0
name = (0,name).1
setValue(name, forKey: "name") // requires class derive from NSObject
name = Unmanaged.passUnretained(self).takeUnretainedValue().name
name = unsafeBitCast(name, to: type(of: name))
name = unsafeDowncast(self, to: type(of: self)).name
perform(#selector(setter:name), with: name) // requires class derive from NSObject
name = (self as Test).name
unsafeBitCast(dlsym(dlopen("/usr/lib/libobjc.A.dylib",RTLD_NOW),"objc_msgSend"),to:(#convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
unsafeBitCast(class_getMethodImplementation(type(of: self), #selector(setter:name)), to:(#convention(c)(Any?,Selector!,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
unsafeBitCast(method(for: #selector(setter:name)),to:(#convention(c)(Any?,Selector,Any?)->Void).self)(self,#selector(setter:name),name) // requires class derive from NSObject
_ = UnsafeMutablePointer(&name)
_ = UnsafeMutableRawPointer(&name)
_ = UnsafeMutableBufferPointer(start: &name, count: 1)
withUnsafePointer(to: &name) { name = $0.pointee }
//Using NSInvocation, requires class derive from NSObject
let invocation : NSObject = unsafeBitCast(method_getImplementation(class_getClassMethod(NSClassFromString("NSInvocation"), NSSelectorFromString("invocationWithMethodSignature:"))),to:(#convention(c)(AnyClass?,Selector,Any?)->Any).self)(NSClassFromString("NSInvocation"),NSSelectorFromString("invocationWithMethodSignature:"),unsafeBitCast(method(for: NSSelectorFromString("methodSignatureForSelector:"))!,to:(#convention(c)(Any?,Selector,Selector)->Any).self)(self,NSSelectorFromString("methodSignatureForSelector:"),#selector(setter:name))) as! NSObject
unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setSelector:")),to:(#convention(c)(Any,Selector,Selector)->Void).self)(invocation,NSSelectorFromString("setSelector:"),#selector(setter:name))
var localVarName = name
withUnsafePointer(to: &localVarName) { unsafeBitCast(class_getMethodImplementation(NSClassFromString("NSInvocation"), NSSelectorFromString("setArgument:atIndex:")),to:(#convention(c)(Any,Selector,OpaquePointer,NSInteger)->Void).self)(invocation,NSSelectorFromString("setArgument:atIndex:"), OpaquePointer($0),2) }
invocation.perform(NSSelectorFromString("invokeWithTarget:"), with: self)
}
}
let test = Test()
test.testit()
There are some good workarounds but there is little point in doing that.
If a programmer (future maintainer of the code) sees code like this:
a = a
They will remove it.
Such a statement (or a workaround) should never appear in your code.
If your property looks like this:
var a: Int {
didSet {
// code
}
}
then it's a not a good idea to invoke the didSet handler by assignment a = a.
What if a future maintainer adds a performance improvement to the didSet like this?
var a: Int {
didSet {
guard a != oldValue else {
return
}
// code
}
}
The real solution is to refactor:
var a: Int {
didSet {
self.updateA()
}
}
fileprivate func updateA() {
// the original code
}
And instead of a = a directly call updateA().
If we are speaking about outlets, a suitable solution is to force the loading of views before assigning for the first time:
#IBOutlet weak var photographerLabel: UILabel?
var photographer: Photographer? {
didSet {
_ = self.view // or self.loadViewIfNeeded() on iOS >= 9
photographerLabel?.text = photographer.name // we can use ! here, it makes no difference
}
}
That will make the code in viewDidLoad unnecessary.
Now you might be asking "why should I load the view if I don't need it yet? I want only to store my variables here for future use". If that's what you are asking, it means you are using a view controller as your model class, just to store data. That's an architecture problem by itself. If you don't want to use a controller, don't even instantiate it. Use a model class to store your data.
I hope one day #Swift developers will fix this miscuzzi :)
Simple crutch:
func itself<T>(_ value: T) -> T {
return value
}
Use:
// refresh
style = itself(style)
image = itself(image)
text = itself(text)
(optionals including)
Make a function that the didSet calls then call that function when you want to update something? Seems like this would guard against developers going WTF? in future
#vacawama did a great job with all those options. However in iOS 10.3, Apple banned some of these ways and most likely will be doing it in the future again.
Note: To avoid the risk and future errors, I will use a temporary variable.
We can create a simple function for that:
func callSet<T>(_ object: inout T) {
let temporaryObject = object
object = temporaryObject
}
Would be used like: callSet(&foo)
Or even a unary operator, if there is a fitting one ...
prefix operator +=
prefix func +=<T>(_ object: inout T) {
let temporaryObject = object
object = temporaryObject
}
Would be used like: +=foo

access property from another class

I have a custom swift class like this
class NichedHelper: NSObject {
private var _theController:UIViewController? = nil
var theController:UIViewController? {
get {
return self._theController
}
set {
self._theController = newValue
}
}...
it has an implementation function like this and _theController passing a Lobb class that inherit UIViewController
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController!
abil.bottomConst.constant = -80
}
it throw error 'AnyObject' does not have a member named 'bottomConst'.
since i don't know what the english word for this kind of technique, so that will be my first question.
my second question, is it possible if i am sure Lobb class (or other class) have a variable called bottomConst, how can i access it from class NichedHelper?
you have declared the _theController as private , remove that just declare as
var _theController:UIViewController!
// this is how we roll in swift ;) bye bye Objective-C
I don't know exactly what you are trying to do and why you have two UIViewController instances. So I'm not able to answer your first question but regarding your second one, you have to cast the object to a UIViewController object:
func DoPump(from: String, theBoard: CGRect, overide: Bool) {
let abil:AnyObject = _theController as! UIViewController
abil.bottomConst.constant = -80
}
This at least should make the compiling error away, if you have the bottomConst attribute declared as a variable of UIViewControllers in an extension (since they do not have this variable normally.
Well, i change from passing the UIViewController to NSLayoutConstraint

Swift: permanent variable change through global files

I am working in between three files: Menu.swift, Main.swift and Game.swift.
In my Main.swift, I define the variable swipeNumber:
class Main {
var swipeNumber: Int = 0 {
didSet{
println("The new swipe number is \(swipeNumber)")
}
}
}
N.B. It is in a class so that I can reference the variable from other files, and the didSet property observer will function.
As you can see, its initial value (I think) is 0.
Then, in my Menu.swift, I retrieve the information from the Main class in Main.swift.
let main = Main()
I then have three buttons, which will, on touch, change the swipeNumber variable, based on which button was pressed.
class Menu: UIViewController {
#IBAction func pressedThreeSwipes(sender: AnyObject) {
main.swipeNumber = 3
}
#IBAction func pressedFiveSwipes(sender: AnyObject) {
main.swipeNumber = 5
}
#IBAction func pressedTenSwipes(sender: AnyObject) {
main.swipeNumber = 10
}
//...
}
When I run the program, my property observer appears to work, printing messages such as:
The new swipe number is 3
The new swipe number is 5
The new swipe number is 10
And in the Game class, (for troubleshooting purposes), I have another property observer, checking the integer of the variable swipeNumber when the button test is pressed:
class Game: UIView {
let main = Main()
func didMoveToView(view: UIView) {
/* Setup your scene here */
println("now")
println("\(main.swipeNumber)"
//Nothing happens here, suggesting that didMoveToView is failing
}
#IBAction func test(sender: AnyObject) {
println("\(main.swipeNumber)")
}
}
My func test prints a number, but sadly that number is not 3, 5, or 10. It's 0.
I think that the problem lies with my variable in Main.swift, however I am not sure.
Any advice or 'fixes', whether quick or lengthy, would be very greatly appreciated.
Thank you,
Will
You have different instances of your class Main, and they each carry a different value for the same properties.
You should try the Singleton pattern (see e.g. here or here).
When you call Main(), you are creating a new object...emphasis on NEW. It has no knowledge of what you've done to other objects of the same type. If you want to use the same object in different places, you need to make it a parameter and pass it into methods rather than creating a different object.

Resources