learning swift: expressions are not allowed at the top level - swift

I'm learning Swift. I met a problem cannot be solved.
import UIKit
func helloword(str:String) {
print(str)
}
helloword("say")
I use helloword("say") but Xcode tell me the error:
expressions are not allowed at the top level

You can't simply call this method anywhere in the file. It must be called in a control flow. I mean call it inside a function.
For example, call your function from your viewDidLoad method like so :
override func viewDidLoad() {
self.helloword("say") // here self is the View Controller itself
}

Related

How to create separate data mapping file with function names in Swift

I have a macOS app that I'm creating in Swift and I have integrated an external HID device that has a number of controls on it.
The HID part is done where I am receiving all of the hid commands from the device and I am trying to create a mapping file where I can maintain the HID key mappings in a separate swift file.
All I want in that file is the data and what I want to do is this;
raw hid data is received from HID device (In ViewController)
Lookup the function name assigned to this hid data (In separate file)
Run the function that is mapped to that key. (Function located in the main ViewController)
So far I have the external swift file setup with all of the mapping and that all works fine but my issue is when I try to call the looked up function in the ViewController, it says the function can't be found in the scope.
Initially I thought I would use a delegate but the external file isn't a viewcontroller, just a separate swift file so I don't know if I can do that?.
I've tried searching but everything I've found is calling a function from another ViewController which I'm not. It's very possible I'm not using the best approach and my goal is to just keep all of the mapping in a separate file as there is a lot and it woudl be easier to maintain.
Any suggestions are appreciated.
This is one way to achieve this. It can get tedious. You can totally skip writing out a separate protocol for the delegate, but this is cleaner design.
protocol HIDMessageDelegate: AnyObject {
// example messages
func message1()
func message2()
func message3()
}
class HIDMessageParser {
static weak var delegate: HIDMessageDelegate?
static func parseHIDMessage() {
var condition = 0
// this is where your switch statement will go and you'll parse things and call the relevant delegate method
switch (condition) {
default:
delegate?.message1()
}
}
}
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
HIDMessageParser.delegate = self
}
}
extension MyViewController: HIDMessageDelegate {
func message1() {
}
func message2() {
}
func message3() {
}
}
You can simply create a UIViewController as the external file and add it as a property to the main ViewController.
In the external file add this.
#IBOutlet var uiViewController: UIViewController!
In the ViewController add this.
var externalFileViewController: UIViewController!
override func viewDidLoad() {
super.viewDidLoad()
externalFileViewController = externalFileViewController?.loadView()
// If we have an object then load it
if let viewController = externalFileViewController {
viewController.view.frame = view.frame
viewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(viewController.view)
uiViewController = viewController
}
}
Now in the viewController look up the functions to be called from the external file and call them using the function name.
The functions are defined in the external file using #IBAction.
Let me know if you have any questions.

swift unit test if a function was called

I am still learning swift and so this might be a very very basic question.
I have this function in my view controller -
override func viewDidLoad() {
super.viewDidLoad()
fetchData()
}
I am trying to test if the fetchData function is called, when the viewDidLoad is called. In Objective C, I used to be able to use OCMock and expect if that method was called.
Would anyone be able to help me with how I can do test this in swift?

Non-'#objc' method 'didFinish' does not satisfy requirement of '#objc' protocol 'YPSignatureDelegate'

We are using YPDrawSignature from here to draw a signature on the IOS App.
We are getting the following error on the UIViewController.
Non-'#objc' method 'didFinish' does not satisfy requirement of '#objc' protocol 'YPSignatureDelegate'
The UIViewController is this.
class SignatureViewController: UIViewController , UICollectionViewDelegate, UICollectionViewDataSource, YPSignatureDelegate {
func didStart(_ view : YPDrawSignatureView) {
// print("Started Drawing")
}
func didFinish(_ view : YPDrawSignatureView){
// func didFinish (){
}
}
UPDATE.
We tried the following.
#objc func didStart(_view : YPDrawSignatureView) {
// print("Started Drawing")
}
But still get the same error.
The YPDrawSignatureView has the following Delegate
#objc
public protocol YPSignatureDelegate: class {
func didStart(_ view : YPDrawSignatureView)
func didFinish(_ view : YPDrawSignatureView)
}
extension YPSignatureDelegate {
func didStart(_ view : YPDrawSignatureView) {}
func didFinish(_ view : YPDrawSignatureView) {}
}
I've downloaded YPSignatureView, and did similar to what you have. All I implemented was:
func didStart(_ view : YPDrawSignatureView) {}
func didFinish(_ view : YPDrawSignatureView) {}
and of-coarse assigning a delegate to the view, with those methods implemented by the delegate. And I don't get any errors. Make sure you haven't accidentally changed the YPSignatureView.swift file by clicking on the red button that says Fix as a suggestion. Before I implemented didStart and didFinish, I did get the same error as you have, with a button that says Fix in YPSignatureView. Clicking on that silently changes code within YPSignatureView.swift. Make sure YPSignatureView.swift is pristine and implement the above two functions and you should be fine. Re-download the file to be safe, implement those two methods as above, and thats it.
My didStart() and didFinish() functions are called when touches begins and finishes respectively. Let me know how you go.
Update: as mentioned above, you probably clicked on these two error messages here:
Do not do that. If you did then get a fresh copy of that file then just implement the protocol methods without any #objc. Hit Command + Shift + k to clean build folder and then build again. I did not get any errors after that, runs fine, and yours should too.
In your implementation, you forgot to put the space between _ and view;
Change
func didFinish(_view : YPDrawSignatureView) to
func didFinish(_ view : YPDrawSignatureView)

