Git tips

How do I discard changes in my working copy that are not in the index?

git stash save --keep-index --include-untracked

You don't need to include –include-untracked if you don't want to be thorough about it.

or drop the stash

git stash drop

or all unstaged files in current working directory use:

git checkout -- .

For a specific file use:

git checkout -- path/to/file/to/revert

Add alias:

Here is what alias I have:

# ~/.gitconfig
[alias]
    squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"
    last = log -1 HEAD
    fb = "!f() { git branch -a | grep ${1} --color=auto; }; f"
    graph = "!f() { git log --graph --date=relative --pretty=tformat:'%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ad)%Creset'; };f"
    cb = "!f() { git branch -a | grep -m1 -e ${1}.*${2} --color=auto | xargs git checkout; }; f"

git squash

You can add alias by:

$: git config --global alias.squash '!f(){ git reset --soft HEAD~${1} && git commit --edit -m"$(git log --format=%B --reverse HEAD..HEAD@{1})"; };f'

You will the command you add in ~/.gitconfig

squash = "!f(){ git reset --soft HEAD~${1} && git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\"; };f"

e.g: squash the last 5 commits to 1 commit:

git squash 5

Reference

git find branch

Find the branch whose name includes some keyword

fb = "!f() { git branch -a | grep ${1} --color=auto; }; f"

e.g: find a branch whose name includes certificate:

$: git fb certificate
remotes/origin/bugfix/PGIA-571-fix-certificates

git find branch and checkout

Find the branch name whose name includes some keword:

cb = "!f() { git branch -a | grep -m1 -e ${1}.*${2} --color=auto | xargs git checkout; }; f"

e.g: Find and checkout the branch whose name includes bugfix and 754

$: git cb bugfix 754
Switched to branch 'bugfix/PGIA-754-cannot-close-game'
Your branch is behind 'origin/bugfix/PGIA-754-cannot-close-game' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
$:(bugfix/PGIA-754-cannot-close-game) ✗
Posted in git | Tagged | Leave a comment

Views vs. layers

A layer is a simple model class that exposes a number of properties to represents some image-based content. Every UIView is backed by a layer, so you can think of layers as the lower-level behind the scenes class behind your content.
A layer is different from a view (with respect to animations) for the following reasons:
A layer is a model object – it exposes data properties and implements no logic. It has no complex Auto Layout dependencies nor does it handle user interactions.
It has pre-defined visible traits – these traits are a number of data properties that affect how the contents is rendered on screen, such as border line, border color, position and shadow.
Finally, Core Animation optimizes the caching of layer contents and fast drawing directly on the GPU.
To compare views and layers side by side:

Views

  • Complex view hierarchy layouts, Auto Layout, etc.
  • User interactions.
  • Often have custom logic or custom drawing code that executes on the main thread on the CPU.
  • Very flexible, powerful, lots of classes to subclass.

Layers

  • Simpler hierarchy, faster to resolve layout, faster to draw.
  • No responder chain overhead.
  • No custom logic by default. and drawn directly on the GPU.
  • Not as flexible, fewer classes to subclass.

If you need to choose between views and layers here is tip: choose view animations any time you can to do the job; you will know when you need more performance or flexibility and have to switch to layer animations instead.

Posted in iOS, Mobile | Tagged | Leave a comment

Xcode tips and ticks

Faster Testing

When using TDD you can work more quickly by running a subset of tests – press ⌃⌥⌘G to re-run only your last test, or ⌘-click several tests to run only them.

Opening Xcode

If you’re in a folder that has both a workspace and a project, use xed . to open the workspace in Xcode. If there were only a project, that would have been opened instead.

Generated interfaces

If you're looking at a Swift data type and just want a summary of what it does, press ⌃⌘↑ to have Xcode generate an interface showing only its external properties and methods.

Filtering the jump bar

If you find Xcode's jump bar a little long, try filtering it. After pressing ⌃6 to bring up the jump bar, you can start typing a few letters to filter all your properties and methods using a fuzzy search.

Clearing up space

If you’ve been using Xcode for a few years, you should run xcrun simctl delete unavailable to remove any old simulators that are no longer supported. It's common to save 20-60GB with that one command!

Watchpoints

