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!


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


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:


Posted in iOS, Mac | Tagged | Leave a comment

Charles Proxy in Android

1. SSL Proxy/Charles + Android trouble


2. Enable SSL Proxy for Nougat+

Update your AndroidManifest.xml application section with networkSecurityConfig.xml

<application android:name=”AppName” 

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"?>
      <certificates src="system" />
      <certificates src="user" />

  <!-- For debug only -->
      <certificates src="system" />
      <certificates src="user" />
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:

  • 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>>


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


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

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

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}

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


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
        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)



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
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

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.


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


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

Cocoapod tips

check installed cocoapod plugin

gem list --local | grep cocoapods

create pod

pod spec create POD_NAME


Register an account by running the following, entering your full name and e-mail address:

pod trunk register you@email.com 'Full Name' 

create pod demo

pod lib create SwiftKit
pod spec lint --verbose

pod trunk push SwiftKit.podspec  


check pod info

you can use pod trunk info [pod] to get information on a pod and pod trunk me can be used to verify your local account.

pod trunk info RxSwift
pod trunk me

view trunk

pod trunk me

You can view your own registered information, including name, email, since, sessions, all Pods you submitted to Cocopods, etc.

Add more owners

You can add more owners to pod:

pod trunk add-owner crafttang crafttang@gmail.com


lipo is a usefull command, it is used to view framework's supported architectures, or merge/split framework.

1. -info

查看刚才编译的 Framework 库在 debug 和 release 下支持的框架:
$ lipo -info iddc-appstore.framework/iddc
Architectures in the fat file: iddc-appstore.framework/iddc are: armv7 arm64

15:50:02  ~/Downloads/lipo 
$ lipo -info iddc-universal.framework/iddc
Architectures in the fat file: iddc-universal.framework/iddc are: i386 armv7 x86_64 arm64

2. -create

上面生成的库,要么是只支持模拟器的,要么是只支持真机的,那么如何才能又能兼顾真机和模拟器呢? -create 使用方式:

lipo -create 库1 库2 -output 新库  

3. -thin

如果有一个 fat file 但是你不需要支持那么多框架,也可以通过拆分,为库瘦身, -thin 使用方式:

lipo 旧库 -thin 需拆分框架 -output 新库


一篇较为详细的 iOS动态静态库创建打包方法 总结


Pod lint fails when containing dynamic-frameworks without simulator architectures

Posted in iOS, Mobile | Tagged | Leave a comment

Hide implementation of swift framework when distributing

In this article, I will demonstrate how to create a Swift NiceLogger framework and push to Cocoapods without sharing source code.

You can download the final project from NiceLogger

1. Create a Swift framework

1.1 Create a new framework project

Product Name NiceLogger, Set language to Swift

1.2 Create a new file NiceLogger.swift

Copy the code from Here into NiceLogger.swift and save it.

1.3 Build to generate framework

Now build it and you will find the colour of the framework name turns to black, which means the framework has been generated.

1.4 Create Zip Archive

Right click the NiceLogger.framework, select Show in Finder, copy the NiceLogger.framework and MIT License to the NiceLogger home folder:

1.5 Create a Zip Archive

Archive them inside a .zip, upload it to your server, and link to it using the s.source parameter in the .podspec file.

Create the NiceLogger.zip file by running the following command in your project directory:

zip -r NiceLogger.zip LICENSE NiceLogger.framework  

You can upload the NiceLogger.zip file to your server, in a path similar to the following:


You can also create a GitHub repository and push the .zip file to it.

Once you upload the .zip, link it in the s.source parameter of the .podspec file.

  s.source            = { :http => 'https://s3.amazonaws.com/elasticbeanstalk-us-east-1-564874457370/NiceLogger.zip' }

2. Create Cocoapod repo

2.1 Create .podspec

Create a NiceLogger.podspec file in your project directory which will contain information about the CocoaPod you are publishing, for instance name, version, sources, and more.

pod spec create NiceLogger