How to use a word at end of function signature?

I'm trying to write a delegate interface like this:
// This delegate is just a sample. It could be any delegate.
// What's important here is the third function's signature.
protocol MyViewDelegate {
func myView(_ myView: MyView, didDoSomething something: String)
func myView(_ myView: MyView, didDoAnotherThing thing: String at: Date)
func myView(_ myView: MyView, didDoYetSomethingElse)
}
However, the third function is invalid syntax. It's used to notify the delegate that some specific event happened, so the signature is important and I want to keep all function signatures consistent.
Question: What's the recommended signature for the third function?
It may not be very satisfying, but you simply can't do that. There is lots of precedent for writing these kinds of methods like this:
func myViewDidAskUserName(_ myView: MyView)
For example, a common one from Apple:
func applicationDidFinishLaunching(_ application: UIApplication)
You are trying to give the delegate a choice of two kinds of thing that happened: said something, or asked user name. That sort of choice among possibilities, in Swift, is an enum:
enum WhatHappened {
case didSaySomething(String)
case didAskUserName
}
Now write your method signature like this:
protocol MyViewDelegate {
func myView(_ myView: MyView, _ whatHappened: WhatHappened)
}
The method can be called by saying
myView(theView, .didSaySomething("hello"))
or by saying
myView(theView, .didAskUserName)

Swift Delegation

I'm having trouble wrapping my head around delegation in Swift. After reading some guides, I was able to set it up delegation between two ViewControllers, but I'm not understanding how it works. In my first view controller, I have a a label that displays what has been entered in the second view controller which contains a text field and a button (that returns to the first view controller). Here is the code for the first view controller:
#IBOutlet weak var labelText: UILabel!
func userDidEnterInformation(info: String) {
labelText.text = info;
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "transition"){
let secondVC: SecondViewController = segue.destinationViewController as! SecondViewController;
secondVC.delegate = self;
}
}
Here's the code for the second view controller:
protocol DataEnteredDelegate{
func userDidEnterInformation(info: String);
}
#IBOutlet weak var userText: UITextField!
var delegate: DataEnteredDelegate? = nil;
#IBAction func buttonPressed(sender: AnyObject) {
let information = userText.text!;
delegate!.userDidEnterInformation(information);
self.navigationController?.popToRootViewControllerAnimated(true);
}
My understanding is that in the text inside the text field gets stored in the information constant, then the userDidEnterInformation method from the protocol is called, with the method being defined inside the first view controller. This method then changes the label inside the first view controller. The thing is, I'm not sure what is happening in the prepareForSegue function. Specifically, I'm not sure what's the purpose of secondVC.delegate = self.
I would appreciate any sort of clarity on delegation.
The diagram is simple but can help you understand what's going on.
FirstViewController must conform to the DataEnteredDelegate protocol you have defined (see Sumit's answer). When using secondVC.delegate = self, you are saying that for the segue transition with the destination being a SecondViewController, the attribute delegate of that SecondViewController instance will be set to this instance of FirstViewController, thus delegating things from SecondViewController to your FirstViewController as made possible by the DataEnteredDelegate protocol.
The protocol you created in second viewcontroller is an Interface. You must implement your first view controller with the DataEnteredDelegate protocol.
class FirstViewController:UIViewController, DataEnteredDelegate{
func userDidEnterInformation(info: String) {
//stub
}
}
If the delegate of the second VC is not set in prepareForSegue() it remains nil. The second VC is then unable to call the first VC.
On a side note, if the delegate is nil your code will crash because delegate! is trying to unwrap an optional binding with the value of nil. It's better to first unwrap the delegate variable:
if let handler = delegate {
handler.userDidEnterInformation(information)
}
Alternatively, you could use Swift's Optional Chaining, calling userDidEnterInformation only if delegate is not nil.
delegate?.userDidEnterInformation(information);
In addition it is recommended to declare the delegate weak, to prevent retain cycles:
weak var delegate: DataEnteredDelegate?
Delegates and Protocols
Do not try to figure out how the dictionary definition of “delegate” fits with the concept of delegation in Swift. It doesn't.
Delegation in Swift is an agreement between two players—a sensing object and a requesting object. The “delegate” is the “requesting object.” Just think “asker” or “requester” every time you see “delegate” and it will make a lot more sense. Here is their agreement...
The Sensing Object (Second View Controller):
I have data from some event that took place. I will publish instructions (a protocol) on how you may access that data. If you want it, you must do three things.
You must declare in your class type that your class abides by my protocol.
You must write the functions that I describe in my protocol. I don't care what those functions do but the function type must match what I publish.
In YOUR code, you must set MY “delegate” (think “asker”) property to point to you. {secondVC.delegate = self} That way I can call one of YOUR functions to deliver the data.
After that, when I get some data, I will call one of the functions in your object that I told you to write. My call to your function will contain the data you are looking for as one of the arguments. {delegate!.userDidEnterInformation(information)} Note: delegate! (asker!) is YOU.
The Delegate (Requesting) Object (First View Controller):
O.K. You've got a deal.