When your program is paused, right-click on a variable and choose Watch. The next time that variable changes, you can step back one frame to see where it happened.

Identifying constraints

It’s common to see lots of output in Xcode’s log when you have an Auto Layout problem, but if you add identifiers to your constraint you’ll get more helpful messages – easy to do in both Interface Builder and code!

Measuring build time

If you've ever wondered how long it takes for Xcode to build your project, you can find out with one Terminal command.

defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES

Show build time

defaults write com.apple.dt.Xcode ShowBuildOperationDuration -bool YES
Posted in iOS, Mobile | Tagged | Leave a comment

gitsubree usage

Basic commands about subtree

git subtree add   --prefix=<prefix> <commit>
git subtree add   --prefix=<prefix> <repository> <ref>
git subtree pull  --prefix=<prefix> <repository> <ref>
git subtree push  --prefix=<prefix> <repository> <ref>
git subtree merge --prefix=<prefix> <commit>
git subtree split --prefix=<prefix> [OPTIONS] [<commit>]

1. Add subtree

For example

git remote add -f ios git@github.com:zhihuitang/stockholm-rail-ios.git

-f: Pull immediately after adding
Add the library’s remote as if it was my own. This will significantly simplify the commands, so that you don’t have to specify the repository’s address all the time

git subtree add --prefix=ios ios master

Use subtree add to add that repo’s code into a path in the parent’s project, specified by prefix. The last parameter, master, is the branch you are pulling code from (my-subtree/master).

2. Pull sub-repo from remote

git subtree pull —prefix=ios ios master [—-squash]

Pull master branch of sub-repo from remote.

3. Push sub-repo to remote

If you changed something in sub-repo folder and want to push the changes to remote sub-repo, you can do as follows:

git subtree push --prefix=ios ios master [--squash]
Posted in git | Tagged | Leave a comment

CommentPlus for Swift Func

Usage

reference: https://zhihuitang.github.io/2019/01/13/CommentPlus-for-Swift-Func.html

  • Download the Xcode extension from here
  • Unzip and double click the extension CommentPlus.app to install the extension
  • In Xcode, put the cursor on the upper line of func, then select Xcode menu bar:
    Editor -> CommentGenerator -> Generate Func Comment:

comment

Posted in iOS, Mac | Tagged | Leave a comment

Charles Proxy in Android

1. SSL Proxy/Charles + Android trouble

https://stackoverflow.com/questions/17823434/ssl-proxy-charles-and-android-trouble/31945622#31945622

2. Enable SSL Proxy for Nougat+

Update your AndroidManifest.xml application section with networkSecurityConfig.xml

<application android:name=”AppName” 
 android:icon=”@mipmap/ic_launcher” 
 android:label=”@string/app_name”    
 android:networkSecurityConfig=”@xml/network_security_config”>

Add network_security_config.xml file to your xml resource folder app/src/main/res/xml/network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <base-config>
    <trust-anchors>
      <certificates src="system" />
      <certificates src="user" />
    </trust-anchors>
  </base-config>

  <!-- For debug only -->
  <debug-overrides>
    <trust-anchors>
      <certificates src="system" />
      <certificates src="user" />
    </trust-anchors>
  </debug-overrides>
</network-security-config>
Posted in Android | Tagged | 2 Comments

Intermediate iOS Debugging

1. Stop when exception

1.1 Open Breakpoint navigator (Cmd + 8)
1.2 Add Exception Breakpoint

1.3 Keep default values

1.4 Move breakpoint to User level

2. Edit breakpoint

  • Edit Breakpoint

  • Add Debugger Command:

    We will see the following output:

7.89903245455977
7.890427382096
7.87502883137136
7.87004694731339
  • or Add Log Message

ScreensFromBottom: @screensFromBottom@ Threshold: @screensFromBottomToLoadMoreCats@

We will see the logs in output window:

ScreensFromBottom: 8.5084718344868104 Threshold: 2.5
ScreensFromBottom: 8.5034899504288379 Threshold: 2.5
ScreensFromBottom: 8.499866762023041 Threshold: 2.5
ScreensFromBottom: 8.4966964721679688 Threshold: 2.5
ScreensFromBottom: 8.4948848779650703 Threshold: 2.5
ScreensFromBottom: 8.4921674866607226 Threshold: 2.5
ScreensFromBottom: 8.4899029939070996 Threshold: 2.5
ScreensFromBottom: 8.4880913997042011 Threshold: 2.5

