How to hack iOS apps

Issue #19

We need to care about security nowadays, here are some links I find useful to read more about this matter

Detecting languages and framework

iOS Security

Private frameworks

Hack macOS apps

Private frameworks

Hacking Apple

Hack Android apps

How to deal with singleton in iOS

Issue #18

A single singleton

There are many classes that designed to be used as singleton, like UserDefaults.standard, FileManager.default, NotificationCenter.default or even our own classes like UserManager, Storage, … Singleton is a design patter and has its own use case, sometimes we still need to use it. But if we are to use singleton, we should just use 1, and group all other singleton under this single singleton. Thanks to Vadym for showing this to me

Swift makes it extremely easy to make singleton, let name it App then we have a single point of control for all the singletons

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct App {
static let model = AppModel()
static let realmProvider = RealmProvider()
static let networkingProvider = NetworkingProvider()
static var navigator = Navigator()
static let config = AppConfig()
static let pushNotificationCenter = PushNotificationCenter()
static let lifeCycle = LifeCycle()
}
```

These are use cases where a single instance is needed

### AppModel
This is where we store model for an app, that can be
- is onboarding shown
- organization name
- `Session` that encapsulates token, current profile

### LifeCycle
This is where we listen to app life cycle, I use `rx` to make it easy, see https://github.com/onmyway133/blog/issues/12

### RealmProvider
I prefer `Realm` for storing and caching, usually 1 `Realm` is enough. This is where we return the a certain `Realm` instance

```swift
class RealmProvider {
static func realm() -> Realm {
let configuration = Realm.Configuration(schemaVersion: App.config.schemaVersion)
return try! Realm(configuration: configuration)
}
}

AppConfig

This is where we have configurations for staging and production environment, those can be client key, Firebase configuration, analytics keys, …

I use Compass to do central navigation, and there should be 1 Navigator that does the job

Inject a singleton

Sometime we rely on a singleton to do our job, to make dependencies clear and testing easier, we need to inject this singleton, and leverage Swift default parameter, thanks to John for showing this to me

Here is an example of a ViewModel that relies on networking

1
2
3
4
5
6
7
8
9
10
11
12
13
class ProfileViewModel {

let networking: Networking<APIEndpoint>

init(networking: Networking<APIEndpoint> = App.networking) {
self.networking = networking

networking.rxRequest(APIEndpoint.profile)
.bindNext({ profile in
print(profile)
})
}
}

Swift snippets

Issue #17

I always forget how to write correct #available( or #if swift(>=3.0) or just lazy to write required init?(coder aDecoder: NSCoder) every time I make a subclass. That’s why I made SwiftSnippets to save time for these tedious tasks. Installation is easy with script, so you should give it a try.

I can’t recommend this enough, it saves me tons of time

What you don't know is what you haven't learned

Issue #14

Some of my sayings that I like 😉

Mine

  • There are so many many many things to learn
  • The opportunities are endless
  • Everybody has access to the internet. Everybody has nearly the same chances. Your future is yours to decide
  • A dream is not a dream anymore when you don’t have time for it
  • Being single means free time. Being coupled means memorable time
  • You learn less with the happy path
  • I don’t like to be at the centre, nor being abandoned
  • Even sense of humour can be trained
  • Youth is your strength. Take advantage of it
  • Easy to please. Hard to impress
  • Don’t show me the evil sides of the world
  • Please don’t confuse peace vs boredom
  • What matters is your passion, not your ability
  • The ones that are likely to fail are the ones having the fewest friends. And the ones that have the fewest friends are the ones having the most boring life
  • Life is like a marathon. People run. Some are lucky enough to have support. Some are even luckier, they already crossed the finish line when they were born. Running, however, has its own fun
  • Life is predestined. But you can of course change it
  • Losers are easily the most hot tempered. It is the consequence, not the cause
  • What if there is no inheritance. Will that make everybody the same?
  • Some people become Norwegian when they were born. Others have to apply for the citizenship https://www.udi.no/en/word-definitions/norwegian-by-birth/
  • Every deck has a chance to win, as long as you believe in the heart of the cards
  • Life is a game. People play on different difficulty levels. “But mine is much harder”, said everybody
  • What you don’t know is what you haven’t learned

Others

  • I don’t know the key to success. But the key to failure is trying to please everybody ~ Bill Cosby

Composition in Realm

Issue #13

There is time we have models that have some kind of inheritance, like Dog, Cat, Mouse can be Animal. We can use composition to imitate inheritance, we just need to make sure it has unique primary key

Cat and Dog

These are pretty much basic Realm objects

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Dog: Object {
dynamic var id: Int = 0

required convenience init(json: [String: Any]) {
self.init()
id <- json.integer(key: "id")
}
}

class Cat: Object {
dynamic var id: Int = 0

required convenience init(json: [String: Any]) {
self.init()
id <- json.integer(key: "id")
}
}

Animal

