swift: Calling a function in another file - swift

I would like to call functions residing in other files. In my given example I received no syntax errors, and the app runs. When I click the IBAction, the App crashes. I have no idea why. I am using Xcode 6.4 and coding for OS X
please advise.
// FileUtilties.swift
import Foundation
import Cocoa
class FileUtilities: NSObject, NSAlertDelegate {
var fileList : String!
func listFilesFromDocumentsFolder() -> [String]
{
var theError = NSErrorPointer()
let dirs = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true) as? [String]
if dirs != nil {
let dir = dirs![0]
let fileList = NSFileManager.defaultManager().contentsOfDirectoryAtPath(dir, error: theError)
return fileList as! [String] // edit: added ! for Swift 1.2 compatibitily
}
else{
let fileList : String!
}
return [fileList]
}
// Alert popup Window
func IdialogOKCancel(myQuestion: String, myText: String) -> Bool {
let myPopup: NSAlert = NSAlert()
myPopup.messageText = myQuestion
myPopup.informativeText = myText
myPopup.alertStyle = NSAlertStyle.WarningAlertStyle
myPopup.addButtonWithTitle("OK")
myPopup.addButtonWithTitle("Cancel")
let res = myPopup.runModal()
if res == NSAlertFirstButtonReturn {
return true
}
return false
}
}
// ViewController.swift
import Cocoa
class ViewController: NSViewController, NSTableViewDataSource, NSTableViewDelegate {
// setsup outlet to add data to ListViewer
#IBOutlet weak var ListViewer: NSScrollView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func AddButtonClicked(sender: AnyObject) {
var rtnText: String = "this is the text "
var rtn: Bool = false
let instance = FileUtilities()
rtn = instance.IdialogOKCancel("Ok?", myText: rtnText)
}
}

Related

How to import or access UITextField from another Class