3. Symbolic breakpoint – Condition

For example, check if some specified UIViewController is released properly.

  1. Add Symbolic Breakpoint...

  2. (Optional) Add Condition:

Check if it is UIViewController:

(BOOL)[$arg1 isKindOfClass: isKindOfClass: [UIViewController class]]

Check if it is user's custom CatDetailViewController:

(BOOL)[$arg1 isKindOfClass: (id)NSClassFromString(@"Catstagram.CatDetailViewController")]

3. Symbolic breakpoint – Action

Print the class name when UIView released:

We will see the following output:

<_UIVisualEffectSubview: 0x7f9e6e41aea0; frame = (0 0; 414 64); autoresize = W+H; userInteractionEnabled = NO; layer = <CALayer: 0x60000003e300>>
<UIImageView: 0x7f9e70334b60; frame = (0 64; 414 0.333333); userInteractionEnabled = NO; layer = <CALayer: 0x604000030f60>>
<UIView: 0x7f9e7030f210; frame = (0 0; 414 0); layer = <CALayer: 0x6040000312a0>>
<UIImageView: 0x7f9e70334930; frame = (0 0; 414 64); userInteractionEnabled = NO; layer = <CALayer: 0x604000031140>>
<UINavigationTransitionView: 0x7f9e6e41ddc0; frame = (0 0; 414 736); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x60000003c340>>
<UIViewControllerWrapperView: 0x7f9e70004550; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x60000003e8c0>>
<UIView: 0x7f9e6e51c850; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x60c00003b040>>

4. LLDB

LLDB is Apple’s “from the ground up” replacement for GDB, developed in close coordination with the LLVM compilers to bring you state-of-the-art debugging with extensive capabilities in flow control and data inspection. Starting with Xcode 5, all new and preexisting development projects are automatically reconfigured to use LLDB.

reference: http://lldb.llvm.org/lldb-gdb.html

4.1 p/po

4.2 frame variable(fr v)

frame variable is only for printing the contents of variables, no side effect to variable

(lldb) frame var global
(int32_t) global = 5

example:

(lldb) p screensFromBottom
(CGFloat) $R1 = 9.3780370518781133
(lldb) po screensFromBottom
9.37803705187811

(lldb) frame variable screensFromBottom
(CGFloat) screensFromBottom = 9.3780370518781133
(lldb) fr v screensFromBottom
(CGFloat) screensFromBottom = 9.3780370518781133
(lldb) 

4.3 Variable out of scope

Create a variable out of debug session scope by using $:

(lldb) p let $label = cell.titleLabel
(lldb) p print($label.text!)

5. Changing UI when debugging

run CATransaction.flush(), flush pending changing to the UI:

(lldb) p titleLabel.text = "Hello lldb"
(lldb) p CATransaction.flush()

6. Get memory address of return value of a function:

(lldb) register read $rax
rax = 0x000062401232eb99a

Print out the memory in LLDB:

(lldb) po unsafeBitCast(0x000062401232eb99a, to UIImage.self)
<UIImage: 0x000062401232eb99a>, {40, 40}

(lldb)
Posted in iOS, Mobile | Tagged | Leave a comment

Auto Layout

let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false

Interface Builder will automatically set its value to false if the view has constraints defined. But for the views created in code, it defaults to true.

UIView has a property called auto resizing mask, but its type is UIView auto resizing.

var autoresizingMast: UIViewAutoresizing { get set }

Hugging & Compression

Intrinsic size for a view is whatever size will exactly fit its size of wear content.

  • Content Hugging: Don't grow

  • Compression Resistance: Don't shink

Autolayout formula

attribute 1 = multiplier * attribute 2 + constant

https://www.raywenderlich.com/162311/adaptive-layout-tutorial-ios-11-getting-started

Xcode provides two size classes: Regular and Compact. Although they are related to the physical dimensions of a view, they also represent the semantic size of the view.

The following table shows how the size classes apply to the different devices and orientations:

