iOS using google MaterialComponents? - ios

I'm trying to use the MDCRaisedButton from MaterialComponents, added it as a pod dependency similar to all my other dependencies, but as I try to import it I get a compile error Module MaterialComponents not found
Are there any extra steps I need to take to use this pod?
I noticed in demos they use
pod 'MaterialComponents/Typography', :path => '../../'
What does path do? it gives error when i try to run pod update

:path isn't an addition you would need when using MaterialComponents in your own app, and is used for developing on top of local pods. Have a look here for more info: https://guides.cocoapods.org/using/the-podfile.html#using-the-files-from-a-folder-local-to-the-machine
In order to use MDCRaisedButton you would need to initially create a Podfile with pod 'MaterialComponents/Buttons' inside the chosen app target. If you are using swift as your language of development, I recommend also adding use_frameworks!. An example Podfile would look like this:
target 'Demo App' do
use_frameworks! # can remove if using Objective-C
pod 'MaterialComponents/Buttons'
end
After that, the import and usage would be:
Swift:
import MaterialComponents.MaterialButtons
let button = MDCRaisedButton()
Objective-C:
#import "MaterialButtons.h"
MDCRaisedButton *button = [[MDCRaisedButton alloc] init];
More info can be found here: https://github.com/material-components/material-components-ios/tree/develop/components/Buttons
As a side note, MDCRaisedButton will soon be deprecated and theming an MDCButton using the MDCContainedButtonThemer is now the best way to get the same raised button style. Therefore the current best practice of doing this is adding to your podfile:
pod 'MaterialComponents/Buttons+Extensions/ButtonThemer'
And then in your implementation:
Swift:
import MaterialComponents.MaterialButtons_ButtonThemer
let buttonScheme = MDCButtonScheme()
let button = MDCButton()
MDCContainedButtonThemer.applyScheme(buttonScheme, to: button)
Objective-C:
#import "MaterialButtons+ButtonThemer.h"
MDCButton *button = [[MDCButton alloc] init];
MDCButtonScheme *buttonScheme = [[MDCButtonScheme alloc] init];
[MDCContainedButtonThemer applyScheme:buttonScheme toButton:button];
More info can be found here: https://github.com/material-components/material-components-ios/blob/develop/components/Buttons/docs/theming.md
The added value of using theming and the scheme is that you can customize the button scheme and have that scheme apply to all your buttons at once. Moreover, if you want a certain color scheme and/or typography scheme throughout your app, the theming now allows these schemes to be applied to all of the material components within your app.

Related

Sharing CocoaPod using UIApplication.shared between iOS Extension and app