I have Outlet of UITextField in the main VC and I want to access its text from another class... for more clarification please see my code below:
class ProductManagement : UIViewController, UITextFieldDelegate{
#IBOutlet weak var ProductName: UITextField!
}
and I want to read the text in below class
import Firebase
import FirebaseStorage
class XUpload {
static func UploadImage(Image : UIImage, Completion : #escaping (_ url : String)->()) {
guard let imageData = Image.pngData() else { return }
let storage = Storage.storage().reference()
let pathRef = storage.child("Images/Products")
let imageRef = pathRef.child( // I want the product name to be written here! )
imageRef.putData(imageData, metadata: nil) { (meta, error) in
imageRef.downloadURL { (url, error) in
print (url as Any)
}
}
}
}
I tried to create a custom protocol to delegate the UITextField but the problem is I couldn't conform it inside XUpload class !!
Please someone write for me how is my code should be because I'm beginner and new to Swift language.
Thank you in advance.
EDIT:
Elia Answer applied below:
ProductManagement Class + Extension
class ProductManagement : UIViewController, UITextFieldDelegate{
override func viewDidLoad() {
super.viewDidLoad()
self.ProductName.delegate = self
}
#IBOutlet weak var ProductName: UITextField!
#objc private func textFieldDidChange(_ sender: UITextField) {
NotificationCenter.default.post(name: .PnameInputText, object: nil, userInfo: ["text": sender.text!])
}
}
extension Notification.Name {
public static var PnameInputText = Notification.Name("PnameInputText")
}
XUpload Class
import Firebase
import FirebaseStorage
class XUpload {
private init() {
NotificationCenter.default.addObserver(self, selector: #selector(handle(_:)), name: .PnameInputText, object: nil)
}
public static var shared = XUpload()
var textFieldText : String = "" {
didSet {print("updated value > ", textFieldText)}
}
#objc private func handle(_ sender: Notification) {
if let userInfo = sender.userInfo as NSDictionary?, let text = userInfo["text"] as? String {
textFieldText = text
}
}
static func UploadImage(Image : UIImage, Completion : #escaping (_ url : String)->()) {
guard let imageData = Image.pngData() else { return }
let storage = Storage.storage().reference()
let pathRef = storage.child("Images/Products")
let imageRef = pathRef.child("img_"+shared.textFieldText)
print("var of textFieldText value: " + shared.textFieldText) //print nothing... empty value!!
imageRef.putData(imageData, metadata: nil) { (meta, error) in
imageRef.downloadURL { (url, error) in
print ("Image uploaded to this link >> " + url!.description)
guard let str = url?.absoluteString else { return }
Completion (str)
}
}
}
}
extension UIImage {
func upload(completion : #escaping (_ url : String) ->()) {
XUpload.UploadImage(Image: self) { (ImageURL) in completion (ImageURL)
}
}
}
As you can see there is no value given to var textFieldText by the Notification Center! why??
class ProductManagement : UIViewController, UITextFieldDelegate{
var productInputText: String = ""
#IBOutlet weak var ProductName: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
ProductName.addTarget(self, action: #selector(textFieldDidChange), for: .editingChanged)
}
#objc private func textFieldDidChange(_ sender: UITextField) {
productInputText = sender.text ?? ""
}
}
This will store your input text into a variable basically with listen edit changes.
And if I get not wrong, you use UploadImage within ProductManagement class. Add a function property to UploadImage as below
class XUpload {
static func UploadImage(Image : UIImage, productName: String, Completion : #escaping (_ url : String)->()) {
guard let imageData = Image.pngData() else { return }
let storage = Storage.storage().reference()
let pathRef = storage.child("Images/Products")
let imageRef = pathRef.child( // I want the product name to be written here! )
imageRef.putData(imageData, metadata: nil) { (meta, error) in
imageRef.downloadURL { (url, error) in
print (url as Any)
}
}
}
}
and call it in ProductManagement class like below.
XUpload.UploadImage(image: myImage, productName: productInputText, Completion: {
}
EDIT:
After getting your comment, I decided the best way do it is using Notification, you use UITextField in ProductManagement class so there is no need to handle delegate method of them in XUpload
Describe notification name
extension Notification.Name {
public static var TextChange = Notification.Name("TextChange")
}
In ProductManagement, textDidChange method post text to Notification
#objc private func textFieldDidChange(_ sender: UITextField) {
NotificationCenter.default.post(name: .TextChange, object: nil, userInfo: ["text": sender.text])
}
I convert your XUpload class to a Singleton class.
class XUpload {
private init() {
NotificationCenter.default.addObserver(self, selector: #selector(handle(_:)), name: .TextChange, object: nil)
}
public static var Shared = XUpload()
#objc private func handle(_ sender: Notification) {
if let userInfo = sender.userInfo as NSDictionary?, let text = userInfo["text"] as? String {
textFieldText = text
}
}
var textFieldText: String = "" {
didSet {
print("updated value > " , textFieldText)
}
}
static func uploadImage() {
// use updated textfield text with textFieldText
}
}
Then store a variable in ProductManagement class as a singleton object and it wil work for you. The text in textfield updated every changes in XUpload class.
class ProductManagement : UIViewController, UITextFieldDelegate{
var staticVariable = XUpload.Shared
}

Call a func from another class in swift

I would like to call a function which is coded on another class.
So far I have made a struct on the file structs.swift for my data:
struct defValues {
let defCityName: String
let loadImages: Bool
init(defCity: String, loadImgs: Bool){
self.defCityName = defCity
self.loadImages = loadImgs
}
}
I have made the file Defaults.swift containing:
import Foundation
class DefaultsSet {
let cityKey: String = "default_city"
let loadKey: String = "load_imgs"
func read() -> defValues {
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey(cityKey){
print(name)
let valuesToReturn = defValues(defCity: name, loadImgs: true)
return valuesToReturn
}
else {
let valuesToReturn = defValues(defCity: "No default city set", loadImgs: true)
return valuesToReturn
}
}
func write(city: String, load: Bool){
let def = NSUserDefaults.standardUserDefaults()
def.setObject(city, forKey: cityKey)
def.setBool(load, forKey: loadKey)
}
}
in which I have the two functions read, write to read and write data with NSUsersDefault respectively.
On my main ViewController I can read data with:
let loadeddata: defValues = DefaultsSet().read()
if loadeddata.defCityName == "No default city set" {
defaultCity = "London"
}
else {
defaultCity = loadeddata.defCityName
defaultLoad = loadeddata.loadImages
}
But when I try to write data it gives me error. I use this code:
#IBOutlet var settingsTable: UITableView!
#IBOutlet var defaultCityName: UITextField!
#IBOutlet var loadImgs: UISwitch!
var switchState: Bool = true
#IBAction func switchChanged(sender: UISwitch) {
if sender.on{
switchState = true
print(switchState)
}else {
switchState = false
print(switchState)
}
}
#IBAction func saveSettings(sender: UIButton) {
DefaultsSet.write(defaultCityName.text, switchState)
}
You need an instance of the DefaultsSet class
In the view controller add this line on the class level
var setOfDefaults = DefaultsSet()
Then read
let loadeddata = setOfDefaults.read()
and write
setOfDefaults.write(defaultCityName.text, switchState)
The variable name setOfDefaults is on purpose to see the difference.
Or make the functions class functions and the variables static variables and call the functions on the class (without parentheses)
From the code you posted, it seems you either need to make the write method a class method (just prefix it with class) or you need to call it on an instance of DefaultsSet: DefaultsSet().write(defaultCityName.text, switchState).
Another issue I found is that you also need to unwrapp the value of the textField. Your write method takes as parameters a String and a Bool, but the value of defaultCityName.text is an optional, so String?. This results in a compiler error.
You can try something like this:
#IBAction func saveSettings(sender: UIButton) {
guard let text = defaultCityName.text else {
// the text is empty - nothing to save
return
}
DefaultsSet.write(text, switchState)
}
This code should now compile and let you call your method.
Let me know if it helped you solve the problem

