Access Bundle Identifier of App in Custom Framework - ios

I am designing a sdk where I am making some url call at some point of time. For this I want the context of the app which is making a call. As a context I want to pass the bundle identifier of the app using that sdk.
SDK is in form of library which some third party app will import and use. I have the reference of UIViewController which is invoking the url call in third party app. I have no idea if having only UIViewController reference is enough or not.
So how can I get access to bundle identifier of their app?
Thanks

It's quite easy.
Each type (class or struct) knows which bundle it is associated with.
Step 1# Get the type of any object inside the framework.
Step 2# Get the bundle of that type. And access its id.
// VC is ref to the class object of the main app
// # 1 -> This will give the class type of the object
let objectType = type(of: vc)
// # 2 -> This will get you the bundle of the main app
let bundle = Bundle(for: objectType.self)
// This will finally give you the bundle id
let bundleId = bundle.bundleIdentifier

Related

Is it possible to pass data from your viewcontroller to a viewcontroller inside pod library?

First of all, i am not asking if its possible to pass data between view controllers. I am specifically asking if there is anyway to pass some values from a viewController thats in my project to a viewController inside a pod library !
The issue i am facing is that, i am using a payment SDK and the SDK doesn't support pay by saved card info. Normally , whenever i invoke a function from the SDK using a delegate in the sdk, a payment view is presented and it accepts inputs and the SDK works with those inputs.
But what i am trying to do is, pass values from saved card data (name number etc) to the viewController thats inside the pod library.
I tried creating a delegate on my class and tried to access it on the pod library viewController and it returned an error saying that it cannot find the item in scope. I tried creating a global variable and that also returned the same error.
So, this is my question. Is there anyway to pass data from my viewController to the viewController thats present in the SDK ?
You can, although it will be pointless to. Whenever you try to edit a file in the /Pods directory, it will warn you with something similar to the following message
You can press Unlock and append your code, or in your case, a delegate. But the next time you run pod install, it would override everything you've written.
The real pain starts when collaborating, as the /Pods directory does not get pushed into VCS.
I actually found a way to this. I made this happen by using sharedInstance
This is how i did it, here i created a sharedInstance variable.
#objc public final class NISdk: NSObject {
#objc public static let sharedInstance = NISdk()
//We will write the next function here//
}
Then i made a function that accepts parameters
#objc public func SavedCardPayment(cardToken : String,expiryMonth : String,
expiryYear : String,cvv : String,cardholderName : String,useSavingCards : Int)
{ // we will write some ways to store the values we just got as parameters// }
On this function
{
//You must declare these global variables somewhere inside the pod !
ViewControllerInsidePod.savedCardToken = cardToken
ViewControllerInsidePod.savedExpiryMonth = expiryMonth
ViewControllerInsidePod.savedExpiryYear = expiryYear
ViewControllerInsidePod.savedCvv = cvv
ViewControllerInsidePod.savedName = cardholderName
}
Now all info which user entered on the screen will be passed to SDK and then stored in these global variables (which are only available inside the pod n not in the project). Then i used these values to call a function of the pod from itself.
Now i will go back to my viewController in my project, and then call the function using sharedInstance
let sharedSDKInstance = NISdk.sharedInstance
let sharedSDKInstance = NISdk.sharedInstance
sharedSDKInstance.SavedCardPayment(cardToken : "\(token)",
expiryMonth : "\(expiryM)",expiryYear : "\(expiryY)",cvv : "\(cvv)",
cardholderName : "\(name)",useSavingCards : 1)
Hope this helps someone, i dunno if this is the right way to do it but this is how i passed data from a viewController that was outside of scope!
REMEMBER THIS
Also as #Visal Rajapakse , this will only work as long as you never update that specific pod !

Not able to access pod swift file, after installing pod

I have created a pod using the raywenderlich tutroial link, everything works fine
creating
installing and
importing the pod too
but when I am trying to access the view controller its says
use of unresolved identifier 'file name'
I tried cmd+click to jump to defination of the file and it navigating me to the file also
code implemented
let bundle = Bundle(for: SDKSplashVC.self) // error here - Use of unresolved identifier 'SDKSplashVC'
let storyboard = UIStoryboard(name: "SDKSplash", bundle: myBundle)
let controller = storyboard.instantiateViewController(withIdentifier: "SDKSplashVC")
self.present(controller, animated: true, completion: nil)
I have imported the pod, crossed check in managed scheme and build phases, my custom pod is listed their but still error persist
Any help would be really helpful.
Thank you
In the tutorial author is using class PickFlavorViewController which is defined in RWPickFlavor cocoa pod.
let bundle = Bundle(for: PickFlavorViewController.self)
You can take a look and see all the files in this pod here.
Docs say that you can use the init(for:) initializer of Bundle to locate the framework bundle based on a class defined in that framework.
You are getting the error because SDKSplashVC is not defined. You need to write something like this:
import UIKit
public class SDKSplashVC: UIViewController {}
Make sure the class is defined as public.

Integrating Admob with ios application

Is it possible that I give admob id in one place in xcode and use it anywhere else in the code, just like in android studio strings implementations or do I need to provide it everytime I create admob ad
You can define your admob key in appDelegate to use in all ViewControllers check below steps:
Create appDelegate shared instance using below code:
static var sharedInstance : AppDelegate!
Define your admob id like below:
let adMobId = "abcd1234"
Now you can use your admob id in any class with below code:
AppDelegate.sharedInstance.adMobId
Create a new Swift class with name Constants and assign values to your admob_banner and admob_home like this
import Foundation
let admob_banner = "your banner id"
let admob_home = "your home id"
Now you can use these variables anywhere in your project

Loading from mainBundle

In a some popular open source swift project. I noticed following approach used to load a file from main bundle.
#objc class TestClass: NSObject { }
let bundle = NSBundle(forClass: TestClass.self)
let path = bundle.pathForResource(filename, ofType: "json")
We can also use this approach.
let path = NSBundle.mainBundle().pathForResource(filename, ofType: "json")
Why would someone choose first approach over second one?
This returns the bundle that contains the TestClass class:
NSBundle(forClass: TestClass.self)
While this returns the main bundle of the application:
NSBundle.mainBundle()
If you execute this code from your application code, it will always return your main bundle. But if that class is contained in a different library or framework, it will return the bundle that contains it.
For example, all Swift libraries in CocoaPods are integrated using dynamic frameworks, and they are deployed in a different bundle inside the main bundle. So all the frameworks must use the embedded bundle to access their resources.
I'd recommend using the first approach (NSBundle(forClass:) method) to improve code portability. And it's required when creating dynamic frameworks.

Swift and CoreData Casting issues in test vs non-test

I'm using Swift on Xcode 6 with CoreData.
I've read the release notes and have seen this issue about making sure to mark up a core data model with a module name (app name) so you can cast an NSManagedObject to your model type at run time.
When I do this, I can get an app to run properly (good!). However, my issue is, when I try to test that same code, the test will always crash whenever the cast happens with a Swift dynamic cast failed error (bad :(). This makes it difficult to test my application.
Is there any impact on the module name we use when the app is built for test vs. running?
Thanks in advance for any pointers...
Follow up:
This is not ideal:
As noted above, In order for Swift to use a Core Data model, you need to decorate the class name with the name of your app. This works find for building the app, but the tests run under a different app name! That means you need to go into the data modeler and change that class name from myAppname.myEntity to myAppnameTests.myEntity before you can use those Entities by name when used by or called from a test.
Your are totally right, the issue is the when you run the App it's looking for myAppname.myEntity and when you run as Test it's looking as myAppnameTests.myEntity. The solution I use at this time (Xcode 6.1) is to NOT fill the Class field in the CoreData UI, and to do it in code instead.
This code will detect if you are running as App vs Tests and use the right module name and update the managedObjectClassName.
lazy var managedObjectModel: NSManagedObjectModel = {
// The managed object model for the application. This property is not optional...
let modelURL = NSBundle.mainBundle().URLForResource("Streak", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)!
// Check if we are running as test or not
let environment = NSProcessInfo.processInfo().environment as [String : AnyObject]
let isTest = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"
// Create the module name
let moduleName = (isTest) ? "StreakTests" : "Streak"
// Create a new managed object model with updated entity class names
var newEntities = [] as [NSEntityDescription]
for (_, entity) in enumerate(managedObjectModel.entities) {
let newEntity = entity.copy() as NSEntityDescription
newEntity.managedObjectClassName = "\(moduleName).\(entity.name)"
newEntities.append(newEntity)
}
let newManagedObjectModel = NSManagedObjectModel()
newManagedObjectModel.entities = newEntities
return newManagedObjectModel
}()
You need to add one line to you Entity.swift file to make it also an Objective-C class like this:
#objc(YourEntity)
class YourEntity: NSManagedObject {
...
}
I think this as a bug if your project does not contains any objective-c code. However, you need to add that line until this fixed.
I learned it from here.
Youtube video at 11:45
Here is an updated answer that actually worked for me. Using XCode 9.2
Ok, I figured it out! It's not exactly intuitive though. First, I had to comment out the #objc(EntityName) line in the NSManagedObject subclasses. Then I went back to the default Module defined in the xcdatamodeld (Current Project Module). Then I had to ensure that only my test classes (and NONE of the NSManagedObject subclasses or any other source files) were compiled as part of my test project (this was critical) and #testable import AppName in my test project class files. Now it never trys to use these classes in the AppNameTests module, only AppName and the test projects can only call public or internal code...therefore all is right in the universe, yay!

Resources