Here Animal can contain either dog or cat, we can add more if there are many other “inheritance” of Animal. The required init makes sure Animal can only be init with 1 type of child class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal: Object {
dynamic var id: Int = 0

dynamic var cat: Cat?
dynamic var dog: Dog?

required convenience init(cat: Cat) {
self.init()
self.cat = cat
self.id = cat.id
}

required convenience init(dog: Dog) {
self.init()
self.dog = dog
self.id = dog.id
}
}

How to do delegate with RxSwift

Issue #12

We can use DelegateProxy and DelegateProxyType to make beautiful delegate with RxSwift. But in some other cases, we can just create a custom class with PublishSubject.

This is how we can make rx out of UIApplication life cycle events

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class LifeCycle {
let didEnterBackground = PublishSubject<Void>()
let willEnterForeground = PublishSubject<Void>()
let didBecomeActive = PublishSubject<Void>()
let willResignActive = PublishSubject<Void>()

init() {
let center = NotificationCenter.default
let app = UIApplication.shared

center.addObserver(forName: Notification.Name.UIApplicationDidEnterBackground,
object: app, queue: .main, using: { [weak self] _ in
self?.didEnterBackground.onNext(())
})

center.addObserver(forName: Notification.Name.UIApplicationWillEnterForeground,
object: app, queue: .main, using: { [weak self] _ in
self?.willEnterForeground.onNext(())
})

center.addObserver(forName: Notification.Name.UIApplicationDidBecomeActive,
object: app, queue: .main, using: { [weak self] _ in
self?.didBecomeActive.onNext(())
})

center.addObserver(forName: Notification.Name.UIApplicationWillResignActive,
object: app, queue: .main, using: { [weak self] _ in
self?.willResignActive.onNext(())
})
}
}

Usage

1
2
3
4
5
6
let lifeCycle = LifeCycle()
lifeCycle.didBecomeActive
.bindNext({ [weak self] in
self?.viewModel.reloadProfile()
})
.disposed(by: bag)

How to group extension methods in Swift

Issue #11

Swift allows us to define more methods on existing class using extension.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
extension UIView {

func shake() {

}

func fade() {

}

func centerIn(anotherView: UIView) {

}
}

If you ‘re afraid of the naming conflict, you can prefix your methods, like

1
2
3
view.abc_shake()
view.abc_fade()
view.abc_centerIn(anotherView: containerView)

Or a better way, reverse it 💃 , like

1
2
3
view.animation.shake()
view.animation.fade()
view.layout.centerIn(anotherView)

This way, no more conflict and we make it clear that shake() and fade() belongs to animation category
Actually, animation and layout are properties in UIView extension. This may cause naming conflict, but the number of them is reduced

This is how it works

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
extension UIView {

struct Animation {
let view: UIView

func shake() {
// Shake the view
}

func fade() {
PowerfulAnimationEngine.fade(view)
}
}

var animation: Animation {
return Animation(view: self)
}

struct Layout {
let view: UIView

func centerIn(anotherView: UIView) {

}
}

var layout: Layout {
return Layout(view: self)
}
}

How to execute an action only once in Swift

Issue #10

There is time we want to execute an action only once. We can surely introduce a flag, but it will be nicer if we abstract that out using composition. Then we have

1
2
3
4
5
6
7
8
9
10
11
12
13
class Once {

var already: Bool = false

func run(@noescape block: () -> Void) {
guard !already else {
return
}

block()
already = true
}
}

Usage

1
2
3
4
5
6
7
8
9
10
11
class ViewController: UIViewController {
let once = Once()

override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)

once.run {
cameraMan.setup()
}
}
}

In the same way, we can check to run a closure when a value changes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
final class WhenChange<T: Equatable> {
private(set) var value: T

init(value: T) {
self.value = value
}

func run(newValue: T, closure: (T) -> Void) {
if newValue != value {
value = newValue
closure(value)
}
}
}

Law of Jante

Issue #9

The other day I was watching Inside Sweden’s Silicon Valley, and he mentions Law of Jante

It is pretty much this

  • You’re not to think you are anything special.
  • You’re not to think you are as good as we are.
  • You’re not to think you are smarter than we are.
  • You’re not to convince yourself that you are better than we are.
  • You’re not to think you know more than we do.
  • You’re not to think you are more important than we are.
  • You’re not to think you are good at anything.
  • You’re not to laugh at us.
  • You’re not to think anyone cares about you.
  • You’re not to think you can teach us anything.

This is controversial, there are many discussions about this

Putting on your black hat, it sounds negative. Putting on your yellow hat, it sounds positive
But what I learn about it is the team work. No one lives alone, everyone lives among the others. It is about to be humble and learn collaboration

Advices to students

Issue #8