I want to observe when a value has been changed, and to load the view

//Patient class
import Foundation
struct Patients {
var family: NSArray
var given: NSArray
var id: String
var birthdate:String
var gender: String
}
struct Address {
var city: String
var country: String
var line: NSArray
}
class Patient {
var flag = 0
var address = Address(city: "", country: "", line: [""])
var patient_info = Patients(family: [""], given: [""], id: "", birthdate: "", gender: "")
var response : AnyObject?
init(response: AnyObject) {
self.response = response
if let entry = response.objectForKey("entry") {
//MARK: Address
if let resource = entry[0].objectForKey("resource") {
if let add = resource.objectForKey("address") {
address.city = add[0].objectForKey("city")! as! String
address.country = add[0].objectForKey("country")! as! String
address.line = add[0].objectForKey("line")! as! NSArray
//MARK: patient
patient_info.birthdate = resource.objectForKey("birthDate")! as! String
patient_info.gender = resource.objectForKey("gender")! as! String
if let name = resource.objectForKey("name") {
patient_info.family = name[0].objectForKey("family")! as! NSArray
patient_info.given = name[0].objectForKey("given")! as! NSArray
}
}
}
//MARK: id
if let link = entry[0].objectForKey("link") {
if let url = link[0].objectForKey("url") {
let id = url.componentsSeparatedByString("/")
patient_info.id = id[id.count-1]
}
}
}
print(patient_info)
}
}
//ViewController class
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
var viewModel = ViewModel()
#IBOutlet weak var family_name: UITextField!
#IBOutlet weak var given_name: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
family_name.delegate = self
given_name.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
switch textField {
case family_name:
family_name.resignFirstResponder()
given_name.becomeFirstResponder()
case given_name:
given_name .resignFirstResponder()
default:
print("")
}
return true
}
#IBAction func search(sender: UIButton) {
let family_name1 = family_name.text!
let given_name1 = given_name.text!
viewModel .searchForPatient(family_name1, given_name: given_name1)
//When the name property from my patient class changed I can call the //below method. How to implement the observer?
performSegueWithIdentifier("showSegue", sender:sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender:AnyObject?){
if segue.identifier == "showPatientSegue" {
if let displayViewController = segue.destinationViewController as? DisplayViewController {
displayViewController.viewModelDisplay.patient = viewModel.patient
}
}
}
}
// ViewModel where I make the request.
import Foundation
import Alamofire
import SystemConfiguration
class ViewModel {
var patient = Patient!()
func searchForPatient(family_name: String, given_name : String) {
let header = ["Accept" : "application/json"]
Alamofire.request(.GET, "https://open-ic.epic.com/FHIR/api/FHIR/DSTU2/Patient?family=\(family_name)&given=\(given_name)", headers: header).responseJSON { response in
self.patient = Patient(response: response.result.value!)
}
}
func checkInternetConnection() -> Bool {
var zeroAddress = sockaddr_in()
zeroAddress.sin_len = UInt8(sizeofValue(zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(&zeroAddress) {
SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
}
var flags = SCNetworkReachabilityFlags()
if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) {
return false
}
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
return (isReachable && !needsConnection)
}
}
The problem is that the view loads fester than the request and I need to observe when a property has been changed in my Patient class, so the view can be loaded. If the view loads faster than the request I can't display the Patient information which I need.
You have lots options:
Store a delegate (weak!) object to the ViewController so that when your patient finishes, you can load the view. In the meantime, display something sensible in the view instead.
Send an NSNotification out, which the ViewController is a listener for.
KVO (Explanation of it here, just search for 'key-value observing'), which would require your Patient object to extend NSObject so that you can leverage objc's KVO.
Hope that helps!
You can add an observer on your variable this way :
var yourVariable:String!{
didSet{
refreshView()
}
}

Changing the view color when comparing values

I created a view to use as background and I would like to change its color when label text is greater or less than variable number. The script is okay but the color is not changing.
Thanks in advance.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var localName: UITextField!
#IBOutlet weak var localNameLabel: UILabel!
#IBOutlet weak var localTemp: UILabel!
#IBAction func getData(sender: AnyObject) {
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=" + localName.text! + "")
}
#IBOutlet weak var fundo: UIView!
override func viewDidLoad() {
super.viewDidLoad()
getWeatherData("http://api.openweathermap.org/data/2.5/weather?q=London")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func getWeatherData(urlString: String){
let url = NSURL (string: urlString)
let task = NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in
dispatch_async(dispatch_get_main_queue(), {
self.setLabels(data!)
})
}
task.resume()
}
func setLabels(weatherData: NSData) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(weatherData, options:NSJSONReadingOptions.MutableContainers) as! NSDictionary
print(json)
//localNameLabel.text = json[("name")] as? String
if let name = json[("name")] as? String {
localNameLabel.text = name
}
if let main = json[("main")] as? NSDictionary {
if let temp = main[("temp")] as? Double {
//convert kelvin to celsius
let ft = (temp - 273.15)
let myString = ft.description
localTemp.text = myString
self.changeColor()
}
}
} catch let error as NSError {
print(error)
}
var number : Float
func changeColor(){
number = 19.0
if(Float(localTemp.text!) < number){
fundo.backgroundColor = .blueColor()
}else{
fundo.backgroundColor = .orangeColor()
}
}
}
}
Edited to post the entire script
In your view controller you need to add UITextFieldDelegate which will allow you to access methods related to your text field. The top of your view controller should look like this:
class ViewController: UIViewController,UITextFieldDelegate //set delegate to class
You then need to set the delegate of your text field to self in viewDidLoad and add a target for when the text field changes:
override func viewDidLoad() {
super.viewDidLoad()
localTemp.delegate = self //set delegate to this vc
localTemp.addTarget(self, action: "textFieldDidChange:", forControlEvents: UIControlEvents.EditingChanged)
}
You can then implement this method which will run on every key press and you need to call your changeColor() method as above:
func textFieldDidChange(textField: UITextField) {
self.changeColor()
}