AutoLayout in TableView

tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 140

When you set the rowHeight as UITableViewAutomaticDimension, the table view is told to use the Auto Layout constraints and the contents of its cells to determine each cell’s height.

In order for the table view to do this, you must also provide an estimatedRowHeight. In this case, 140 is just an arbitrary value that works well in this particular instance. For your own projects, you should pick a value that better conforms to the type of data that you’ll be displaying.

Posted in iOS | Tagged | Leave a comment

Key-Value Observing

Key-value observing is a mechanism that allows objects to be notified of changes to specified properties of other objects. You can use key-value observing with a Swift class, as long as the class inherits from the NSObject class. You can use these two steps to implement key-value observing in Swift.

  1. Add the dynamic modifier and @objc attribute to any property you want to observe. For more information on dynamic, see Requiring Dynamic Dispatch.
class MyObjectToObserve: NSObject {
    @objc dynamic var myDate = NSDate()
    func updateDate() {
        myDate = NSDate()
    }
}
  1. Create an observer for the key path and call the observe(_:options:changeHandler) method like description on Apple doc. Or create a observer on observable(MyObjectToObserve) directly
var myContext = 0

class MyObserver: NSObject {
    @objc var objectToObserve: MyObjectToObserve
    var observation: NSKeyValueObservation?
    
    init(object: MyObjectToObserve) {
        objectToObserve = object
        super.init()
        objectToObserve.addObserver(self, forKeyPath: "myDate", options: .new, context: &myContext)
    }
    deinit {
        objectToObserve.removeObserver(self, forKeyPath: "myDate")
    }
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        //print("here , \(keyPath), \(object), \(change?[NSKeyValueChangeKey.newKey])")
        guard context == &myContext else { return }
        if let value = change?[NSKeyValueChangeKey.newKey] {
            print("Observation,  myDate changed: \(value)")
        }
    }
}

  1. Verifiy observer:
let observed = MyObjectToObserve()
let observer = MyObserver(object: observed)

observed.updateDate()
sleep(3)
observed.updateDate()

print("finished")

You will see output in Xcode debug area:

Observation:  myDate changed: 2018-01-30 18:15:41 +0000
Observation:  myDate changed: 2018-01-30 18:15:44 +0000
finished
Posted in iOS, Mobile | Tagged | 1 Comment

How to upload an App with universal framework to AppStore?

Cocoapods is a popular dependencies management tool for iOS development. We notice that in Cocoapods-v1.3, the framework MUST include simulator (x86_64, i386) architectures, otherwise the pod spec lint will fail and the pod cannot be uploaded to Cocoapods server. It shows error as follows:

xcodebuild: fatal error: lipo: -remove's specified would result in an empty fat file
xcodebuild: ld: warning: ld: warning: ignoring file SampleSDK/iOS/Sample.framework/Sample, missing required architecture x86_64 in file

Ref: Pod lint fails

BUT the Apps with framework including simulator architectures will not be accepted by AppStore.

The binary you uploaded was invalid
Unsupported Architecture. Your executable contains unsupported architecture '[x86_64, i386]

When we are trying to upload the App with architectures (x86_64, i386), it will show error as follows:

Generally if a framework including armv7,arm64 architectures only, the host App with the framework won't be installed in Simulators.

Achitecture Simulator Real Device Cocoapods AppStore
x86_64/i386
armv7/arm64
x86_64/i386+armv7/arm64

For simulator: x86_64/i386
For real device: armv7/arm64

So the ideal framework is a universal(x86_64/i386+armv7/arm64) one, the framework should be able to run in both simulator and real device, and also should be able to upload to AppStore.

Solution

The solution here is that we continue using universal framework. When archiving the host App, we remove the x86_64/i386 architectures in achieve/pre-actions, and restore x86_64/i386 architectures in achieve/post-actions.

Imaging we have a Cocoapods framework in our project. Usually the framework locates in

${SRCROOT}/Pods/YOUR-FRAMEWORK

1. Add script to Archive: pre-actions

Please remember to replace the framework name with your own framework name

2. Add script to Archive: post-actions

Please remember to replace the framework name with your own framework name

3. Done

Posted in iOS, Mobile | Tagged | 2 Comments