Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. It automates the process of checking Java code to spare humans of this boring (but important) task. This makes it ideal for projects that want to enforce a coding standard.
app/build.gradle
1 2 3 4 5 6 7 8 9 10 11 12 13 14
apply plugin:'checkstyle'
task checkstyle(type: Checkstyle) { description 'Check code standard' group 'verification'
configFile file('$project.rootDir/tools/checkstyle.xml') source 'src' include '**/*.kt' exclude '**/gen/**'
<!-- Checks for Naming Conventions --> <!-- See http://checkstyle.sourceforge.net/config_naming.html --> <modulename="MethodName"/> <modulename="ConstantName"/>
<!-- Checks for Imports --> <!-- See http://checkstyle.sourceforge.net/config_imports.html--> <modulename="AvoidStarImport"/> <modulename="UnusedImports"/>
<!-- Checks for Size --> <!-- See http://checkstyle.sourceforge.net/config_sizes --> <modulename="ParameterNumber"> <propertyname="max"value="6"/> </module>
<!-- other rules ignored for brevity --> </module> </module>
Android Studio provides a code scanning tool called lint that can help you to identify and correct problems with the structural quality of your code without your having to execute the app or write test cases
<!-- Disable the given check in this project --> <issueid="IconMissingDensityFolder"severity="ignore" /> <!-- Change the severity of hardcoded strings to "error" --> <issueid="HardcodedText"severity="error" /> </lint>
To make your app as small as possible, you should enable shrinking in your release build to remove unused code and resources. When enabling shrinking, you also benefit from obfuscation, which shortens the names of your app’s classes and members, and optimization, which applies more aggressive strategies to further reduce the size of your app
When you use Android Studio 3.4 or Android Gradle plugin 3.4.0 and higher, R8 is the default compiler that converts your project’s Java bytecode into the DEX format that runs on the Android platform
# Remove logs -assumenosideeffects class android.util.Log { public static boolean isLoggable(java.lang.String, int); public static int v(...); public static int i(...); public static int w(...); public static int d(...); public static int e(...); }
You don’t need to use ktlint or detekt to ensure that your code is formatted consistently. Simply enable “File is not formatted according to project settings” in the inspection settings.
dependencies { ktlint "com.pinterest:ktlint:0.32.0" // additional 3rd party ruleset(s) can be specified here // just add them to the classpath (e.g. ktlint 'groupId:artifactId:version') and // ktlint will pick them up }
The intention of a whitelist is that only new code smells are printed on further analysis. The blacklist can be used to write down false positive detections (instead of suppressing them and polute your code base).
React and React Native are just frameworks, and they do not dictate how we should structure our projects. It all depends on your personal taste and the project you’re working on.
In this post, we will go through how to structure a project and how to manage local assets. This of course is not written in stone, and you are free to apply only the pieces that suit you. Hope you learn something.
There is the ios folder for Xcode projects, the android folder for Android projects, and an index.js and an App.js file for the React Native starting point.
ios/
android/
index.js
App.js
As someone who has worked with native on both Windows Phone, iOS and Android, I find that structuring a project all comes down to separating files by type or feature
type vs feature
Separating by type means that we organise files by their type. If it is a component, there are container and presentational files. If it is Redux, there are action, reducer, and store files. If it is view, there are JavaScript, HTML, and CSS files.
Group by type
redux
actions
store
reducers
components
container
presentational
view
javascript
html
css
This way, we can see the type of each file, and easily run a script toward a certain file type. This is general for all projects, but it does not answer the question “what is this project about?” Is it news application? Is it a loyalty app? Is it about nutrition tracking?
Organising files by type is for a machine, not for a human. Many times we work on a feature, and finding files to fix in multiple directories is a hassle. It’s also a pain if we plan to make a framework out of our project, as files are spread across many places.
Group by feature
A more reasonable solution is to organise files by feature. Files related to a feature should be placed together. And test files should stay close to the source files. Check out this article to learn more.
A feature can be related to login, sign up, onboarding, or a user’s profile. A feature can contain sub-features as long as they belong to the same flow. If we wanted to move the sub feature around, it would be easy, as all related files are already grouped together.
My typical project structure based on features looks like this:
Besides the traditional files App.js and index.js and the ios1 and android folders, I put all the source files inside the src folder. Inside src I have res for resources, library for common files used across features, and screens for a screen of content.
As few dependencies as possible
Since React Native is heavily dependent on tons of dependencies, I try to be pretty aware when adding more. In my project I use just react-navigation for navigation. And I’m not a fan of redux as it adds unneeded complexity. Only add a dependency when you really need it, otherwise you are just setting yourself up for more trouble than value.
The thing I like about React is the components. A component is where we define view, style and behavior. React has inline style — it’s like using JavaScript to define script, HTML and CSS. This fits the feature approach we are aiming for. That’s why I don’t use styled-components. Since styles are just JavaScript objects, we can just share comment styles in library .
src
I like Android a lot, so I name src and res to match its folder conventions.
react-native init sets up babel for us. But for a typical JavaScript project, it’s good to organise files in the src folder. In my electron.js application IconGenerator, I put the source files inside the src folder. This not only helps in terms of organising, but also helps babel transpile the entire folder at once. Just a command and I have the files in src transpiled to dist in a blink.
babel ./src --out-dir ./dist --copy-files
Screen
React is based around components. Yup. There are container and presentational components, but we can compose components to build more complex components. They usually end in showing in the full screen. It is called Page in Windows Phone, ViewController in iOS and Activity in Android. The React Native guide mentions screen very often as something that covers the entire space:
Mobile apps are rarely made up of a single screen. Managing the presentation of, and transition between, multiple screens is typically handled by what is known as a navigator.
index.js or not?
Each screen is considered the entry point for each feature. You can rename the LoginScreen.js to index.js by leveraging the Node module feature:
Modules don’t have to be files. We can also create a find-me folder under node_modules and place an index.js file in there. The same require(‘find-me’) line will use that folder’s index.js file
So instead of import LoginScreen from ‘./screens/LoginScreen’ , we can just do import LoginScreen from ‘./screens’.
Using index.js results in encapsulation and provides a public interface for the feature. This is all personal taste. I myself prefer explicit naming for a file, hence the name LoginScreen.js.
Navigator
react-navigation seems to be the most popular choice for handling navigation in a React Native app. For a feature like onboarding, there are probably many screens managed by a stack navigation, so there is OnboardingNavigator .
You can think of Navigator as something that groups sub screens or features. Since we group by feature, it’s reasonable to place Navigator inside the feature folder. It basically looks like this:
import { createStackNavigator } from 'react-navigation'
import Welcome from './Welcome'
import Term from './Term'
const routeConfig = {
Welcome: {
screen: Welcome
},
Term: {
screen: Term
}
}
const navigatorConfig = {
navigationOptions: {
header: null
}
}
export default OnboardingNavigator = createStackNavigator(routeConfig, navigatorConfig)
library
This is the most controversial part of structuring a project. If you don’t like the name library, you can name it utilities, common, citadel , whatever…
This is not meant for homeless files, but it is where we place common utilities and components that are used by many features. Things like atomic components, wrappers, quick fixes function, networking stuff, and login info are used a lot, and it’s hard to move them to a specific feature folder. Sometimes we just need to be practical and get the work done.
In React Native, we often need to implement a button with an image background in many screens. Here is a simple one that stays inside library/components/ImageButton.js . The components folder is for reusable components, sometimes called atomic components. According to React naming conventions, the first letter should be uppercase.
Then somewhere in the src/screens/onboarding/term/Term.js , we can import by using relative paths:
import moveToBottom from '../../../../library/utils/move'
import ImageButton from '../../../../library/components/ImageButton'
This is a big red flag in my eyes. It’s error prone, as we need to calculate how many .. we need to perform. And if we move feature around, all of the paths need to be recalculated.
Since library is meant to be used many places, it’s good to reference it as an absolute path. In JavaScript there are usually 1000 libraries to a single problem. A quick search on Google reveals tons of libraries to tackle this issue. But we don’t need another dependency as this is extremely easy to fix.
The solution is to turn library into a module so node can find it. Adding package.json to any folder makes it into a Node module . Add package.json inside the library folder with this simple content:
{
"name": "library",
"version": "0.0.1"
}
Now in Term.js we can easily import things from library because it is now a module:
You may wonder what res/colors, res/strings , res/images and res/fonts are in the above examples. Well, for front end projects, we usually have components and style them using fonts, localised strings, colors, images and styles. JavaScript is a very dynamic language, and it’s easy to use stringly types everywhere. We could have a bunch of #00B75D color across many files, or Fira as a fontFamily in many Text components. This is error-prone and hard to refactor.
Let’s encapsulate resource usage inside the res folder with safer objects. They look like the examples below:
Like library , res files can be access from anywhere, so let’s make it a module . Add package.json to the res folder:
{
"name": "res",
"version": "0.0.1"
}
so we can access resource files like normal modules:
import strings from 'res/strings'
import palette from 'res/palette'
import images from 'res/images'
Group colors, images, fonts with palette
The design of the app should be consistent. Certain elements should have the same look and feel so they don’t confuse the user. For example, the heading Text should use one color, font, and font size. The Image component should use the same placeholder image. In React Native, we already use the name styles with const styles = StyleSheet.create({}) so let’s use the name palette.
Below is my simple palette. It defines common styles for heading and Text:
Here we use the object spread operator to merge palette.heading and our custom style object. This means that we use the styles from palette.heading but also specify more properties.
If we were to reskin the app for multiple brands, we could have multiple palettes. This is a really powerful pattern.
Generate images
You can see that in /src/res/images.js we have properties for each image in the src/res/images folder:
This is tedious to do manually, and we have to update if there’s changes in image naming convention. Instead, we can add a script to generate the images.js based on the images we have. Add a file at the root of the project /scripts/images.js:
The cool thing about Node is that we have access to the fs module, which is really good at file processing. Here we simply traverse through images, and update /src/res/images.js accordingly.
Whenever we add or change images, we can run:
node scripts/images.js
And we can also declare the script inside our main package.json :
Now we can just run npm run images and we get an up-to-date images.js resource file.
How about custom fonts
React Native has some custom fonts that may be good enough for your projects. You can also use custom fonts.
One thing to note is that Android uses the name of the font file, but iOS uses the full name. You can see the full name in Font Book app, or by inspecting in running app
for (NSString* family in [UIFont familyNames]) {
NSLog(@"%@", family);
for (NSString* name in [UIFont fontNamesForFamilyName: family]) {
NSLog(@"Family name: %@", name);
}
}
For custom fonts to be registered in iOS, we need to declare UIAppFonts in Info.plist using the file name of the fonts, and for Android, the fonts need to be placed at app/src/main/assets/fonts .
It is good practice to name the font file the same as full name. React Native is said to dynamically load custom fonts, but in case you get “Unrecognized font family”, then simply add those fonts to target within Xcode.
Doing this by hand takes time, luckily we have rnpm that can help. First add all the fonts inside res/fonts folder. Then simply declare rnpm in package.json and run react-native link . This should declare UIAppFonts in iOS and move all the fonts into app/src/main/assets/fonts for Android.
"rnpm": {
"assets": [
"./src/res/fonts/"
]
}
Accessing fonts by name is error prone, we can create a script similar to what we have done with images to generate a safer accession. Add fonts.js to our scripts folder
This step depends on personal taste, but I find it more organised if we introduce the R namespace, just like how Android does for assets with the generated R class.
Once you externalize your app resources, you can access them using resource IDs that are generated in your project’s Rclass. This document shows you how to group your resources in your Android project and provide alternative resources for specific device configurations, and then access them from your app code or other XML files.
This way, let’s make a file called R.js in src/res:
import strings from './strings'
import images from './images'
import colors from './colors'
import palette from './palette'
const R = {
strings,
images,
colors,
palette
}
export default R
Replace strings with R.strings, colors with R.colors, and images with R.images. With the R annotation, it is clear that we are accessing static assets from the app bundle.
This also matches the Airbnb convention for singleton, as our R is now like a global constant.
23.8 Use PascalCase when you export a constructor / class / singleton / function library / bare object.
In this post, I’ve shown you how I think you should structure folders and files in a React Native project. We’ve also learned how to manage resources and access them in a safer manner. I hope you’ve found it useful. Here are some more resources to explore further:
// Show something on top of other exportdefaultclassOverlayContainerextendsReact.Component<Props> { render() { const { behind, front, under } = this.props
This is a script to remove Cartography, and use plain NSLayoutAnchor syntax. Use Constraint.on() from Sugar. It will change all .swift files recursively under provided folder.
/// Turn every line into flatten transformed line functionhandleMatch(match) { let lines = match.split('\n') lines = lines.map((line) => { return handleLine(line.trim()) }).filter((transforms) => { return transforms != null })
let flatten = [].concat.apply([], lines) return flatten }
/// Check to handle lines, turn them into `LayoutAnchor` statements functionhandleLine(line) { const itemPattern = '\\w*\\.\\w* (=|<|>)= \\w*\\.*\\..*' const sizePattern = '\w*\.\w* (=|<|>)= (\s|.)*'
if (line.includes('edges == ')) { return handleEdges(line) } elseif (line.includes('center == ')) { return handleCenter(line) } elseif (hasPattern(line, itemPattern) && !hasSizeKeywords(line)) { return handleItem(line) } elseif (hasPattern(line, sizePattern)) { return handleSize(line) } elseif (line.includes('>=') || line.includes('>=')) { return line } elseif (line.includes('.')) { // return the line itself to let the human fix return line } elseif (line.length == 0) { return ['\n'] } else { returnnull } }
/// For ex: listView.bottom == listView.superview!.bottom - 43 /// listView.bottom == listView.superview!.bottom - Metrics.BackButtonWidth functionhandleItem(line) { const equalSign = getEqualSign(line) const parts = line.split(` ${equalSign} `) const left = parts[0]
let rightParts = parts[1].trim() let right = rightParts.split(' ')[0] let number = rightParts.replace(right, '').replace('- ', '-').trim() if (number.startsWith('+ ')) { number = number.slice(2) } let equal = getEqual(line)
functionsort() { const string = ` - Favorite WWDC 2017 sessions https://github.com/onmyway133/blog/issues/56 - Favorite WWDC 2018 sessions https://github.com/onmyway133/blog/issues/245 - How to do clustering with Google Maps in iOS https://github.com/onmyway133/blog/issues/191 `
Making splash screen with LaunchScreen.storyboard is now the default way to do in iOS. Testing it with UITests is a bit tricky as this screen is showed the system, and if we test that, we are just testing the system.
What we should test is the content we put in the LaunchScreen storyboard. Is it showing correctly on different screen sizes? Is it missing any texts or images?
One way to test that is via Unit Test. LaunchScreen storyboard always come with 1 UIViewController configured as an initial view controller
1 2 3 4 5 6 7 8 9 10 11 12
classLauncScreenTests: XCTestCase{ functestLaunchScreen() { let launchScreen = UIStoryboard(name: "LaunchScreen", bundle: nil) let viewController = launchScreen.instantiateInitialViewController()!
let label = viewController.view.subviews.compactMap({ $0as? UILabel }).first! XCTAssertEqual(label.text, "Welcome to my app")
let imageView = viewController.view.subviews.compactMap({ $0as? UIImageView }).first! XCTAssertNotNil(imageView.image) } }
Starting today, you can use any of the hundreds of community-curated themes on GitHub.com. To build your site with any public, GitHub-hosted theme, add the following to your site’s _config.yml file:
This year I failed the lottery ticket to WWDC, and I also missed the keynote live stream because I was sailing on the Christian Radich outside Oslo fjord that day. Luckily all the videos are available on Apple Developer site very shortly, and we can watch them now on Chrome or the unofficial WWDC app on macOS. I recommend the WWDC macOS app as it allows to mark favourites and filter, also offers the ability to adjust play speed to 1.25 or 1.5 saves me some time watching.
This year WWDC focuses a lot on privacy, stability, and speed, which are all I wish, so many thanks to Apple engineers who made that happen, and the resit to install the so called more stable iOS 12 is real. As an iOS engineers, I like to focus more about the necessary things to me, that is about the Swift programming language, new changes in Cocoa Touch, enhancements in Xcode and testing tricks. I also like to explore more aboutmachinelearning so I’m very glad that Apple is investing more into this technology with the introduction of Turi Create and Create ML.
To me, APIs come and get deprecated very often and it’s good to know them, but the most important thing is to invest in your programming, debugging and testing skill which you can apply in many other platforms.
Continued from last year favourites list, below are my favourite sessions with personal notes. Things are listed in no particular order. Hope you find it useful.
If you don’t have time, you should watch only this session. Platform State of the Union is like keynote for developers as it highlights important changes.
Privacy: Apple confirms on its commitment in privacy and security, also introduces password management feature and auto fill on iOS 12. Generating strong password, integrating with 3rd password management and quickly populating OTP field from SMS message have never been easier. GateKeeper gets some improvements as well and begins to require app to be notarised.
iOS 12: huge improvement in performance, Siri gets smarter with Shortcut support, group calling in FaceTime and grouped notification. Also for emoji fan, Memoji was introduced.
macOS 10.14 Mojave: more with Dark Mode. They demo mostly with Xcode in dark mode, which looks so cool. This year WWDC banner give hints about iOS and macOS cross-platform apps, which is partially true with Marzipan, a way to allow iOS apps to run on the mac.
Xcode 10: with improvements in code editing and source control changes bar indicator. Debugging with memory debug tool, LLDB performance enhancement and particular the new build system completely rewritten in Swift with parallel tasks are exciting news.
Swift 4.2: if you follow swift repo then Swift 4.2 may not be very surprising. There are also announcements for Swift 5 plan.
Machine Learning: is never hotter than this. This year we see huge investments in machine learning with Create ML, Turi Create, Natural Language frameworks, CoreML 2, new detection capabilities in Vision.
ARKit 2, watchOS 5, tvOS 12, AppStore Connect and AppStore Connect APIs are some other important news you don’t want to miss.
Together with this session, I recommend you to read What’s new in Swift 4.2 summary which is very succinct. Besides improvement in complication and runtime, Swift 4.2 offers some new features: iterable enum case, synthesised Equatable and Hashable, handy functions for shuffling, random generating. To me, the need to explicitly handle Implicitly unwrapped optional is also a reasonable change.
This is a brief introduction to all changes coming to iOS 12, together with tips on how to be a good iOS citizen. Learn what can affect scrolling experience and prefetching technique, memory consumption and automatic backing stores, how to get the best from UIImage and UIImageView . AutoLayout engine got a lot of performance improvement so it won’t bother you any more. To me the most satisfying is to get all the UIKit notifications and classes reorganised under nested types, which makes code reasoning very easy.
I’ve written about Playground before and I’m very glad that Apple also invests a lot in it. The way people can interact and train model [Create ML](http://Introducing Create ML) in Playground is mesmerising. People may question how Playground works so well in session’s demos, but we can’t resist the new changes coming to Playground like Step by Step execution, markup rendering improvements and also how easy it is to consume custom frameworks. We can also now publish our own Playground through subscription.
Apple starts the machine learning trend last year with the introduction of Core ML. We might be excited and frustrated at the same time as Core ML is powerful but there’s no way we can customise it. Now the 2parts tell us how to implement custom layer and model, techniques to reduce model size like quantisation and flexible model. This makes the foundation for improvement in Vision in object tracking and the debut of Natural Language framework. Machine learning has never been easier.
I can’t miss any testing sessions as it is part of every day’s work. How can your program avoids regression bugs and ready for refactoring without any tests?
This session shows improvement in coverage and the introduction of xccov tool to help us build automation on top of coverage report. Parallel distributed testing in Xcode 10 can save us some time to have coffee. Another wonderful news is that tests have multiple order execution mode to avoid bugs due to implicit dependencies.
This is my most favourite. The session starts with a pyramid of tests with unit integration and end-to-end test analogy explanation, then to some very cool tips and tricks.
Testing network request: I like the separation of APIRequest and APIRequestLoader with URLSession , dependency injection with default parameter and the customisation of URLProtocol in URLSessionConfiguration
Testing notification: Notification is system wide and I try to avoid it as much as possible. This shows how to inject dependency with default parameter and use of own NotificationCenter instead of NotificationCenter.default to ease testing
Testing location: build abstraction with LocationProvider and LocationFetcher . How to use custom protocol and protocol for delegate to mock during test
Testing timer: how to use and mock RunLoop behaviour with Timer
LLDB has been improved to enable to debugging reliability experience, issues with AST context corruption, Swift type resolution are now things in the past. We can review how to use some common LLDB commands with handy arguments, and how to use Xcode breakpoint to its best.
I begin to use UICollectionView more than UITableView , and it also has same code as NSCollectionView,which is more comfortable solution than the horrible NSTableView .
Item size in UICollectionViewLayout : I often rely on UICollectionViewDelegateFlowLayout to specify item size but after watching this session, I feel like moving size related code to Layout object feels more like a good way to go
Mosaic layout: This is not new, but good to watch again. You learn how to implement custom layout using cached layout attributes
Data Source update: I didn’t expect Apple mentions this, but it is a good lesson on how UICollectionView handles batch update. I ‘ve written about this before in my A better way to update UICollectionView data in Swift with diff framework and that post gets a lot of attractions. In this session we need to remember that *ordering matters in data source update, but not in collection view update *❗️❗️❗️
Generic was a core feature of Swift since the beginning, we all know that it helps us to implement generic code that can work with many types in a type safe manner. This session reminds that I ‘ve never learned enough, especially the reasonable design behind it.
The sessions showcases Collection protocol and its protocol inheritances: MutableCollection , BidirectionalCollection , RandomAccessCollection and how they can be combined to provide generic functionalities for conformers. The associatedtype requirement in each protocol, especially Index and Element, is very minimal and has enough constraints for the protocol to implement lots of necessary functionalities in its protocol extension, which is eye opening for me. I very like to read open source, so looking at the source code for such protocols helps me understand more.
The part about Fisher Yates shuffle algorithm details how we can come up with necessary protocol while still make them generic
Pay attention to when they mention count and map , you can learn more how each concrete type can hook into the customisation point in protocol extension
Although Codable has a lot to offers in term of data integrity, this is good to know about to make sure the data you receive is actually the right data in correct format and structure. CommonCrypto is also part of new iOS SDK so you don’t need my Arcane library to handle encryption and hashing in your apps.
This is the most pleasant to watch as it is like a conversation between the speaker and the imaginary manager Crusty. Here I learn how to be aware of algorithm complexity and also how to utilise built in Foundation functions which are already optimised for performance.
After this session I can’t help myself but going to Swift repo to read the Algorithms.swift file immediately.
Learn how image encoding and decoding works through data and image buffer and how that affects memory and performance. There are techniques like downsampling that can tackle this problem. This also recommends against using backing store, and instead, use UIImageView
I’ve written about Turi Create before, but it is just scratching the surface of the many tasks offered by Turi. This year Apple releases Turi Create 5 with style transfer task, Vision Feature Print, GPU acceleration and recommender model improvements. I can’t wait to explore. And if you take a look at MLDataTable in Create ML framework, it looks like this has Turi ‘s SFrame under the hood.
That’s it. Thanks for reading. What are your favourite sessions this year? Please share in the comment section below
Do you know that questions about git get the most views on StackOverflow? I’ve searched a lot on Google how to execute certain actions with git, and this actually slowed me down a lot. There are some actions that we tend to use a lot, so it’s good to learn them. Here are my favorites, learning from friends and internet, hope you find them useful.
Before we begin, you should run git –version to check your current git version, mine is 2.12.2 as in macOS High Sierra. Here is the official git documentation, you can read details about git commands, parameters and new releases of git.
Create the branch on your local machine and switch in this branch:
git checkout -b branch_name
Create branch from commit:
git branch branch_name sha1_of_commit
Push the branch to remote:
git push origin branch_name
Rename other branch:
git branch -m old new
Rename current branch:
git branch -m new
Rename remote branch:
git branch -m old new # Rename branch locally
git push origin :old # Delete the old branch
git push --set-upstream origin new # Push the new branch, set local branch to track the new remote
Doing things in command line is cool and faster. However for viewing branches and commits, I find using a GUI client more visualizing and comfortable. You can see a list of all GUI clients here, I myself use SourceTree.
Check before you commit
We usually have some experiment code that we don’t want they to step into our commit. I usually mark my experiment with // but sometimes forget to unstage that.
Starting with 2.9, Git has improvement on its commit hook which makes it globally using hooksPath.
Firstly we nee to create a file called pre-commit, and place it into, for example, /Users/khoa/hooks:
if git rev-parse --verify HEAD >/dev/null 2>&1 then against=HEAD else # Initial commit: diff against an empty tree object against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 fi
# The special marker tag to mark things which we still need to change marker="<TEST>"
# Redirect output to stderr. exec 1>&2
iftest $(git diff --cached -z $against | grep $marker | wc -c) != 0 then cat <<\EOF Error: Still has invalid debug markers in code: EOF echo `git diff --cached -z $against -G $marker` exit 1 fi
In your project, run git config core.hooksPath /Users/khoa/hooks.
let replicatorLayer = CAReplicatorLayer() let animation = CABasicAnimation(keyPath: #keyPath(CALayer.opacity))
let line = CALayer() let lineCount: Int = 12 let duration: TimeInterval = 1.0 let lineSize: CGSize = CGSize(width: 20, height: 6) let lineColor: UIColor = UIColor.darkGray
let angle = CGFloat.pi * 2 / CGFloat(lineCount) let rotation = CATransform3DMakeRotation(angle, 0, 0, 1.0)
// x: // y: half the height, changing affects rotation of lines line.position = CGPoint(x: 48, y: 75)
line.add(animation, forKey: nil)
Pay attention to position of the line. The larger the x, the closer to center. y should be half the height of the replicator layer size, changing it affects the skewness of the line.
CAAnimation is about presentation layer, after animation completes, the view snaps back to its original state. If we want to keep the state after animation, then the wrong way is to use CAMediaTimingFillMode.forward and isRemovedOnCompletion
To make image in button smaller, use imageEdgeInsets with all positive values To have round and shadow, specify shadowOpacity, cornerRadius, shadowOffset
The name of the font isn’t always obvious, and rarely matches the font file name. A quick way to find the font name is to get the list of fonts available to your app, which you can do with the following code:
1 2 3 4
for family inUIFont.familyNames.sorted() { let names = UIFont.fontNames(forFamilyName: family) print("Family: \(family) Font names: \(names)") }
let navigationController1 = UINavigationController(rootViewController: viewController1) let navigationController2 = UINavigationController(rootViewController: viewController2) let navigationController3 = UINavigationController(rootViewController: viewController3)
Use tintColor instead of the deprecated selectedImageTintColor to indicate selected item color.
For icon size, check Tab Bar Icon Size, usually 50x50 for 2x and 75x75 for 3x
In portrait orientation, tab bar icons appear above tab titles. In landscape orientation, the icons and titles appear side-by-side. Depending on the device and orientation, the system displays either a regular or compact tab bar. Your app should include custom tab bar icons for both sizes.
In order for our prebuilt UI elements to function, you’ll need to provide them with an ephemeral key, a short-lived API key with restricted API access. You can think of an ephemeral key as a session, authorizing the SDK to retrieve and update a specific Customer object for the duration of the session.
client.makeJson(options: options, completion: { result in switch result { case .success(let json): completion(json, nil) case .failure(let error): completion(nil, error) } }) } }
Setting up STPCustomerContext and STPPaymentContext
finalclassMainController: UIViewController{ let client = EphemeralKeyClient() let customerContext: STPCustomerContext let paymentContext: STPPaymentContext
If we use stripe_id from card, which has the form of card_xxx, we need to include customer info
If we use token, which has the form tok_xxx, then no need for customer info
From STPPaymentResult
When you’re using STPPaymentContext to request your user’s payment details, this is the object that will be returned to your application when they’ve successfully made a payment. It currently just contains a source, but in the future will include any relevant metadata as well. You should pass source.stripeID to your server, and call the charge creation endpoint. This assumes you are charging a Customer, so you should specify the customer parameter to be that customer’s ID and the source parameter to the value returned here. For more information, see https://stripe.com/docs/api#create_charge
type ApplePayRequest struct { Token string`json:"token"` }
funchandleChargeUsingApplePay(w http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) var t ApplePayRequest err := decoder.Decode(&t) if err != nil { panic(err) }
params := &stripe.ChargeParams{ Amount: stripe.Int64(150), Currency: stripe.String(string(stripe.CurrencyUSD)), Description: stripe.String("Charge from my Go backend for Apple Pay"), } params.SetSource(t.Token) ch, err := charge.New(params) if err != nil { fmt.Fprintf(w, "Could not process payment: %v", err) fmt.Println(ch) w.WriteHeader(400) } w.WriteHeader(200) }
The PKPaymentAuthorizationController class performs the same role as the PKPaymentAuthorizationViewController class, but it does not depend on the UIKit framework. This means that the authorization controller can be used in places where a view controller cannot (for example, in watchOS apps or in SiriKit extensions).
client.useApplePay(payment: payment, completion: { result in switch result { case .success: completion(.init(status: .success, errors: nil)) case .failure(let error): completion(.init(status: .failure, errors: [error])) } }) } }
Showing Apple Pay option
From appleMerchantIdentifier
The Apple Merchant Identifier to use during Apple Pay transactions. To create one of these, see our guide at https://stripe.com/docs/mobile/apple-pay . You must set this to a valid identifier in order to automatically enable Apple Pay.
Requests payment from the user. This may need to present some supplemental UI to the user, in which case it will be presented on the payment context’s hostViewController. For instance, if they’ve selected Apple Pay as their payment method, calling this method will show the payment sheet. If the user has a card on file, this will use that without presenting any additional UI. After this is called, the paymentContext:didCreatePaymentResult:completion: and paymentContext:didFinishWithStatus:error: methods will be called on the context’s delegate.
Use STPPaymentOptionsViewController to show cards and Apple Pay options
funcpaymentOptionsViewController(_ paymentOptionsViewController: STPPaymentOptionsViewController, didSelect paymentOption: STPPaymentOption) { // No op }
After user selects payment option, the change is saved in dashboard https://dashboard.stripe.com/test/customers, but for card only. Select Apple Pay does not reflect change in web dashboard.
Apple pay option is added manually locally, from STPCustomer+SourceTuple.m 😲
STPApplePayPaymentOptionis not available inpaymentContext.paymentOptions` immediately
Change selected payment option
In STPPaymentContext
setSelectedPaymentOption is read only and trigger paymentContextDidChange, but it checks if the new selected payment option is equal to existing selected payment option
Which in turns call STPCustomerEphemeralKeyProvider. As stripe does not save Apple Pay option in dashboard, this method return list of card payment options, together with the default card as selected payment option 😲
Although the new STPCard has a different address, it is the exact same card with the same info, and the isEqual method of STPCard is
Need to use great timeout value as DispatchQueue is not guaranteed to be precise, a block needs to wait for the queue to be empty before it can be executed
Make expectation less cumbersome
1 2 3 4 5 6 7 8 9 10 11
extensionXCTestCase{ funcwaitOrFail(timeout: TimeInterval) { let expectation = self.expectation(description: #function)
The command line tools will search the SDK for system headers by default. However, some software may fail to build correctly against the SDK and require macOS headers to be installed in the base system under /usr/include. If you are the maintainer of such software, we encourage you to update your project to work with the SDK or file a bug report for issues that are preventing you from doing so. As a workaround, an extra package is provided which will install the headers to the base system. In a future release, this package will no longer be provided. You can find this package at:
To make sure that you’re using the intended version of the command line tools, run xcode-select -s or xcode select -s /Library/Developer/CommandLineTools after installing.
Run
1
open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
# must be unique in a given SonarQube instance sonar.projectKey=my-app # this is the name and version displayed in the SonarQube UI. Was mandatory prior to SonarQube 6.1. sonar.projectName=My App sonar.projectVersion=1.0 # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. # This property is optional if sonar.modules is set. sonar.sources=. # Encoding of the source code. Default is default system encoding #sonar.sourceEncoding=UTF-8
xcodebuild: error: ''MyApp.xcodeproj'' does not exist. 2019-04-29 12:10:17.486 defaults[4134:569992] Domain CFBundleShortVersionString does not exist .Extracting Xcode project informationxcodebuild: error: option 'Destination' requires at least one parameter of the form 'key=value'
👉 Remove quotes in sonar-project.properties 👉 Modify run-sonar-swift.sh, add these before Check for mandatory parameters section
11:01:14.406 INFO: Sensor JaCoCo XML Report Importer [jacoco] 11:01:14.409 DEBUG: No reports found 11:01:14.409 INFO: Sensor JaCoCo XML Report Importer [jacoco] (done) | time=3ms 11:01:14.409 INFO: Sensor SwiftLint [backelitesonarswiftplugin] 11:01:14.417 INFO: Sensor SwiftLint [backelitesonarswiftplugin] (done) | time=8ms 11:01:14.417 INFO: Sensor Tailor [backelitesonarswiftplugin] 11:01:14.418 INFO: Sensor Tailor [backelitesonarswiftplugin] (done) | time=1ms 11:01:14.418 INFO: Sensor OCLint [backelitesonarswiftplugin] 11:01:14.419 INFO: Sensor OCLint [backelitesonarswiftplugin] (done) | time=1ms 11:01:14.419 INFO: Sensor FauxPas [backelitesonarswiftplugin] 11:01:14.419 INFO: Sensor FauxPas [backelitesonarswiftplugin] (done) | time=0ms 11:01:14.419 INFO: Sensor Swift Squid [backelitesonarswiftplugin] 11:01:14.526 INFO: ------------------------------------------------------------------------ 11:01:14.526 INFO: EXECUTION FAILURE 11:01:14.526 INFO: ------------------------------------------------------------------------ 11:01:14.527 INFO: Total time: 6.180s 11:01:14.603 INFO: Final Memory: 25M/566M 11:01:14.603 INFO: ------------------------------------------------------------------------ 11:01:14.603 ERROR: Error during SonarQube Scanner execution java.lang.UnsupportedOperationException: Metric 'files' should not be computed by a Sensor at org.sonar.scanner.sensor.DefaultSensorStorage.saveMeasure(DefaultSensorStorage.java:168) ```
👉Install maven https://maven.apache.org/download.cgi Edit `ObjectiveCSquidSensor.java` and `SwiftSquidSensor`, remove line with `CoreMetrics.FILES` Run `export PATH=$PATH:/Users/khoa/apache-maven/bin` Run `./build-and-deploy.sh` Or `~/apache-maven/bin/mvn clean install`
🎉 Built jar is in `sonar-swift-plugin/target/backelite-sonar-swift-plugin-0.4.4.jar`, copy back to `extensions/plugins`
### How to enable SwiftLint as default profile 🤔
👉 Need to close current Sonar tab and restart server
### Testing failed: unable to attach DB
Modify `run-sonar-swift.sh` to add `-UseModernBuildSystem=NO` to `buildCmd+=(-destination`
14:53:23.251 ERROR: Error during SonarQube Scanner execution org.sonarsource.scanner.api.internal.ScannerException: Unable to execute SonarQube at org.sonarsource.scanner.api.internal.IsolatedLauncherFactory.lambda$createLauncher$0(IsolatedLauncherFactory.java:85)
1 2 3 4
👉Start sonar server
### LizardReportParser$SwiftFunction cannot be cast
Error during SonarQube Scanner execution java.lang.ClassCastException: com.backelite.sonarqube.swift.complexity.LizardReportParser$SwiftFunction cannot be cast to org.sonar.api.batch.fs.internal.DefaultInputComponent
1 2
👉 Run [lizard](https://github.com/terryyin/lizard) manually