Posts from the “iOS” Category

Migration Tips of Large-Scale iOS Project from Circle 1.0 to 2.0


  • The project uses Circle 1.0, i.e. has a circle.yml file on the root directory of the project

Execution Tips

  • How to obtain the ymal file of Circle 2.0, .circleci/config.yml
    • Use the 1.0 to 2.0 config-translation Endpoint to convert circle.yml to .circleci/config.yml. This is the most efficient way to get the correct yaml configuration of the project on Circle 2.0, because it accurately turns all implicit configurations in Circle 1.0 to explicit ones in Circle 2.0. Since Circle 2.0 removes support of implicit configurations, it’s hard to manually write the yml file for Circle 2.0 as it requires being aware of all implicit configurations on Circle 1.0, which could be 10 times slower than the endpoint
  • Code signing
    • “fastlane match”: Circle 2.0 only uses “fastlane match” for code signing, and, certificates and provisioning profiles uploaded to Circle 1.0 are banned on Circle 2.0. For this reason, it’s critical to validate all certificates and provisioning profiles on the git repo associated with “fastlane match” to correctly code signing
    • Keychain: Circle 2.0 uses a temporary keychain, “fastlane_tmp_keychain”, to interact with certificates and provisioning profiles fetched from the git repo associated with “fastlane match”, but it doesn’t have a way to delete the temporary keychain (“fastlane_tmp_keychain” is set by “setup_circle_ci” in the “before_all” lane of Fastfile). Although Circle suggests to call “setup_circle_ci” on the “before_all” lane, it leads to “duplicated keychain” error if two lanes are triggered in one job by “.circleci/config.yml”. For this reason, I suggest to call “setup_circle_ci” in the lane that does code signing to prevent the temporary keychain from being created multiple times when multiple lanes are triggered in one job
  • Minimize debugging time during migration
    • The biggest challenge during migration is debugging failed circle builds. It’s challenging because the long error log looks really scary – what errors are false positives? what errors actually matter? how to minimize the build time of the fix of an error on Circle? Look back to my painful experience of the migration, the following tips are ones I’d suggest to make life happier when doing migration:
      • Focus on the errors printed out, and get more details of logs of error to find the cause. An efficient way of getting detailed log is ssh into the build. For example, if it’s a code signing error, the first thing to check is to validate the certificate and provisioning profiles on the git repo associated with “fastlane match”, as well as code signing configurations of project settings. Trivial errors on the circle log, such as “invalid T” associated with circle machine goes after
      • Test lane locally. If a lane causes error, it’s faster to test locally than pushing to circle to run
      • Comment out unnecessary code. When fixing a particular error, a big time saver is commenting out unnecessary code of the error to save dev time

Highlights of Dynamic Constraints

Dynamic constraints could be efficient and powerful if we get it work correctly. In other words, we need to make sure:

  • Do the deactivation/activation of constraints at the right place.
  • Try to avoid frequent view hierarchy changes (addSubview, removeFromSuperview).
  • Make sure active constraints don’t have conflicts.

Let’s dive into details of the three aspects:

Do the deactivation/activation of constraints at the right place:

For a view, `updateConstraints` is where the system gets the constraints for subviews and is efficient to apply constraint changes. To trigger `updateConstraints`, call `setNeedsUpdateConstraints` wherever the constraints need to be changed.

Try to avoid frequent view hierarchy changes (addSubview, removeFromSuperview):

Frequent view hierarchy changes requires recalculation of the layout, which is not efficient for displaying the views, and could cause update of the view not visually smooth.

Make sure active constraints don’t conflict:

Although iOS automatically deactivating conflicted constraints based on some magic in the system, it’s better to not contain conflicted constraints in the first place. Because older iOS versions may not be as intelligent as the newer ones dealing with constraints conflicts, and it could results in unexpected layout.

Weakify Self

Note: weakifying self in a block has a few reasons.

If the block is retained by self, weakifying self helps avoiding retain cycle.

Otherwise, if the block is not retained by self, WE STILL WANNA WEAKIFY SELF because we don’t wanna anything unknown to retain self especially when self is complicated (i.e. view controller). Retaining self by in a block may ultimately delay the release of the instance of type to which self belongs.

Conclusion is, always weakify self in blocks. But remembering the reasons is important to avoid not weakifying when some reasons are forgotten.

Convert NSData to NSString

NSData -> NSString:

Ignore stringWithUTF8String and use initWithData:encoding:NSUTFStringEncoding because stringWithUTF8String expects a NUL terminated buffer whereas NSData doesn’t guarantee that. Invalid NSData could cause a nil result using stringWithUTF8String.

NSDate Formatting Convenience

Go to the “NSDateFormatter” to preview the result of your format:

And refer to the following table to construct the format:

iPhone NSDateFormatter date formatting table