I have a custom built private CocoaPod that I wrote. I'm trying to use it in my iOS application, which is working fine. But when I add it to my iMessage App or Share Extension it fails and gives me an error 'shared' is unavailable: Use view controller based solutions where appropriate instead. when trying to use UIApplication.shared.
My first thought of how to fix this was to add a Swift Flag IN_EXTENSION or something like that. Then wrap the code in an #if block.
Problem is the target for the CocoaPod source is in some type of framework. The source is not part of the app or extensions directly. So adding that flag doesn't really help.
Below is an example of my Podfile.
source 'https://github.com/CocoaPods/Specs.git'
source 'git#github.com:CUSTOMORG/Private-CocoaPods-Spec.git'
platform :ios, '9.0'
use_frameworks!
inhibit_all_warnings!
target 'MyApp' do
pod 'MyCustomSwiftPackage', '1.0.0'
end
target 'MyApp Share Extension' do
pod 'MyCustomSwiftPackage', '1.0.0'
end
If I comment out the line pod 'MyCustomSwiftPackage', '1.0.0' under MyApp Share Extension it works fine. But if I leave it uncommented it fails.
I do need this package in my share extension tho.
I've thought about writing a separate pod that just handles the UIApplication.shared logic and adding that pod to the MyApp. But that seems like a real pain. Especially since I'm not aware of a way to deploy 2 CocoaPods in 1 project that rely on the same source files.
If that is the only solution it almost seems better to use Git Submodules and have the source directly in the app, so I can have it part of those targets directly and the #if SHOULD work then. Problem with that is the dependancies of the CocoaPod wouldn't be handled if I use Git Submodules. So I really have to use CocoaPods somehow.
I'd prefer a simple solution that doesn't feel as hacky as those ones. So is there a better way to handle this and fix that error without resorting to rewriting a TON of code, and that isn't a super hacky solution?
In the comments it was mentioned to use NSSelectorFromString with UIApplication.responds and UIApplication.perform. Problem with that is if Apple ever changes the API, the code will break, even for previous versions of the application since it is being called dynamically with no API future proofing. Although that solution sounds easy, it seems like a really bad decision.
The answer below looks very promising. Sadly after a few changes outlined in the comments, it still isn’t working, with the main application having both the Core subspec along with the AppExtension subspec.
Say you’re owner of MyLibrary:
Pod::Spec.new do |s|
s.name = "MyLibrary"
# Omitting metadata stuff and deployment targets
s.source_files = 'MyLibrary/*.{m,h}'
end
You use unavailable API, so the code conditionally compiles some parts based on a preprocessor macro called MYLIBRARY_APP_EXTENSIONS. We declare a subspec, called Core with all the code, but the flag off. We make that subspec the default one if user doesn’t specify one. Then we’ll declare an additional subspec, called AppExtension including all the code, but setting the preprocessor macro:
Pod::Spec.new do |s|
s.name = "MyLibrary"
# Omitting metadata stuff and deployment targets
s.default_subspec = 'Core'
s.subspec 'Core' do |core|
core.source_files = 'MyLibrary/*.{m,h}'
end
s.subspec 'AppExtension' do |ext|
ext.source_files = 'MyLibrary/*.{m,h}'
# For app extensions, disabling code paths using unavailable API
ext.pod_target_xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'MYLIBRARY_APP_EXTENSIONS=1' }
end
end
Then in your application Podfile you’ll link against Core in your main app target, and against AppExtension in your extension, like so:
abstract_target 'App' do
# Shared pods between App and extension, compiled with same preprocessor macros
pod 'AFNetworking'
target 'MyApp' do
pod 'MyLibrary/Core'
end
target 'MyExtension' do
pod 'MyLibrary/AppExtension'
end
end
That’s it!
Since you need access to UIApllication.shared only to get topViewController. You can't make it as dependency of your framework, that user needs to provide.
Let's declare provider and with precondition ensure that developer will not forget to setup this property:
protocol TopViewControllerProvider: class {
func topViewController() -> UIViewController
}
enum TopViewController {
static private weak var _provider: TopViewControllerProvider?
static var provider: TopViewControllerProvider {
set {
_provider = newValue
}
get {
precondition(_provider != nil, "Please setup TopViewController.provider")
/// you can make provider optional, or handle it somehow
return _provider!
}
}
}
Then in your app you could do:
class AppDelegate: UIApplicationDelegate {
func applicationDidFinishLaunching(_ application: UIApplication) {
...
TopViewController.provider = self
}
}
extension AppDelegate: TopViewControllerProvider { ... }
In extension you can just always return self.
Another way to get topViewController by any view:
extension UIView {
func topViewController() -> UIViewController? {
/// But I not sure that `window` is accessible in extension
let root = window?.rootViewController
....
}
}

IOS Module not found error when adding Objective-C framework projects using CocoaPods as a module in Swift project

I have scenario where i have couple of SDKs and a test app. Here is the example.
SDKCore - “Objective-C Framework”
SDKUI - “Objective-C Framework”
SDKCore is added as a dependency using Cocoapods pod 'SDKCore', :path => '../SDKCore' and with flag ‘use_frameworks’
SDKUIViewController uses methods from SDKCore. I’m importing like this #import SDKCore;
Code
#import "SDKUIViewController.h"
#import SDKCore;
#interface SDKUIViewController ()
#end
#implementation SDKUIViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[SDKClass hitTest];
self.view.backgroundColor = [UIColor redColor];
}
#end
SDKTestSwift
SDKCore is added as a dependency using Cocoapods pod 'SDKCore', :path => '../SDKCore' and with flag ‘use_frameworks’
SDKUI is added as a dependency using Cocoapods pod 'SDKUI', :path => '../SDKUI' and with flag ‘use_frameworks’
Problem
is when i compile SDKTestSwift I’m getting Module SDKCore not founda compile error on one of the files from SDKUI (See attached)
Really got stuck here. Thanks a lot in advance.
You can download the sample project from here.
Sorry, I actually have misunderstood you from the start, your issue lying in your pod, not project, this happen because you didn't state that the SDKUI depend on SDKCore, thats why you cant use any code from SDKCore
To fix this, simply add s.dependency 'SDKCore' in your SDKUI.podspec and run pod install again, then it works

