How can I write a function in a separate swift file and use it (import it) to my ViewController.swift file? I have written a lot of code and all of the code is in the ViewController.swift file, I really need to make this look good and place functions on separate files, for cleaner code. I have functions dealing with parsing HTML, functions dealing with ordering results, presenting results, responding to user actions, etc. Many thanks for any help!
if let htmlString = String(contentsOfURL: checkedUrl, encoding: NSUTF8StringEncoding, error: nil) {
// Parsing HTML
let opt = CInt(HTML_PARSE_NOERROR.value | HTML_PARSE_RECOVER.value)
var err : NSError?
var parser = HTMLParser(html: htmlString, encoding: NSUTF8StringEncoding, option: opt, error: &err)
var bodyNode = parser.body
// Create an array of the part of HTML you need
if let inputNodes = bodyNode?.findChildTags("h4") { //inputNodes is an array with all the "h4" tag strings
for node in inputNodes {
let result = html2String(node.rawContents)
println("Nyheter: \(result)")
}
}
When I add that function to a separate swift file, how can I use it in my ViewDidLoad method using a "shorthand"? A short keyword that grabs that chunk of code and use it?
Easy. You just create a new Swift file into your Xcode project (File - New - File - Swift file or just ⌘-N) and put your functions and classes there. No need to import anything in your view controller file as both files are part of the same package and thus see each others functions and types (unless marked as private).
func parseHtml(url: NSURL) -> String { ... your code goes here ... }
You need to use singletons.
Create NSObject in User.swift
import Foundation
import UIKit
class User: NSObject {
var name: String = 0
func getName() -> String{
name = "Erik Lydecker"
return name
}
}
Then initialize your object and trigger the method there.
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let instanceOfUser = User()
instanceOfUser.getName() // Erik Lydecker
}
}
You can create a Utils class, filled with static variables/functions
For example:
class Utils {
static func convert(to variable: String) -> Int? {
... do some stuff to variable
return newVariable
}
}
// on your ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if let converted = Utils.convert(to: "123") {
print(converted)
}
}
By making use of static functions you can access them anywhere and everywhere to reuse them.
------------------
Another way is to make use of extensions
For example:
extension String {
var toInt: Int? {
return Int(self)
}
}
// on your ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if let converted = "SomeValue".toInt {
print(converted)
}
}
Both of these can be used in many different scenarios.
Related
I started learning Swift today and in my first test app I am getting this error:
TestClass is not convertible to AnotherClass
The following is the TestClass:
class TestClass : NSObject {
var parameter1 : String = ""
var parameter2 : String = ""
override init() {
super.init()
}
func createJob(parameter1: String, parameter2: String) -> TestClass {
self.parameter1 = parameter1
self.parameter2 = parameter2
return self;
}
}
And this is the AnotherClass:
class AnotherClass: NSObject {
private struct internalConstants {
static let test1 = "testData"
static let test2 = "testData2"
}
var current : String
override init() {
self.current = internalConstants.test1
super.init()
}
func executeTask(testClass : TestClass) {
if testClass.parameter1 == "abc" {
return;
}
}
}
And this is the ViewController where I am getting the compiler error:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let obj = TestClass()
AnotherClass.executeTask(obj)
}
}
AnotherClass.executeTask line is giving the compiler error.
The obj variable sent as a parameter on this line is highlighted by Xcode with the error
"TestClass is not convertible to AnotherClass".
In C# or Objective C it is allowed to pass custom objects as a parameter to another methods. How can I do it in Swift?
Let's correct first the TestClass. This is how you should init a class like that:
class TestClass : NSObject {
....
init(parameter1: String, parameter2: String) {
....
}
}
Much simpler. Now, going back to your problem,
"TestClass is not convertible to AnotherClass".
Take a look at it again. The line you've mentioned in your question. You are trying to do this:
let obj = TestClass()
AnotherClass.executeTask(obj)
This line, AnotherClass.executeTask(obj), is giving you an error because indeed executeTask() is an instance method. You could do three ways for that.
add static keyword to the func executeTask... So it becomes like this: static func executeTask(testClass : TestClass) {
Instead of static keyword, you could add class. It becomes like so: class func executeTask(....
OR, better if you just instantiate the AnotherClass. Make a new object of AnotherClass. How to instantiate? You tell me. But here:
let anotherClass = AnotherClass()
Either implement executeTask as a class function
class func executeTask(testClass : TestClass) {
if testClass.parameter1 == "abc" {
return;
}
}
or instantiate AnotherClass in vieweDidLoad
let obj = TestClass()
let another = AnotherClass()
another.executeTask(testClass: obj)
Note the slightly different call to executeTask with the argument name.
And there is really no reason for you to subclass NSObject as I see it.
I think it's best to keep is simple. Create an instance of AnotherClass inside of ViewController.
class ViewController: UIViewController {
// Create an instance of AnotherClass which lives with ViewController.
var anotherClass = AnotherClass()
override func viewDidLoad() {
super.viewDidLoad()
let obj = TestClass()
// Use the instance of AnotherClass to call the method.
anotherClass.executeTask(testClass: obj)
}
}
I have a problem with storing my generic classes in an array. How should I format the type for my array while keeping the reference to the original type (I know I could do var myClasses: [Any] = [] but that wouldn't be helpful when retrieving the variable from my array :(
Example is below:
import UIKit
protocol Reusable { }
extension UITableViewCell: Reusable { }
extension UICollectionViewCell: Reusable { }
class SomeClass<T> where T: Reusable {
init() { }
}
var myClasses: [SomeClass<Reusable>] = []
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
Edit: Just to clarify, I have used the collection and table cells as an example, I am not actually planning on storing them together :)
Edit 2 var myClasses: [SomeClass<Reusable>] = [] generates error: using 'Reusable' as a concrete type conforming to protocol 'Reusable' is not supported
Edit 3 var myClasses: [SomeClass<AnyObject>] = [] generates error: type 'AnyObject' does not conform to protocol 'Reusable'
I think you can create some sort of Holder class that can accept and retrieve your object:
class Holder {
lazy var classes: [Any] = []
func get<T>(_ type: T.Type) -> [T]? {
return classes.filter({ $0 is T }) as? [T]
}
}
And the main part:
let holder = Holder()
holder.classes.append(SomeClass<UITableViewCell>())
if let someTableViewCells = holder.get(SomeClass<UITableViewCell>.self)?.first {
// or filter out again to get specific SomeClass of UITableViewCell
print(someTableViewCells)
}
Or without holder class:
var classes: [Any] = []
classes.append(SomeClass<UITableViewCell>())
if let someTableViewCell = classes.filter({ $0 is SomeClass<UITableViewCell> }).first as? SomeClass<UITableViewCell> {
print(someTableViewCell)
}
You should use array of AnyObject in your case. Because as you know swift is strong typed language and for example
SomeClass<UITableViewCell>
and
SomeClass<UICollectionViewCell>
are different types of objects. As for example Array< Int > and Array< String >, they are both arrays, but still it's a different types of objects. So in this case you'll have to use declaration:
var myClasses: [AnyObject] = []
and check type of object or typecast them every time you'll need.
if (myClasses[0] is SomeClass<UICollectionViewCell>) { do something }
or
if let cell = myClasses[0] as? SomeClass<UICollectionViewCell> { do something }
My suggestion is adding parent protocol SomeClass Container for your SomeClass generic. Then put an array of SomeClass objects inside SomeClass.
protocol Reusable { func cakePlease() }
extension UITableViewCell: Reusable {
func cakePlease() { }
}
extension UICollectionViewCell: Reusable {
func cakePlease() { }
}
protocol SomeClassContainer {
func teaPlease()
func afternoonPlease()
}
class SomeClass<T: Reusable>: SomeClassContainer {
var item: T?
init() { }
func teaPlease() { }
func afternoonPlease() {
teaPlease()
item?.cakePlease()
}
}
var myClasses = [SomeClassContainer]()
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses.append(SomeClass<UITableViewCell>())
myClasses.append(SomeClass<UICollectionViewCell>())
myClasses[0].teaPlease()
if let item = (myClasses[0] as? SomeClass<UITableViewCell>)?.item {
item.cakePlease()
}
for myClass in myClasses {
if let tableCell = (myClass as? SomeClass<UITableViewCell>)?.item {
tableCell.cakePlease()
} else if let collectionCell = (myClass as SomeClass<UICollectionViewCell>)?.item {
collectionCell.cakePlease()
}
}
myClasses.forEach({ $0.afternoonPlease() })
Generally the way to type your array would be to go as specific as possible whilst still covering all bases.
What I mean by this for example is storing an array of UIViewController objects, even though each will actually be a different type. You wouldn't use Any here because you really don't need to be that general.
In your case, why not use Reusable? Since all your generic classes conform to it anyway, it is as general as you can go whilst still maintaining convenience.
I have one swift file and in which I have kept my common function that I need every time so when I am accessing that function I am not getting value
Myfirst class
import Foundation
import UIKit
class test1
{
var mydata = NSMutableArray()
//MY COMMON FUNCTION
func loadMoredata(create_at:String)->NSMutableArray
{
**//////My CODE**
//getting correct data
print(mydata)
return mydata
}
}
Mysecond Class
override func viewWillAppear(_ animated: Bool)
{
//Calling that function
// not getting data here or empty array
let dt:NSMutableArray = test1().loadMoredata(create_at: "0")
// not getting data here or empty array
print(test1().mydata)
}
You dont get anything back from your function since your mydata property is an instance property. Hence, every test1 object you create, will have its own mydata property with its own data.
If you want store global state you could make test1 a singleton class, where mydata is globally accessible.
class test1
{
static let shared = test1()
var mydata = NSMutableArray()
private init(){}
//MY COMMON FUNCTION
func loadMoredata(create_at:String)->NSMutableArray
{
**//////My CODE**
//getting correct data
print(mydata)
return mydata
}
}
Let's say that certain items can appear in a Feed, so long as they implement the necessary properties defined by the Feedable protocol. Let's also say that the Photo object is feed-worthy:
extension Photo: Feedable { }
Is it possible to say that an Array of these photos might also be Feedable?
extension [Photo] : Feedable
Or do I always need some kind of wrapper object, such as a PhotoAlbum, to conform to Feedable?
Edit
To re-iterate, I was curious whether I can make only arrays of Photo objects Feedable. Not making Array of any content type Feedable, not making an array of Feedables itself Feedable (both of which are offered as solutions below if that's what you need).
In other words, a solution (which I doubt exists) would allow me to define a variable of type Feedable with the following outcomes:
var feedable: Feedable
//photo is feedable, so this is fine
feedable = Photo() //ok
//arrays of photos are feedable
let photo1 = Photo()
let photo2 = Photo()
feedable = [photo1, photo2]
//arrays of other things are not
feedable = ["no", "dice"] //nope
//even if the contents of an array are themselves Feedable, that's not sufficient. E.g. Video is Feedable, but Array of Videos is not.
let video1 = Video()
let video2 = Video()
feeble = video1 //fine
feedable = [video1, video2] //nope
Perhaps this gist (which doesn't compile of course) shows the intention more clearly.
You can achieve your goal in this way:
Swift 4:
protocol Feedable {
func foo()
}
extension String: Feedable {
func foo() {
}
}
extension Array: Feedable where Element: Feedable {
func foo() {
}
}
// or in more generic way to support Array, Set and other collection types
extension Collection: Feedable where Element: Feedable {
func foo() {
}
}
If there was an array of Photo and Video,what would you like to be?
1.Every element performs like what they are.
extension Array where Element : Feedable {
func foo() {
if Element.self == Photo.self {
} else {
}
}
}
2.The whole array performs as 'Video'.
extension Array where Element : Photo {
func foo() {
}
}
I think this is currently not possible. In my project I have the same issue with a ModelProducer.
protocol M: ModelType {}
protocol ModelProducerType {
associatedtype M: ModelType
var model: M? { get }
func produce()
}
struct Test: ModelType {}
class TestProducer: ModelProducerType {
var model: Test?
func produce() {
model = Test()
}
}
I use ModelType as a ghost protocol. The problem is I cannot make a model producer that produces multiple ModelTypes, because of the same reason you discovered. The solution in this case was the following:
protocol M: ModelType {}
protocol ModelProducerType {
associatedtype M: ModelType
var model: [M] { get }
func produce()
}
struct Test: ModelType {}
class TestProducer: ModelProducerType {
var model: [Test] = []
func produce() {
model = [Test()]
}
}
This is more flexible from the start. I get rid of the optional variable and single model producers just have one item in the array. Maybe you can use a similar approach.
You can make an array to conform a protocol like this:
typealias PhotoArray = [Photo]
extension PhotoArray: Feedable {}
I didn't try in playground but maybe you can simply make an Array of Feedable:
var myPhotosArray = [Feedable]()
Then everything implementing the Feedable protocol would be allowed in the Array. If you want only a photo array, You can still subclass your Photo object to make a FeedablePhoto object.
Try this in Playground instead of downvoting without even testing.
Seriously 3 downvotes without any reasons and explanations...
import UIKit
protocol Tree: class {
func grow()
}
class BigTree: Tree {
internal func grow() {
print("Big tree growing")
}
}
class SmallTree: Tree {
internal func grow() {
print("Small tree growing")
}
}
class Car {
//not a tree
}
var manyTrees = [Tree]()
manyTrees.append(BigTree())
manyTrees.append(SmallTree())
manyTrees.append(Car()) //This makes an error "Car doesn't conform to expected type 'Tree'"
I am trying to use the array of strings that is named "theList" in another class in my project. I understand that the variable is declared within a method and therefore not of global scope. But how can I fix that? I have tried a few things and nothing is working. What is the best way to accomplish this?
EDIT: All I want to do is set "theList" equal to "Body"
[![enter image description here][1]][1]
I hope this clears it up a little bit, and I can select an answer. Thanks to everyone!
I think if you want to access that list outside of the function you should simply make it a variable of the class.
class TaskManager: NSObject {
//Sets up array of Tasks
var tasks = [task]()
var theList = [String]()
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
theList = tasks.map({ (task) -> String in
return task.serial
})
}
}
Your best way to do this is to look at the scope of your class and the declaration. Define your list in a place that has scope everywhere you want to use it, or make it global. That's how I do this kind of task. Sometimes add it to a higher level class declaration, and sometimes I make it global to, say, the entire program. Depends on the task and my outlook at the time of coding.
Let you function return the results of adding the task then:
class TaskManager: NSObject {
// declare your variables
func addTask(serial: String) -> [String] {
// put your realisation
}
}
or make your variable with serials available publicly:
class TaskManager: NSObject {
var tasks = [Task]()
public var serials = [String]()
func addTask(serial: String) {
// put your realisation
// save the results to serials
}
}
and then access it via the instance.
Adding theList Variable above class made it global.
Then I had to remove let from the addTask function.
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
let theList = tasks.map({ (task) -> String in
return task.serial
}).joinWithSeparator("\n")
Became
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
theList = tasks.map({ (task) -> String in
return task.serial
}).joinWithSeparator("\n")
The final code is as follows.
import UIKit
var theList : String = String()
var taskMgr: TaskManager = TaskManager()
struct task {
var serial = "Un-Named"
}
public class TaskManager: NSObject {
//Sets up array of Tasks
var tasks = [task]()
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
theList = tasks.map({ (task) -> String in
return task.serial
}).joinWithSeparator("\n")
do {
//try tasksString.writeToFile(pathToFile, atomically: true, encoding: NSUTF8StringEncoding)
print(theList)
}
}
}
I selected the answer. Thank you to all who helped cure my tired eyes.