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

Cocoapod tips

check installed cocoapod plugin

gem list --local | grep cocoapods

create pod

pod spec create POD_NAME

https://eladnava.com/publish-a-universal-binary-ios-framework-in-swift-using-cocoapods/

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  

https://s3.amazonaws.com/elasticbeanstalk-us-east-1-564874457370/SwiftKit.zip

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

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

reference:

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

遗留问题:

Pod lint fails when containing dynamic-frameworks without simulator architectures

Posted in iOS, Mobile | Tagged | Leave a comment