PureLayout in Swift with Cocoapods

Using cocoapods-0.37.2 Here's my Podfile:
platform :ios, "8.0"
use_frameworks!
pod "GoogleMaps"
pod 'FontAwesomeKit'
pod 'PureLayout'
I'm trying to import PureLayout in my .swift file like this:
import PureLayout
class ViewController: UIViewController {
...
But I get error:
No such module 'PureLayout'
Why??? I thought that Cocoapods creates -Bridging-Header.h by itself when using use_frameworks! ?
I did not use the use_framework option but I used PureLayout in Swift with CocoaPods without issues.
My Podfile only has one line pod 'PureLayout'.
I created the <Your Module>-Bridging-Header.h file myself as instructed by Apple here. I added a line #import "PureLayout.h". I've also updated my target build settings to enter the bridging header file name as per instructions.
In the .swift file where I want to use PureLayout, I did not need to have import PureLayout. It would give me an error as you mentioned. The auto layout calls would just work, since it's defined through bridging header.
One side point - the test target would fail to compile if you add bridging header file to its build settings. So make sure the bridging header update is done on target not project level.
I guess the current README in PureLayout github page is a bit outdated, since it currently asks you to do import PureLayout in the .swift file. I added a comment in this README enhancement issue too.
install the podfile
open your .xcworkspace
put your import PureLayout statement in, get the error
delete the statement
clean your product
build your product
put your import PureLayout statement back in
This has been common occurence and fix for me when using outside frameworks.
spacemonkey's method work for me. In that case, you don't even need a -Bridging-Header.h file.
As for FontAwesomeKit, since it contains some resource file(xx.ttf, xx.otf, etc), you should add this file to project to make it work. Like following shows:
Clean your project and recompile, then you are done.
Following is my demo for this:
import FontAwesomeKit
import ChameleonFramework
import SnapKit
class ViewController: UIViewController {
#IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let iconSize : CGFloat = 128;
let codeIcon = FAKFontAwesome.codepenIconWithSize(iconSize);
imageView.image = codeIcon.imageWithSize(CGSizeMake(iconSize, iconSize));
view.backgroundColor = UIColor.flatGreenColorDark();
}
}
PS. Since you use swift, maybe the SnapKit Layout framework will much suitable than PureLayout.

Unable to integrate ZXingObjC in a iOS Swift Project

