so i've created function and it does something, but i want it to do something else if the parameter type is different, for example:
func (parameter: unknownType){
if(typeof parameter == Int){
//do this
}else if(typeof parameter == String){
//do that
}
}
i've done this in javascript or other programming languages, but i don't know how to do this in swift
i've created function which takes 1 argument UITextField and centers it using constraints
now i want to center my button, but since button is not UITextField type it does not work, so is there a way i can tell function to do the same on UIButton??
Use Overload:
class Example
{
func method(a : String) -> NSString {
return a;
}
func method(a : UInt) -> NSString {
return "{\(a)}"
}
}
Example().method("Foo") // "Foo"
Example().method(123) // "{123}"
The equivalent of the Javascript code would be:
func doSomething(parameter: Any?) {
if let intValue = parameter as? Int {
// do something with the int
} else if let stringValue = parameter as? String {
// do something with the string
}
}
But be warned, this approach makes you loose the type safety which is one of most useful feature of Swift.
A better approach would be to declare a protocol that is implemented by all types that you want to allow to be passed to doSomething:
protocol MyProtocol {
func doSomething()
}
extension Int: MyProtocol {
func doSomething() {
print("I am an int")
}
}
extension String: MyProtocol {
func doSomething() {
print("I am a string")
}
}
func doSomething(parameter: MyProtocol) {
parameter.doSomething()
}
doSomething(1) // will print "I am an int"
doSomething("a") // will print "I am a string"
doSomething(14.0) // compiler error as Double does not conform to MyProtocol
It can
Sample code:
func temFunc(obj:AnyObject){
if let intValue = obj as? Int{
print(intValue)
}else if let str = obj as? String{
print(str)
}
}
You can make use of Any and downcasting:
func foo(bar: Any){
switch(bar) {
case let a as String:
/* do something with String instance 'a' */
print("The bar is a String, bar = " + a)
case let a as Int:
/* do something with Int instance 'a' */
print("The bar is an Int, bar = \(a)")
case _ : print("The bar is neither an Int nor a String, bar = \(bar)")
}
}
/* Example */
var myString = "Hello"
var myInt = 1
var myDouble = 1.5
foo(myString) // The bar is a String, bar = Hello
foo(myInt) // The bar is an Int, bar = 1
foo(myDouble) // The bar is neither an Int nor a String, bar = 1.5
Check this solution:
https://stackoverflow.com/a/25528882/256738
You can pass an object AnyObject and check the class in order to know what kind of object it is.
UPDATE
Good point #Vojtech Vrbka
Here an example:
let x : AnyObject = "abc"
switch x {
case is String: println("I'm a string")
case is Array: println("I'm an Array")
// Other cases
default: println("Unknown")
}
The answer in
How to strip special characters out of string?
is not working.
Here is what I got and it gives me an error
func removeSpecialCharsFromString(str: String) -> String {
let chars: Set<String> = Set(arrayLiteral: "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_")
return String(str.characters.filter { chars.contains($0) }) //error here at $0
}
The error at $0 says
_Element (aka Character) cannot be converted to expected argument type 'String'.
Like this:
func removeSpecialCharsFromString(text: String) -> String {
let okayChars : Set<Character> =
Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_".characters)
return String(text.characters.filter {okayChars.contains($0) })
}
And here's how to test:
let s = removeSpecialCharsFromString("père") // "pre"
SWIFT 4:
func removeSpecialCharsFromString(text: String) -> String {
let okayChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-=().!_")
return text.filter {okayChars.contains($0) }
}
More cleaner way:
extension String {
var stripped: String {
let okayChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-=().!_")
return self.filter {okayChars.contains($0) }
}
}
Use this extension like:
let myCleanString = "some.Text##$".stripped
Output: "some.Text"
I think that a cleaner solution could be this approach:
extension String {
var alphanumeric: String {
return self.components(separatedBy: CharacterSet.alphanumerics.inverted).joined().lowercased()
}
}
Try this:
someString.removeAll(where: {$0.isPunctuation})
In Swift 1.2,
let chars = Set("abcde...")
created a set containing all characters from the given string.
In Swift 2.0 this has to be done as
let chars = Set("abcde...".characters)
The reason is that a string itself does no longer conform to
SequenceType, you have to use the characters view explicitly.
With that change, your method compiles and works as expected:
func removeSpecialCharsFromString(str: String) -> String {
let chars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_".characters)
return String(str.characters.filter { chars.contains($0) })
}
let cleaned = removeSpecialCharsFromString("ab€xy")
print(cleaned) // abxy
Remark: #Kametrixom suggested to create the set only once. So if there is
performance issue with the above method you can either move the
declaration of the set outside of the function, or make it a
local static:
func removeSpecialCharsFromString(str: String) -> String {
struct Constants {
static let validChars = Set("abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLKMNOPQRSTUVWXYZ1234567890+-*=(),.:!_".characters)
}
return String(str.characters.filter { Constants.validChars.contains($0) })
}
without removing spaces between words
extension String {
var removeSpecialCharacters: String {
return self.components(separatedBy: CharacterSet.alphanumerics.inverted).filter({ !$0.isEmpty }).joined(separator: " ")
}
}
I have been trying to fix all my code since swift 2.0 update. I have a problem that seems to be the way tuples work now:
public func generate() -> AnyGenerator <(String, JSON)> {
switch self.type {
case .Array:
let array_ = object as! [AnyObject]
var generate_ = array_.generate()
var index_: Int = 0
return anyGenerator{
if let element_: AnyObject = generate_.next() {
return ("\(index_++)", JSON(element_))
} else {
return nil
}
}
case .Dictionary:
let dictionary_ = object as! [String : AnyObject]
var generate_ = dictionary_.generate()
return anyGenerator{
if let (key_: String, value_: AnyObject) = generate_.next() {
return (key_, JSON(value_))
} else {
return nil
}
}
default:
return anyGenerator{
return nil
}
}
}
Specifically the line:
if let (key_: String, value_: AnyObject) = generate_.next()
Is throwing the error: Tuple pattern element label 'key' must be '_'
I tried to make that change already, but I didnt work...
Any ideas?
The problem is: We cannot use type annotation inside of tuple patterns anymore.
In the release notes:
Type annotations are no longer allowed in patterns and are considered part of the outlying declaration. This means that code previously written as:
var (a : Int, b : Float) = foo()
needs to be written as:
var (a,b) : (Int, Float) = foo()
if an explicit type annotation is needed. The former syntax was ambiguous with tuple element labels. (20167393)
So, you can:
if let (key_, value_): (String, AnyObject) = generate_.next() {
But in this case, you could omit : (String, AnyObject):
if let (key_, value_) = generate_.next() {
I would like to create a typed map (Dictionary) class to meet the following requirements:
func testMap() {
var map = ActivitiesMap()
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
logger.debug("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
In short, I want this class to both be a dictionary such that it can be used in the for loop, however I want to ensure the keys are strings and the values are Activity objects.
I tried many variations of inheriting from Dictionary or typing the class, but so far it's resulted in multiple errors.
EDIT:
I don't think a simple generic dictionary will work, such as String:Activity. I want to have extra methods in the ActivityMap class, such as getAllActivitiesBetweenDates().
I need an actual class definition, not a generic dictionary expression.
You can make it looks like dictionary by implement subscript operator
And conform to Sequence protocol to support for-in loop
struct ActivitiesMap : Sequence {
var map = [String:Activity]()
subscript(key: String) -> Activity? {
get {
return map[key]
}
set(newValue) {
map[key] = newValue
}
}
func generate() -> GeneratorOf<(String, Activity)> {
var gen = map.generate()
return GeneratorOf() {
return gen.next()
}
}
// I can't find out type of map.generator() now, if you know it, you can do
//func generate() -> /*type of map.generator()*/ {
// return map.generate();
//}
}
This works for me. Not sure what is in your ActivitiesMap class, but just typed a Dictionary
class Activity{
var title:String = "";
var uuid: String = "";
}
func testMap() {
//var map = ActivitiesMap()
var map: Dictionary< String, Activity> = Dictionary< String, Activity>();
var activity = Activity()
activity.title = "Activity 1"
activity.uuid = "asdf1234"
map[activity.uuid] = activity
for (key, mapActivity) in map {
println("ACTIVITY MAP: \(key)=\(mapActivity)")
}
}
testMap();
This is my output:
ACTIVITY MAP: asdf1234=C11lldb_expr_08Activity (has 2 children)
class Activity {
var title=""
var id=""
init(id:String, title:String) { self.id=id; self.title = title }
}
var activities = [String:Activity]()
let a1 = Activity(id:"a1", title:"title1")
let a2 = Activity(id:"a2", title:"title2")
let a3 = Activity(id:"a3", title:"title3")
activities[a1.id] = a1
activities[a2.id] = a2
activities[a3.id] = a3
for (id,activity) in activities {
println("id: \(id) - \(activity.title)")
}
should print
id: a2 - title2
id: a3 - title3
id: a1 - title1
(key order not guaranteed to be the same)
You can use typealias keyword to define nice name of any type.
Here is how it can be used for your code:
class Activity { /* your code */ }
typealias ActivityMap = Dictionary<String, Activity>
var activityDict = ActivityMap()
And to support custom functions you can write an extension, example bellow:
extension Dictionary {
func getAllActivitiesBetweenDates(fromDate:NSDate, toDate:NSDate) -> Array<Activity>
// your code
return []
}
}
Usage:
let matchedActivities = activityDict.getAllActivitiesBetweenDates(/*date*/, /*date*/)
How to achieve reflection in Swift Language?
How can I instantiate a class
[[NSClassFromString(#"Foo") alloc] init];
You must put #objc(SwiftClassName) above your swift class.
Like:
#objc(SubClass)
class SubClass: SuperClass {...}
This is the way I init derived UIViewController by class name
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass()
More information is here
In iOS 9
var className = "YourAppName.TestViewController"
let aClass = NSClassFromString(className) as! UIViewController.Type
let viewController = aClass.init()
Less hacky solution here: https://stackoverflow.com/a/32265287/308315
Note that Swift classes are namespaced now so instead of "MyViewController" it'd be "AppName.MyViewController"
Deprecated since XCode6-beta 6/7
Solution developed using XCode6-beta 3
Thanks to the answer of Edwin Vermeer I was able to build something to instantiate Swift classes into an Obj-C class by doing this:
// swift file
// extend the NSObject class
extension NSObject {
// create a static method to get a swift class for a string name
class func swiftClassFromString(className: String) -> AnyClass! {
// get the project name
if var appName: String? = NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as String? {
// generate the full name of your class (take a look into your "YourProject-swift.h" file)
let classStringName = "_TtC\(appName!.utf16count)\(appName)\(countElements(className))\(className)"
// return the class!
return NSClassFromString(classStringName)
}
return nil;
}
}
// obj-c file
#import "YourProject-Swift.h"
- (void)aMethod {
Class class = NSClassFromString(key);
if (!class)
class = [NSObject swiftClassFromString:(key)];
// do something with the class
}
EDIT
You can also do it in pure obj-c:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleName"];
NSString *classStringName = [NSString stringWithFormat:#"_TtC%d%#%d%#", appName.length, appName, className.length, className];
return NSClassFromString(classStringName);
}
I hope this will help somebody !
UPDATE: Starting with beta 6 NSStringFromClass will return your bundle name plus class name separated by a dot. So it will be something like MyApp.MyClass
Swift classes will have a constructed internal name that is build up of the following parts:
It will start with _TtC,
followed by a number that is the length of your application name,
followed by your application name,
folowed by a number that is the length of your class name,
followed by your class name.
So your class name will be something like _TtC5MyApp7MyClass
You can get this name as a string by executing:
var classString = NSStringFromClass(self.dynamicType)
Update In Swift 3 this has changed to:
var classString = NSStringFromClass(type(of: self))
Using that string, you can create an instance of your Swift class by executing:
var anyobjectype : AnyObject.Type = NSClassFromString(classString)
var nsobjectype : NSObject.Type = anyobjectype as NSObject.Type
var rec: AnyObject = nsobjectype()
It's almost the same
func NSClassFromString(_ aClassName: String!) -> AnyClass!
Check this doc:
https://developer.apple.com/library/prerelease/ios/documentation/Cocoa/Reference/Foundation/Miscellaneous/Foundation_Functions/#//apple_ref/c/func/NSClassFromString
I was able to instantiate an object dynamically
var clazz: NSObject.Type = TestObject.self
var instance : NSObject = clazz()
if let testObject = instance as? TestObject {
println("yes!")
}
I haven't found a way to create AnyClass from a String (without using Obj-C). I think they don't want you to do that because it basically breaks the type system.
For swift2, I created a very simple extension to do this more quickly
https://github.com/damienromito/NSObject-FromClassName
extension NSObject {
class func fromClassName(className : String) -> NSObject {
let className = NSBundle.mainBundle().infoDictionary!["CFBundleName"] as! String + "." + className
let aClass = NSClassFromString(className) as! UIViewController.Type
return aClass.init()
}
}
In my case, i do this to load the ViewController I want:
override func viewDidLoad() {
super.viewDidLoad()
let controllers = ["SettingsViewController", "ProfileViewController", "PlayerViewController"]
self.presentController(controllers.firstObject as! String)
}
func presentController(controllerName : String){
let nav = UINavigationController(rootViewController: NSObject.fromClassName(controllerName) as! UIViewController )
nav.navigationBar.translucent = false
self.navigationController?.presentViewController(nav, animated: true, completion: nil)
}
This will get you the name of the class that you want to instantiate. Then you can use Edwins answer to instantiate a new object of your class.
As of beta 6 _stdlib_getTypeName gets the mangled type name of a variable. Paste this into an empty playground:
import Foundation
class PureSwiftClass {
}
var myvar0 = NSString() // Objective-C class
var myvar1 = PureSwiftClass()
var myvar2 = 42
var myvar3 = "Hans"
println( "TypeName0 = \(_stdlib_getTypeName(myvar0))")
println( "TypeName1 = \(_stdlib_getTypeName(myvar1))")
println( "TypeName2 = \(_stdlib_getTypeName(myvar2))")
println( "TypeName3 = \(_stdlib_getTypeName(myvar3))")
The output is:
TypeName0 = NSString
TypeName1 = _TtC13__lldb_expr_014PureSwiftClass
TypeName2 = _TtSi
TypeName3 = _TtSS
Ewan Swick's blog entry helps to decipher these strings: http://www.eswick.com/2014/06/inside-swift/
e.g. _TtSi stands for Swift's internal Int type.
In Swift 2.0 (tested in the Xcode 7.01) _20150930
let vcName = "HomeTableViewController"
let ns = NSBundle.mainBundle().infoDictionary!["CFBundleExecutable"] as! String
// Convert string to class
let anyobjecType: AnyObject.Type = NSClassFromString(ns + "." + vcName)!
if anyobjecType is UIViewController.Type {
// vc is instance
let vc = (anyobjecType as! UIViewController.Type).init()
print(vc)
}
xcode 7 beta 5:
class MyClass {
required init() { print("Hi!") }
}
if let classObject = NSClassFromString("YOURAPPNAME.MyClass") as? MyClass.Type {
let object = classObject.init()
}
string from class
let classString = NSStringFromClass(TestViewController.self)
or
let classString = NSStringFromClass(TestViewController.classForCoder())
init a UIViewController class from string:
let vcClass = NSClassFromString(classString) as! UIViewController.Type
let viewController = vcClass.init()
I am using this category for Swift 3:
//
// String+AnyClass.swift
// Adminer
//
// Created by Ondrej Rafaj on 14/07/2017.
// Copyright © 2017 manGoweb UK Ltd. All rights reserved.
//
import Foundation
extension String {
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
}
class StringClassConverter<T> {
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String else {
return nil
}
guard let aClass: T.Type = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
The use would be:
func getViewController(fromString: String) -> UIViewController? {
guard let viewController: UIViewController.Type = "MyViewController".converToClass() else {
return nil
}
return viewController.init()
}
I think I'm right in saying that you can't, at least not with the current beta (2). Hopefully this is something that will change in future versions.
You can use NSClassFromString to get a variable of type AnyClass but there appears to be no way in Swift to instantiate it. You can use a bridge to Objective C and do it there or -- if it works in your case -- fall back to using a switch statement.
Apparently, it is not possible (anymore) to instantiate an object in Swift when the name of the class is only known at runtime. An Objective-C wrapper is possible for subclasses of NSObject.
At least you can instantiate an object of the same class as another object given at runtime without an Objective-C wrapper (using xCode Version 6.2 - 6C107a):
class Test : NSObject {}
var test1 = Test()
var test2 = test1.dynamicType.alloc()
In Swift 2.0 (tested in the beta2 of Xcode 7) it works like this:
protocol Init {
init()
}
var type = NSClassFromString(className) as? Init.Type
let obj = type!.init()
For sure the type coming from NSClassFromString have to implement this init protocol.
I expect it is clear, className is a String containing the Obj-C runtime name of the class which is by default NOT just "Foo", but this discussion is IMHO not the major topic of your question.
You need this protocol because be default all Swift classes don't implement an init method.
Looks like the correct incantation would be...
func newForName<T:NSObject>(p:String) -> T? {
var result:T? = nil
if let k:AnyClass = NSClassFromString(p) {
result = (k as! T).dynamicType.init()
}
return result
}
...where "p" stands for "packaged" – a distinct issue.
But the critical cast from AnyClass to T currently causes a compiler crash, so in the meantime one must bust initialization of k into a separate closure, which compiles fine.
I use different targets, and in this case the swift class is not found. You should replace CFBundleName with CFBundleExecutable. I also fixed the warnings:
- (Class)swiftClassFromString:(NSString *)className {
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleExecutable"];
NSString *classStringName = [NSString stringWithFormat:#"_TtC%lu%#%lu%#", (unsigned long)appName.length, appName, (unsigned long)className.length, className];
return NSClassFromString(classStringName);
}
Isn't the solution as simple as this?
// Given the app/framework/module named 'MyApp'
let className = String(reflecting: MyClass.self)
// className = "MyApp.MyClass"
Also in Swift 2.0 (possibly before?) You can access the type directly with the dynamicType property
i.e.
class User {
required init() { // class must have an explicit required init()
}
var name: String = ""
}
let aUser = User()
aUser.name = "Tom"
print(aUser)
let bUser = aUser.dynamicType.init()
print(bUser)
Output
aUser: User = {
name = "Tom"
}
bUser: User = {
name = ""
}
Works for my use case
Try this.
let className: String = String(ControllerName.classForCoder())
print(className)
I have implemented like this,
if let ImplementationClass: NSObject.Type = NSClassFromString(className) as? NSObject.Type{
ImplementationClass.init()
}
Swift 5, easy to use, thanks to #Ondrej Rafaj's
Source code:
extension String {
fileprivate
func convertToClass<T>() -> T.Type? {
return StringClassConverter<T>.convert(string: self)
}
var controller: UIViewController?{
guard let viewController: UIViewController.Type = convertToClass() else {
return nil
}
return viewController.init()
}
}
class StringClassConverter<T> {
fileprivate
static func convert(string className: String) -> T.Type? {
guard let nameSpace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String, let aClass = NSClassFromString("\(nameSpace).\(className)") as? T.Type else {
return nil
}
return aClass
}
}
Call like this:
guard let ctrl = "ViewCtrl".controller else {
return
}
// ctrl do sth
A page jump example shown here, the hope can help you!
let vc:UIViewController = (NSClassFromString("SwiftAutoCellHeight."+type) as! UIViewController.Type).init()
self.navigationController?.pushViewController(vc, animated: true)
// Click the Table response
tableView.deselectRow(at: indexPath, animated: true)
let sectionModel = models[(indexPath as NSIndexPath).section]
var className = sectionModel.rowsTargetControlerNames[(indexPath as NSIndexPath).row]
className = "GTMRefreshDemo.\(className)"
if let cls = NSClassFromString(className) as? UIViewController.Type {
let dvc = cls.init()
self.navigationController?.pushViewController(dvc, animated: true)
}
Swift3+
extension String {
var `class`: AnyClass? {
guard
let dict = Bundle.main.infoDictionary,
var appName = dict["CFBundleName"] as? String
else { return nil }
appName.replacingOccurrences(of: " ", with: "_")
let className = appName + "." + self
return NSClassFromString(className)
}
}
Here is a good example:
class EPRocks {
#require init() { }
}
class EPAwesome : EPRocks {
func awesome() -> String { return "Yes"; }
}
var epawesome = EPAwesome.self();
print(epawesome.awesome);