To learn more about Podspec attributes, see http://docs.cocoapods.org/specification.html
To understand how to work with Podspecs in the CocoaPods, see https://github.com/CocoaPods/Specs/

for example:

Pod::Spec.new do |s|

  s.name         = "NiceLogger"
  s.version      = "0.0.1"
  s.summary      = "A nice logger tool in Swift."
  s.description  = <<-DESC
  A nice logger tool in Swift. Cheers

  s.homepage     = "https://github.com/zhihuitang"
  s.license      = { :type => "MIT", :file => "license" }
  s.author             = { "Zhihui Tang" => "crafttang@gmail.com" }
  s.ios.deployment_target = '8.0'
  s.ios.vendored_frameworks = 'NiceLogger.framework'
  s.source            = { :http => 'https://s3.amazonaws.com/elasticbeanstalk-us-east-1-564874457370/NiceLogger.zip' }
  s.exclude_files = "Classes/Exclude"


2.2 Ensure this is a valid .podspec

pod spec lint --verbose

If everything is working, you would see the following output:



   Testing with xcodebuild.
 -> NiceLogger (0.0.1)
    - NOTE  | [iOS] xcodebuild:  ld: warning: ignoring file NiceLogger/NiceLogger.framework/NiceLogger, file was built for x86_64 which is not the architecture being linked (i386): NiceLogger/NiceLogger.framework/NiceLogger

Analyzed 1 podspec.

NiceLogger.podspec passed validation.

2.3. Register a Trunk Account

In order to publish your .podspec file to the CocoaPods repository, first you must register an account with the CocoaPods Trunk.

The CocoaPods Trunk is an authentication and CocoaPods API service. For publishing new or update library to CocoaPods for public release, you will need to be registered with the Trunk and have a valid Trunk session on your current device.

Register an account by running the following, entering your full name and e-mail address:

pod trunk register you@email.com 'Full Name'  

Now, check your e-mail. There will be a confirmation link. Click it.

Now your Trunk account is activated and you can publish your CocoaPod!

2.4. Publish the Pod

Run the following command in the same directory as the .podspec to publish it to the CocoaPods repository:

pod trunk push NiceLogger.podspec  

The CLI will validate your .podspec and attempt to install the CocoaPod by downloading the source .zip and validating its contents. If works, you have just published your first universal binary CocoaPod!

If everything works, you would find the following output:

 🎉  Congrats

 🚀  NiceLogger (0.0.1) successfully published
 📅  October 14th, 23:14
 🌎  https://cocoapods.org/pods/NiceLogger
 👍  Tell your friends!

3. Create a Demo to verify the framework

3.1 Create a demo project

3.2 Init cocopods

pod init

Add dependency to Podfile

target 'Demo' do
  pod 'NiceLogger'

  # Pods for Demo

  target 'DemoTests' do
    inherit! :search_paths
    # Pods for testing

  target 'DemoUITests' do
    inherit! :search_paths
    # Pods for testing


3.3 install pod dependency

In the demo project home directory, run

pod install

yeah… we got it……, wait……

[!] Unable to find a specification for `NiceLogger`

In that case please fix it by:

pod repo update

Then run pod install again

pod install

if you are lucky again, you probably get following output:

$ pod install
Analyzing dependencies
Downloading dependencies
Installing NiceLogger (0.0.1)
Generating Pods project
Integrating client project

[!] Please close any current Xcode sessions and use `Test.xcworkspace` for this project from now on.
Sending stats
Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.

3.4 Code with NiceLogger

Navigate to the demo project, double the Demo.xcworkspace to open the demo project. You would see the source code the framework is invisible in Pods.

Now add code to verify the framework works.
Add dependency:

import NiceLogger

Add Code to viewDidLoad():

NiceLogger.d("hehe, viewDidLoad")

3.5 Run the demo project

You would find the log as follows:

2017-10-14T21:26:46.796Z [ViewController.swift#viewDidLoad()#16]-D: hehe, viewDidLoad


Many thanks to MR. Elad Nava, I borrowed some words form his blog:

Posted in iOS, Mobile | Tagged , | 7 Comments