Im working on an iOS project, which shows the customer number in a barcode. I had installed the framework ZXingObjC with CocoaPods, described in GitHub.
I can compile my Project without errors. I can also use the classes of ZXingObjC in my Objective-C classes, without errors. After than, I have added the import Command #import <ZXingObjC/ZXingObjC.h> to my bridging header file, like my other custom objective-c classes, without compile errors. (I had testet the header file by destroying some import statements and got the expected file not found exception.)
But now, I can't use any class of ZXingObjC in my swift classes. I only got the following compile error: Use of undeclared type '...'. The Xcode autocomplete is not working, too.
e.g.
var test : ZXMultiFormatWriter?
>> Use of undeclared type 'ZXMultiFormatWriter'
I tried:
setup new project, same issue
checked header search path: $(SRCROOT)/Pods/Headers/Public/Adjust
reinstalled the ZXingObjC framework
checked build settings: Enable Modules: YES
checked build settings: Other Linker Flags: $(inherited) -ObjC
-framework "ZXingObjC"
checked linked binaries in the build phases: framework is added
checked import statement in the bridging header file (#import
<ZXingObjC/ZXingObjC.h> and #import "ZXingObjC/ZXingObjC.h" -- no
difference)
Windows style: restarting Xcode and Mac ;-)
I'm using:
Xcode: 6.3.2
CocoaPods: 0.37.2
Project Deployment target: iOS 8.0
SDK: 8.3
Does anyone know the problem? Can anyone help?
How can I make the ZXingObjC framework available in swift?
Actually it is an easy issue:
Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
pod 'ZXingObjC', '~> 3.1'
So, on terminal:
cd workspace
pod install
Then, once opened project on Xcode, you have to edit bridging-header adding ZXingObj:
#import <ZXingObjC/ZXingObjC.h>
Finally, in your swift classes that uses ZXingObjC, you have to import ZXingObjC.
import ZXingObjC
class ZXingObjCWrapper {
func encode() {
let writer = ZXMultiFormatWriter.writer()
....
}
}
The rest of the code for when you need to set an UIImage with this bar code:
func generateDataMatrixQRCode(from string: String) -> UIImage? {
do {
let writer = ZXMultiFormatWriter()
let hints = ZXEncodeHints() as ZXEncodeHints
let result = try writer.encode(string, format: kBarcodeFormatDataMatrix, width: 1000, height: 1000, hints: hints)
if let imageRef = ZXImage.init(matrix: result) {
if let image = imageRef.cgimage {
return UIImage.init(cgImage: image)
}
}
}
catch {
print(error)
}
return nil
}
The header search path was not correct in my project. The right values are:
$(inherited)
"${PODS_ROOT}/Headers/Public"
"${PODS_ROOT}/Headers/Public/ZXingObjC"
The second and third lines were not added by installation with CocoaPods.
EDIT: The installed framework have to be added to "Embedded Binaries" in General tab of the project.
I tried everything on this page to add ZXingObjC as a Pod. My goal was to generate an Aztec barcode.
I checked my Header Search Path. As Reddas said, I had to manually add "${PODS_ROOT}/Headers/Public/ZXingObjC". I also added ZXingObjC as an Embedded Binary (in the General Tab).
I checked my bridging file & all was good. I checked my view controllers where I wanted to generate the barcode. The import ZXingObjC was there.
No compile errors. But I can't declare a variable of ZXingObjC.
No luck. Any more suggestions?
EDIT - I went into the Targets, Build Settings and searched for Header Search Paths. I added in BOTH "${PODS_ROOT}/Headers/Public/ZXingObjC" and "${PODS_ROOT}/Headers/Private/ZXingObjC"
This seemed to unclog whatever broke. It works now. Strangely, I can now even delete those entries and it works.

No autocomplete on cocapods for AppCode 3.1

I have recently set up a project from a base XCode Project with the cocoapods installation, and when importing the project into AppCode 3.1, I am not getting any autocompletion for my swift frameworks that are installed through CocoaPods
Here is my Podfile
# Uncomment this line to define a global platform for your project
platform :osx, '10.10'
use_frameworks!
target 'Main' do
pod "SwiftyJSON", ">= 2.1.3"
pod 'BrightFutures', '~> 1.0.0-beta.3'
end
target 'MainTests' do
end
As you can see, I have 2 pods defined (one is SwiftyJson and one is BrightFutures). Both of these pods are installed as Swift Frameworks (hence why use_frameworks!) is there.
Everything appears to be imported and working correctly for AppCode (I have two projects, one is the main project, and the other one is Pods, which is the exact same as it is in XCode), however it appears that AppCode doesn't index any of the Pod frameworks, so I am not getting autocompletion status on using those frameworks, at all. As an example, in the following code
import Cocoa
import BrightFutures
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(aNotification: NSNotification) {
let f = future { 5 }.map{f in f + 7}
f.onSuccess{callback in println(callback)}
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}
I get no autocompletion on the following
import BrightFutures (importing anything else that isn't from CocoaPods works fine)
Auto complete on anything on this line let f = future { 5 }.map{f in f + 7} or this one f.onSuccess{callback in println(callback)}
Autocomplete works fine on anything else (i.e. aNotification) which is an argument on the applicationDidFinishLaunching function
Is there some setting in AppCode that I am missing, or is this something that hasn't been implemented yet? In XCode, autocompletion works completely correctly.
EDIT: I have also noticed that in AppCode, the .framework files are in red (not sure if this means anything?)

Resources