Some say these are from the book Dumbing Down America of Charles Sykes, some say they are from Bill Gates ‘s speech to high school students. I don’t know the origin, but they are cool enough, so I tend to share it again and again

  • Life is not fair. Get used to it ⭐️
  • The world won’t care about your self-esteem. The world will expect you to accomplish something BEFORE you feel good about yourself
  • You will NOT make 40 thousand dollars a year right out of high school. You won’t be a vice president with car phone, until you earn both.
  • If you think your teacher is tough, wait till you get a boss. He doesn’t have tenure.
  • If you mess up, it’s not your parents’ fault, so don’t whine about your mistakes, learn from them.
  • Your school may have done away with winners and losers, but life may not. In some schools they have abolished failing grades and they’ll give you as many times as you want to get the right answer. This doesn’t bear the slightest resemblance to ANYTHING in real life.
  • Life is not divided into semesters. You don’t get summers off and very few employers are interested in helping you find yourself. Do that on your own time.
  • Television is NOT real life. In real life people actually have to leave the coffee shop and go to jobs.
  • Be nice to nerds. Chances are you’ll end up working for one.
  • Before you were born your parents weren’t as boring as they are now. They got that way paying your bills, cleaning up your room and listening to you tell them how idealistic you are. And by the way, before you save the rain forest from the blood-sucking parasites of your parents’ generation, try delousing the closet in your bedroom ⭐️

Reference

Check before you commit

Issue #7

Original post https://medium.com/@onmyway133/check-before-you-commit-5a7601cffc87


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

Create pre-commit file

Create a file called pre-commit, and place it into, for example /Users/khoa/hooks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#!/bin/sh

# https://appventure.me/2016/04/04/prevent-accidental-test-code-commits/

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

if test $(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

Apply the hook

In your project, run git config core.hooksPath /Users/khoa/hooks
That’s it. Whenever you commit a file with that pattern, it won’t let you commit

How to use

Try

1
2
// <TEST>
UserManager.shared.isActive = true

and git commit -m "my commit message" will assert with Error: Still has invalid debug markers in code:

Reference

Markdown editor

Issue #6

I like writing with markdown, it gives me comfortable experience with complete control over what I want to write.

I recommend vmd which renders exactly as GitHub. vmd is for rendering only, you need an editor to write, I use Sublime Text because it opens very fast

I also recommend using spectacle to easily split and organize windows

vmd

Primary key in Realm

Issue #4

Realm is great. But without primary key, it will duplicate the record, like https://github.com/realm/realm-java/issues/2730, http://stackoverflow.com/questions/32322460/should-i-define-the-primary-key-for-each-entity-in-realm, … So to force ourselves into the good habit of declaring primary key, we can leverage Swift protocol

Create primary constrain protocol like this

1
2
3
4
protocol PrimaryKeyAware {
var id: Int { get }
static func primaryKey() -> String?
}

and conform it in out Realm object

1
2
3
4
5
6
7
8
9
10
class Profile: Object, PrimaryKeyAware {

dynamic var firstName: String = ""
dynamic var lastName: String = ""
dynamic var id: Int = 0

override static func primaryKey() -> String? {
return "id"
}
}

This way, when using that object in out RealmStorage, we are safe to say that it has a primary key

1
2
3
4
5
6
7
8
9
10
11
12
13
class RealmStorage<T: Object> where T: PrimaryKeyAware {
let realm: Realm

init(realm: Realm = RealmProvider.realm()) {
self.realm = realm
}

func save(_ objects: [T]) {
try? realm.write {
realm.add(objects, update: true)
}
}
}

The usage is like this

1
2
3
let profile = Profile()
let storage = RealmStorage<Profile>()
storage.save([profile])

Configuration closure in Swift

Issue #2

When I was reading through Swinject, I found something interesting https://github.com/Swinject/Swinject/blob/master/Sources/Container.swift

1
2
3
4
public convenience init(parent: Container? = nil, registeringClosure: (Container) -> Void) {
self.init(parent: parent)
registeringClosure(self)
}

The init has a closure that makes configuration easy, much like a Builder pattern. So I think we can learn from that and make a Configurable protocol

1
2
3
4
5
6
7
8
9
10
protocol Configurable: class {}

extension Configurable {
func config(block: (Self) -> Void) -> Self {
block(self)
return self
}
}

extension NSObject : Configurable {}

With this, we can init some class with less hassle

1
2
3
4
let view = UIView().config {
$0.backgroundColor = .white
$0.layer.cornerRadius = 2
}

Hello world, again

Issue #1

I’ve used Wordpress, then moved to GitHub Pages with Jekyll, Octopress, Hexo, Hugo. You can view my page here http://fantageek.com/. It was good with all the custom themes and Disqus

But then I was a bit lazy with all the commands generate, commit, deploy, it hinders me from writing, so I moved to Medium.

The only thing I like about Medium is its discovery, your posts have high chanced of finding and viewing by people. What’s the point of writing if no one read it? But then I miss all the awesome markdown features of GitHub Pages. Medium is easy to use, but it seems it’s not for hackers, and I find it really uncomfortable when adding code block and headings. Medium also lists my comments as stories, which is kind of 😲

I like to write fast, and with good comments system, and I love Markdown.

I like GitHub. I use GitHub for my notes, so I think I will use it for my blog as well. Hope all these GitHub convenience will encourage me to write more often. This will, of course, be less discoverable by people. So if you by any chance visit this blog, ohayou from me 👋