Swift. Why singleton lost data?

Why my data is lost in singleton? Please take a look:
class YtDataManager {
static var shared_instance = YtDataManager()
let apiKey = /*...*/
let /*...*/
var channelData = [NSObject:AnyObject]()
var videosArray = [[NSObject:AnyObject]]()
var playlistId: String { return self.channelData["playlistId"] as! String}
var urlStringForRequestChannelDetails: String { return String("https://www.googleapis.com/youtube/v3/channels?part=contentDetails,snippet&forUsername=\(self./*ChannelName*/)&key=\(self.apiKey)") }
var urlStringForRequestChannelVideos: String { return String("https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&playlistId=\(self.playlistId)&key=\(self.apiKey)") }
func fn1() { /*...*/ }
func fn2() { /*...*/ }
// class definition continue...
Look, from one function(fn1) I'm writing data to channelData:
//...
self.channelData["title"] = snippetDict["title"]
self.channelData["playlistId"] = ((firstItemDict["contentDetails"] as! [NSObject:AnyObject])["relatedPlaylists"] as! [NSObject:AnyObject])["uploads"]
//...
and so on... From second function(fn2), I'm reading data:
//...
let targetURL = NSURL(fileURLWithPath: self.urlStringForRequestChannelVideos)
//...
Hence urlStringForRequestChannelVideos is computational property it uses playlistId (look code above).
Here I was surprised about emptiness of channedData from second function(I saw it in Debug mode, also I printed it's count to stdout outside of function). Why????
class YtFeedViewController: UIViewController {
#IBOutlet weak var menuButton:UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
if self.revealViewController() != nil {
menuButton.target = self.revealViewController()
menuButton.action = "revealToggle:"
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
YtDataManager.shared_instance.fn1()
print(YtDataManager.shared_instance.channelData.count) //0
YtDataManager.shared_instance.fn2() //errorness
}
self.navigationItem.title = "YouTube feed"
// Do any additional setup after loading the view.
}