specialized static crashing ios app - ios

import Foundation
import K
#objc class KU: NSObject {
static func setUpConfigs() {
let productId = TNYEnvironment.sharedInstance().kProductID
let url = TNYEnvironment.sharedInstance().kUrl
K.sharedInstance.configureK(productId,
url: NSURL(string: url)!)
}
static func trackEvent(eventName: String) {
let formattedEventName = eventName.lowercaseString.stringByReplacingOccurrencesOfString(" ", withString: "_")
self.trackEvent(formattedEventName, properties:[:])
}
static func trackEvent(eventName: String, properties: [String: NSObject]) {
let instance = K.sharedInstance
instance.track(eventName, params:properties)
instance.uploadEvents({})
}
}
Its crashing on ipa build, but works fine on debugger. I guess some issue with swift to objc code. Anyone had the same issue?

Related

Create Files,Debug Step By Step,Crash--EXC_BAD_ACCESS

I want to create test.txt file, sample codes.
I run this app, well done.
But when I debug app step by step, it crash, report
(EXC_BAD_ACCESS(code=EXC_l386_GPFLT)),method:
FileManager.default.createFile()
SWIFT CODES:
import UIKit
class ViewController: UIViewController {
#IBAction func CreateFileButton(_ sender: UIButton) {
createFiles()
}
private func createFiles() {
let manager = FileManager.default
let urlForDocument = manager.urls( for: .documentDirectory,
in:.userDomainMask)
let url = urlForDocument[0]
createFile(name:"test.txt", fileBaseUrl: url)
}
func createFile(name:String, fileBaseUrl:URL){
let manager = FileManager.default
let file = fileBaseUrl.appendingPathComponent(name)
print("f: \(file)")
let exist = manager.fileExists(atPath: file.path)
if !exist {
let data = Data(base64Encoded:"aGVsbG8gd29ybGQ=" ,options:.ignoreUnknownCharacters)
let createSuccess = manager.createFile(atPath: file.path,contents:data,attributes:nil)
print("f: \(createSuccess)")
}
}
}
^ Crash step method:FileManager.default.createFile()
^Crash report
^Just run app, do not debug, it well done

How to send MSMessage in Messages Extension?

I want to implement an imessage app, however being new to the messages framework and iMessage apps being such a new thing there aren't many resources. So I am following the WWDC video and using Apples providing sample app for a guide.
I have three views, the MessageViewController which handles pretty much all the functionality and then a CreateViewController and a DetailsViewController.
I am simply trying to create an MSMessage from the CreateViewController and display in the DetailsViewController.. then add to the data.
However I get a crash when trying to create the data.
#IBAction func createAction(_ sender: AnyObject) {
//present full screen for create list
self.delegate?.createViewControllerDidSelectAdd(self as! CreateViewControllerDelegate)
}
The data type I am trying to pass is the dictionary from a struct:
struct data {
var title: String!
var date: Date!
var dictionary = ["title" : String(), "Array1" : [String](), "Array2" : [String]() ] as [String : Any]
}
So here's how things are set up;
MessagesViewController
class MessagesViewController: MSMessagesAppViewController, {
// MARK: Responsible for create list button
func composeMessage(for data: dataItem) {
let messageCaption = NSLocalizedString("Let's make", comment: "")
let dictionary = data.dictionary
func queryItems(dictionary: [String:String]) -> [URLQueryItem] {
return dictionary.map {
URLQueryItem(name: $0, value: $1)
}
}
var components = URLComponents()
components.queryItems = queryItems(dictionary: dictionary as! [String : String])
let layout = MSMessageTemplateLayout()
layout.image = UIImage(named: "messages-layout-1.png")!
layout.caption = messageCaption
let message = MSMessage()
message.url = components.url!
message.layout = layout
message.accessibilityLabel = messageCaption
guard let conversation = activeConversation else { fatalError("Expected Convo") }
conversation.insert(message) { error in
if let error = error {
print(error)
}
}
}
}
extension MessagesViewController: CreateViewControllerDelegate {
func createViewControllerDidSelectAdd(_ controller: CreateViewControllerDelegate) {
//CreatesNewDataItem
composeMessage(for: dataItem())
}
}
CreateViewController
/**
A delegate protocol for the `CreateViewController` class.
*/
protocol CreateViewControllerDelegate : class {
func createViewControllerDidSelectAdd(_ controller: CreateViewControllerDelegate)
}
class CreateViewController: UIViewController {
static let storyboardIdentifier = "CreateViewController"
weak var delegate: CreateViewControllerDelegate?
#IBAction func create(_ sender: AnyObject) {
//present full screen for create list
self.delegate?.createViewControllerDidSelectAdd(self as! CreateListViewControllerDelegate)
}
}
Would someone show where I am going wrong and how I can send a MSMessage? If I am able to send the message I should then be able to receive and resend.
One issue I see, without being able to debug this myself:
you are setting your components.queryItems to your dictionary var cast as [String:String], but the dictionary returned from data.dictionary is not a [String:String], but a [String:Any]
In particular, dictionary["Array1"] is an array of Strings, not a single string. Same for dictionary["Array2"]. URLQueryItem expects to be given two strings in its init(), but you're trying to put a string and an array of strings in (though I'm not sure that you're actually getting to that line in your queryItems(dictionary:) method.
Of course, your dataItem.dictionary is returning a dictionary with 4 empty values. I'm not sure that's what you want.

