I have created 2 frameworks. One for Chat App UI and another one for Voice and Video Call. UI framework uses Voice framework if voice video module is enabled. If someone has disabled it, I don't want to include it, but I have already used 'import VoiceVideo' to import it into UI framework. So during build it will crash as it will not able to found it.
So my question is can we use functions of a framework without importing it by means of bundle paths or any other way.
I Have tried to create instance of VoiceVideo framework class using "NSClassFromString".
if let bundle = Bundle.init(identifier: "FRAMEWORK_IDENTIFIER"){
let bundlePath = bundle.bundlePath
let newBundle = Bundle.init(path: bundlePath)
newBundle?.load()
let newclass : AnyClass? = newBundle?.classNamed("ChVoiceVideo")
}
But it return nil.
Related
I have to do some operations in my xml file in project
Operations like -
Inserting the records
Writing the records
Deleting the records at specific location(with index).
NSXMLDocument is for Mac OS
Can any one help me with some sample code to understand and to implement the operations on the Xml file without using the third party frameworks.
For iOS Apple provides an XMLParser api for swift and NSXMLParser for Objective C. You can check out this tutorial for reference.
You can use following code to read a file in iOS
let bundle = NSBundle.mainBundle()
let path = bundle.pathForResource("data", ofType: "json")
let content = NSString.stringWithContentsOfFile(path) as String
then pass the string to XMLParser and implement its delegate methods.
Hope this helps.
My project structure is in such a way that I have 2 projects named as ProjectA and ProjectB. Now for these 2 projects, I have created a framework named as ProjFramework.
I have added common files of these 2 projects inside the framework and then added framework in the projects separately. Till now I am able to access all the files and vars. Now I added the images also in the framework and when I am trying to display them in UI then I am getting an error saying unable to read this .png.
As I know I need to include the framework somehow but I don't know how. Please help me in sharing common images b/w 2 XCode projects.
Thanks in advance.
An alternative to #arunjos007' method is to (a) create a bundle of your images, then (b) retrieve from the bundle.
Let's say you have a Framework called Kernel, and you wish to access two types of files - cikernel and png.
Creating a bundle:
Move all your files/images into a new folder on your desktop. Name it whatever you wish. In my example I named them cikernels and images.
Rename your folders, with a .bundle extension. In my example they became cikernels.bundle and images.bundle. You will see the warning below... choose "Add".
Drag the bundle into your framework project. You can expand the bundle to see the contents. Also, you can add/delete/edit the contents of the bundle.
Retrieving files from the bundle:
I've created two public functions, one to retrieve files and one to retrieve images. They are pretty much the same, except for (a) the return type and (b) error handling. (I probably should add some to the UIImage function - but since I have total control on the code - it's not going to be used by anyone else - it's not critical.)
public func returnImage(_ named:String) -> UIImage {
let myBundle = Bundle.init(identifier: "com.companyName.frameworkName")
let imagePath = (myBundle?.path(forResource: "images", ofType: "bundle"))! + "/" + named
let theImage = UIImage(contentsOfFile: imagePath)
return theImage!
}
public func returnKernel(_ named:String) -> String {
let myBundle = Bundle.init(identifier: "com.companyName.frameworkName")
let kernelPath = (myBundle?.path(forResource: "cikernels", ofType: "bundle"))! + "/" + named + ".cikernel"
do {
return try String(contentsOfFile: kernelPath)
}
catch let error as NSError {
return error.description
}
}
One last note: The identifier is defined in the Framework target's General tab. Typically it's in the form com.companyframework*. Change that line to your's.
Before Building and distributing your framework, you should copy files that need to carry with your framework.
To copy files, Click your framework project target, go to "Build Phases" tab and you can see a section called "Copy Files". Add your files here(See below screenshot)
Then build your framework project, after a successful build your distributable framework will be generated in your Products folder. It will look like YourFrameworkProjectName.framework.
Add this file to other projects which need your framework.
Note: If you just want to run your project by connecting with your framework project, you can be done it by adding build dependency. See StackOverflow question here
Currently building an iMessage app, and would like to experiment with using a database. I have a database that I would like to use in the app, and have included it in my project, and verified the target membership is correct. Using SQLite.Swift.
Whenever I try opening the connection to the database in simulator, I always get an error (unexpected nil) for the path of the database.
I've tried an image file the same way with no avail.
let imagePath = Bundle.main.path(forResource: "db", ofType: ".sqlite")
do {
let db = try Connection(imagePath!, readonly: true)
} catch {
}
I believe the issue is more related to what an iMessage "app" is - which is actually an extension, not a true app. There's no initial VC, thus no real Bundle.main to get to.
One (maybe soon a second) app of mine has a Photo Editing Extension - basically what I always have called a "shell connection" to an Apple app. You really have either a "do nothing" app with a connection to one of their apps, or you have a stand-alone app an share the code with the extension.
My solution for sharing code is to use a Framework target. Yes, a third project. (App, extension, shared code.) I found a technique that I think should work for you - basically, for images, scripts (my apps use .cikernel files) you add them into the framework project and return what you need in a function call.
You may be able to streamline this with a need for a Framework target. YMMV. The basics are this:
Someplace in Xcode you have a "Bundle Identifier". Something like *"com.company.projectname".
Put your files into a folder, maybe on your desktop. Add an extension to this folder called ".bundle". macOS will give you a warning, accept it. All you are really doing is creating your bundle.
Drag this into your Xcode project.
Code to get to this bundle, and the files inside it. (I'm not sure if need a framework here - try to drag this into your "MessagesExtension" target first.
So lets say you have images you wish to share between projects, extensions, whatever. After moving them into a folder called "images", andrenaming the folder with a ".bundle" at the end, and finally dragging it into your Xcode project, you pretty much need to add this function:
public func returnImage(_ named:String) -> UIImage {
let myBundle = Bundle.init(identifier: "com.company.project")
let imagePath = (myBundle?.path(forResource: "images", ofType: "bundle"))! + "/" + named
let theImage = UIImage(contentsOfFile: imagePath)
return theImage!
}
For a text file you want:
public func returnKernel(_ named:String) -> String {
let myBundle = Bundle.init(identifier: "com.company.project")
let kernelPath = (myBundle?.path(forResource: "cikernels", ofType: "bundle"))! + "/" + named + ".cikernel"
do {
return try String(contentsOfFile: kernelPath)
}
catch let error as NSError {
return error.description
}
}
Usage, for an image called "Camera.png" which is part of a bundle called "images.bundle":
let cameraImage = returnImage("Camera")
Since I don't work with SQLite files I don't have the exact code, but I think this should work. Remember to change "com.company.project" to what you have for the bundle identifier.
I would like to be able to redirect my logging statements to a file so that I can retrieve them when my app runs standalone (i.e. is not attached to Xcode). I have discovered (thank you Stackoverflow) that freopen can be used to accomplish this.
If I create a new Xcode project and add the code to redirect stderr then everything works as expected.
However, when I add the redirection code to my existing, bluetooth project I am having trouble. The file is being created and I can retrieve it using iTunes or Xcode's Devices window, but it is of size 0. If I explicitly close the file then the text that I wrote actually makes it into the file. It is as though iOS is not flushing the file when the app is terminated. I suspect that the trouble stems from the fact that I have enabled background processing. Can anyone help me to understand this?
Here is my code:
let pathes = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
let filePath = NSURL(fileURLWithPath: pathes[0]).URLByAppendingPathComponent("Test.log")
freopen(filePath.path!, "a", stderr);
fputs("Hello, Samantha!\r\n", stderr);
struct StderrOutputStream: OutputStreamType {
static let stream = StderrOutputStream()
func write(string: String) {fputs(string, stderr)}
}
var errStream = StderrOutputStream.stream
print("Hello, Robert", toStream: &errStream)
fclose(stderr) // Without this the text does not make it into the file.
I'd leave this as a comment, but have you looked into NSFileHandle? It sounds like you just need a way to append data to the end of a text file, correct?
Once you have a handle with something like NSFileHandle(forWritingToURL:), you can use .seekToEndOfFile() and .writeData(_:). As a side note, you'll need to convert your String to Data before writing it.
Admittedly, this will probably end up being more lines of code, and you'll almost certainly need to take threading into consideration.
I'm making an app that makes use of a big database with 5 different tables. I want to import that database to the app bundle and be able to query through the tables. The database won't be edited by the user, so adding and removing records is not required.
What would be the best way to add the database in the app?
The process is as follows:
Add the database to your bundle. When you drag it into your project, you can choose to add it to the target that represents your main app. Alternatively, review the target settings, click on "Build Phases" and confirm the database appears in the "Copy Bundle Resources" list.
Use a framework like FMDB to simplify your life. This is written in Objective-C, but works great in Swift, too. What you need to do is:
Copy the .h and .m files for FMDB into your project;
When prompted to create a "bridging header", do so;
Add the following line to your bridging header:
#import "FMDB.h"
By following those steps, you can use this framework developed in Objective-C into your Swift projects.
You can now write your Swift code, using the FMDB framework. For example, the Swift code to open the database, select columns x, y, and z from a table called test, would look like:
let path = NSBundle.mainBundle().pathForResource("test", ofType:"sqlite")
let database = FMDatabase(path: path)
if !database.open() {
print("Unable to open database")
return
}
if let rs = database.executeQuery("select * from test", withArgumentsInArray: nil) {
while rs.next() {
let x = rs.stringForColumn("x")
let y = rs.stringForColumn("y")
let z = rs.stringForColumn("z")
print("x = \(x); y = \(y); z = \(z)")
}
} else {
print("executeQuery failed: \(database.lastErrorMessage())")
}
database.close()
Swift only. No Objective-C files required.
Here is another solution which uses SQLite.swift instead of FMDB.
1.: Get a valid path to your database file
Don't save the database file in your Assets folder, but add it to your Copy Bundle Resources list in the Build Phases setting of your current target. If your file is called myDb.db you can then get a valid path like this:
let dbUrl = Bundle.main.url(forResource: "myDb", withExtension: "db")!
let dbPath = dbUrl.path
2.: Access your database (without copying it)
It is possible to access your database now without having to (manually?) copy it. Just use the SQLite.swift library:
db = try! Connection(dbPath)
Notes:
The only thing I haven't checked yet explicitly is writing to the database. At least a read-only access does work like a charm though.