Crosswalks on ios - can not find the XWalkView module

I am trying to use crosswalk to make my web app into iOS app
and I following the steps of Quickstart in this page https://github.com/crosswalk-project/crosswalk-ios
but I keep facing the problem:
in the ViewController.swift
When I import XWalkView, It always appears that It can't find the XWalkView module which makes me stop from my development...
Somebody help me.
Here's the code of ViewController.swift:
import UIKit
import WebKit
import XWalkView
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var start_url = "index.html"
var xwalk_extensions = ["Extension.load"]
if let plistPath = NSBundle.mainBundle().pathForResource("manifest", ofType: "plist") {
if let manifest = NSDictionary(contentsOfFile: plistPath) {
start_url = manifest["start_url"] as? String ?? start_url
xwalk_extensions = manifest["xwalk_extensions"] as? [String] ?? xwalk_extensions
}
}
let webview = WKWebView(frame: view.frame, configuration: WKWebViewConfiguration())
webview.scrollView.bounces = false
view.addSubview(webview)
for name in xwalk_extensions {
if let ext: AnyObject = XWalkExtensionFactory.createExtension(name) {
webview.loadExtension(ext, namespace: name)
}
}
if let root = NSBundle.mainBundle().resourceURL?.URLByAppendingPathComponent("www") {
var error: NSError?
let url = root.URLByAppendingPathComponent(start_url)
if url.checkResourceIsReachableAndReturnError(&error) {
webview.loadFileURL(url, allowingReadAccessToURL: root)
} else {
webview.loadHTMLString(error!.description, baseURL: nil)
}
}
}
override func prefersStatusBarHidden() -> Bool {
return true
}
}
perhaps you forget to link the XWalkView.framework in your app target?
Try this step:
Open Xcode, select your app project, in 'General' -> 'Linked Frameworks and Libraries', select '+', choose 'XWalkView.framework' to add it into the linking frameworks. Then shot another build. It's ok if the XWalkView.framework turns red.

How to split filename from file extension in Swift?

Given the name of a file in the bundle, I want load the file into my Swift app. So I need to use this method:
let soundURL = NSBundle.mainBundle().URLForResource(fname, withExtension: ext)
For whatever reason, the method needs the filename separated from the file extension. Fine, it's easy enough to separate the two in most languages. But so far I'm not finding it to be so in Swift.
So here is what I have:
var rt: String.Index = fileName.rangeOfString(".", options:NSStringCompareOptions.BackwardsSearch)
var fname: String = fileName .substringToIndex(rt)
var ext = fileName.substringFromIndex(rt)
If I don't include the typing on the first line, I get errors on the two subsequent lines. With it, I'm getting an error on the first line:
Cannot convert the expression's type '(UnicodeScalarLiteralConvertible, options: NSStringCompareOptions)' to type 'UnicodeScalarLiteralConvertible'
How can I split the filename from the extension? Is there some elegant way to do this?
I was all excited about Swift because it seemed like a much more elegant language than Objective C. But now I'm finding that it has its own cumbersomeness.
Second attempt: I decided to make my own string-search method:
func rfind(haystack: String, needle: Character) -> Int {
var a = Array(haystack)
for var i = a.count - 1; i >= 0; i-- {
println(a[i])
if a[i] == needle {
println(i)
return i;
}
}
return -1
}
But now I get an error on the line var rt: String.Index = rfind(fileName, needle: "."):
'Int' is not convertible to 'String.Index'
Without the cast, I get an error on the two subsequent lines.
Can anyone help me to split this filename and extension?
Swift 5.0 update:
As pointed out in the comment, you can use this.
let filename: NSString = "bottom_bar.png"
let pathExtention = filename.pathExtension
let pathPrefix = filename.deletingPathExtension
This is with Swift 2, Xcode 7: If you have the filename with the extension already on it, then you can pass the full filename in as the first parameter and a blank string as the second parameter:
let soundURL = NSBundle.mainBundle()
.URLForResource("soundfile.ext", withExtension: "")
Alternatively nil as the extension parameter also works.
If you have a URL, and you want to get the name of the file itself for some reason, then you can do this:
soundURL.URLByDeletingPathExtension?.lastPathComponent
Swift 4
let soundURL = NSBundle.mainBundle().URLForResource("soundfile.ext", withExtension: "")
soundURL.deletingPathExtension().lastPathComponent
Works in Swift 5. Adding these behaviors to String class:
extension String {
func fileName() -> String {
return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
func fileExtension() -> String {
return URL(fileURLWithPath: self).pathExtension
}
}
Example:
let file = "image.png"
let fileNameWithoutExtension = file.fileName()
let fileExtension = file.fileExtension()
Solution Swift 4
This solution will work for all instances and does not depend on manually parsing the string.
let path = "/Some/Random/Path/To/This.Strange.File.txt"
let fileName = URL(fileURLWithPath: path).deletingPathExtension().lastPathComponent
Swift.print(fileName)
The resulting output will be
This.Strange.File
In Swift 2.1 String.pathExtension is not available anymore. Instead you need to determine it through NSURL conversion:
NSURL(fileURLWithPath: filePath).pathExtension
In Swift you can change to NSString to get extension faster:
extension String {
func getPathExtension() -> String {
return (self as NSString).pathExtension
}
}
Latest Swift 4.2 works like this:
extension String {
func fileName() -> String {
return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
func fileExtension() -> String {
return URL(fileURLWithPath: self).pathExtension
}
}
In Swift 2.1, it seems that the current way to do this is:
let filename = fileURL.URLByDeletingPathExtension?.lastPathComponent
let extension = fileURL.pathExtension
Swift 5 with code sugar
extension String {
var fileName: String {
URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
}
var fileExtension: String{
URL(fileURLWithPath: self).pathExtension
}
}
SWIFT 3.x Shortest Native Solution
let fileName:NSString = "the_file_name.mp3"
let onlyName = fileName.deletingPathExtension
let onlyExt = fileName.pathExtension
No extension or any extra stuff
(I've tested. based on #gabbler solution for Swift 2)
Swift 5
URL.deletingPathExtension().lastPathComponent
Strings in Swift can definitely by tricky. If you want a pure Swift method, here's how I would do it:
Use find to find the last occurrence of a "." in the reverse of the string
Use advance to get the correct index of the "." in the original string
Use String's subscript function that takes an IntervalType to get the strings
Package this all up in a function that returns an optional tuple of the name and extension
Something like this:
func splitFilename(str: String) -> (name: String, ext: String)? {
if let rDotIdx = find(reverse(str), ".") {
let dotIdx = advance(str.endIndex, -rDotIdx)
let fname = str[str.startIndex..<advance(dotIdx, -1)]
let ext = str[dotIdx..<str.endIndex]
return (fname, ext)
}
return nil
}
Which would be used like:
let str = "/Users/me/Documents/Something.something/text.txt"
if let split = splitFilename(str) {
println(split.name)
println(split.ext)
}
Which outputs:
/Users/me/Documents/Something.something/text
txt
Or, just use the already available NSString methods like pathExtension and stringByDeletingPathExtension.
Swift 5
URL(string: filePath)?.pathExtension
Try this for a simple Swift 4 solution
extension String {
func stripExtension(_ extensionSeperator: Character = ".") -> String {
let selfReversed = self.reversed()
guard let extensionPosition = selfReversed.index(of: extensionSeperator) else { return self }
return String(self[..<self.index(before: (extensionPosition.base.samePosition(in: self)!))])
}
}
print("hello.there.world".stripExtension())
// prints "hello.there"
Swift 3.0
let sourcePath = NSURL(string: fnName)?.pathExtension
let pathPrefix = fnName.replacingOccurrences(of: "." + sourcePath!, with: "")
Swift 3.x extended solution:
extension String {
func lastPathComponent(withExtension: Bool = true) -> String {
let lpc = self.nsString.lastPathComponent
return withExtension ? lpc : lpc.nsString.deletingPathExtension
}
var nsString: NSString {
return NSString(string: self)
}
}
let path = "/very/long/path/to/filename_v123.456.plist"
let filename = path.lastPathComponent(withExtension: false)
filename constant now contains "filename_v123.456"
A better way (or at least an alternative in Swift 2.0) is to use the String pathComponents property. This splits the pathname into an array of strings. e.g
if let pathComponents = filePath.pathComponents {
if let last = pathComponents.last {
print(" The last component is \(last)") // This would be the extension
// Getting the last but one component is a bit harder
// Note the edge case of a string with no delimiters!
}
}
// Otherwise you're out of luck, this wasn't a path name!
They got rid of pathExtension for whatever reason.
let str = "Hello/this/is/a/filepath/file.ext"
let l = str.componentsSeparatedByString("/")
let file = l.last?.componentsSeparatedByString(".")[0]
let ext = l.last?.componentsSeparatedByString(".")[1]
A cleaned up answer for Swift 4 with an extension off of PHAsset:
import Photos
extension PHAsset {
var originalFilename: String? {
if #available(iOS 9.0, *),
let resource = PHAssetResource.assetResources(for: self).first {
return resource.originalFilename
}
return value(forKey: "filename") as? String
}
}
As noted in XCode, the originalFilename is the name of the asset at the time it was created or imported.
Maybe I'm getting too late for this but a solution that worked for me and consider quite simple is using the #file compiler directive. Here is an example where I have a class FixtureManager, defined in FixtureManager.swift inside the /Tests/MyProjectTests/Fixturesdirectory. This works both in Xcode and withswift test`
import Foundation
final class FixtureManager {
static let fixturesDirectory = URL(fileURLWithPath: #file).deletingLastPathComponent()
func loadFixture(in fixturePath: String) throws -> Data {
return try Data(contentsOf: fixtureUrl(for: fixturePath))
}
func fixtureUrl(for fixturePath: String) -> URL {
return FixtureManager.fixturesDirectory.appendingPathComponent(fixturePath)
}
func save<T: Encodable>(object: T, in fixturePath: String) throws {
let data = try JSONEncoder().encode(object)
try data.write(to: fixtureUrl(for: fixturePath))
}
func loadFixture<T: Decodable>(in fixturePath: String, as decodableType: T.Type) throws -> T {
let data = try loadFixture(in: fixturePath)
return try JSONDecoder().decode(decodableType, from: data)
}
}
Creates unique "file name" form url including two previous folders
func createFileNameFromURL (colorUrl: URL) -> String {
var arrayFolders = colorUrl.pathComponents
// -3 because last element from url is "file name" and 2 previous are folders on server
let indx = arrayFolders.count - 3
var fileName = ""
switch indx{
case 0...:
fileName = arrayFolders[indx] + arrayFolders[indx+1] + arrayFolders[indx+2]
case -1:
fileName = arrayFolders[indx+1] + arrayFolders[indx+2]
case -2:
fileName = arrayFolders[indx+2]
default:
break
}
return fileName
}

Swift language NSClassFromString

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);

Resources