• Stars
    star
    366
  • Rank 112,314 (Top 3 %)
  • Language
    Swift
  • License
    MIT License
  • Created over 6 years ago
  • Updated 11 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Pure DI in Swift

Pure

Swift CocoaPods Build Status Codecov

Pure makes Pure DI easy in Swift. This repository also introduces a way to do Pure DI in a Swift application.

Table of Contents

Background

Pure DI

Pure DI is a way to do a dependency injection without a DI container. The term was first introduced by Mark Seemann. The core concept of Pure DI is not to use a DI container and to compose an entire object dependency graph in the Composition Root.

Composition Root

The Composion Root is where the entire object graph is resolved. In a Cocoa application, AppDelegate is the Composition Root.

AppDependency

The root dependencies are the app delegate's dependency and the root view controller's dependency. The best way to inject those dependencies is to create a struct named AppDependency and store both dependencies in it.

struct AppDependency {
  let networking: Networking
  let remoteNotificationService: RemoteNotificationService
}

extension AppDependency {
  static func resolve() -> AppDependency {
    let networking = Networking()
    let remoteNotificationService = RemoteNotificationService()

    return AppDependency(
      networking: networking
      remoteNotificationService: remoteNotificationService
    )
  }
}

It is important to separate a production environment from a testing environment. We have to use an actual object in a production environment and a mock object in a testing environment.

AppDelegate is created automatically by the system using init(). In this initializer we're going to initialize the actaul app dependency with AppDependency.resolve(). On the other hand, we're going to provide a init(dependency:) to inject a mock app dependency in a testing environment.

class AppDelegate: UIResponder, UIApplicationDelegate {
  private let dependency: AppDependency

  /// Called from the system (it's private: not accessible in the testing environment)
  private override init() {
    self.dependency = AppDependency.resolve()
    super.init()
  }

  /// Called in a testing environment
  init(dependency: AppDependency) {
    self.dependency = dependency
    super.init()
  }
}

The app dependency can be used as the code below:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
  // inject rootViewController's dependency
  if let viewController = self.window?.rootViewController as? RootViewController {
    viewController.networking = self.dependency.networking
  }
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  // delegates remote notification receive event
  self.dependency.remoteNotificationService.receiveRemoteNotification(userInfo)
}

Testing AppDelegate

AppDelegate is one of the most important class in a Cocoa application. It resolves an app dependency and handles app events. It can be easily tested as we separated the app dependency.

This is an example test case of AppDelegate. It verifies that AppDelegate correctly injects root view controller's dependency in application(_:didFinishLaunchingWithOptions).

class AppDelegateTests: XCTestCase {
  func testInjectRootViewControllerDependencies() {
    // given
    let networking = MockNetworking()
    let mockDependency = AppDependency(
      networking: networking,
      remoteNotificationService: MockRemoteNotificationService()
    )
    let appDelegate = AppDelegate(dependency: mockDependency)
    appDelegate.window = UIWindow()
    appDelegate.window?.rootViewController = UIStoryboard(name: "Main", bundle: nil).instantiateInitialViewController()

    // when
    _ = appDelegate.application(.shared, didFinishLaunchingWithOptions: nil)

    // then
    let rootViewController = appDelegate.window?.rootViewController as? RootViewController
    XCTAssertTrue(rootViewController?.networking === networking)
  }
}

You can write tests for verifying remote notification events, open url events and even an app termination event.

Separating AppDelegate

But there is a problem: AppDelegate is still created by the system while testing. It causes AppDependency.resolve() gets called so we have to use a fake app delegate class in a testing environment.

First of all, create a new file in the test target. Define a new class named TestAppDelegate and implement basic requirements of the delegate protocol.

// iOS
class TestAppDelegate: NSObject, UIApplicationDelegate {
  var window: UIWindow?
}

// macOS
class TestAppDelegate: NSObject, NSApplicationDelegate {
}

Then create another file named main.swift to your application target. This file will replace the entry point of the application. We are going to provide different app delegates in this file. Don't forget to replace "MyAppTests.TestAppDelegate" with your project target and class name.

// iOS
UIApplicationMain(
  CommandLine.argc,
  CommandLine.unsafeArgv,
  NSStringFromClass(UIApplication.self),
  NSStringFromClass(NSClassFromString("MyAppTests.TestAppDelegate") ?? AppDelegate.self)
)

// macOS
func createAppDelegate() -> NSApplicationDelegate {
  if let cls = NSClassFromString("AllkdicTests.TestAppDelegate") as? (NSObject & NSApplicationDelegate).Type {
    return cls.init()
  } else {
    return AppDelegate(dependency: AppDependency.resolve())
  }
}

let application = NSApplication.shared
application.delegate = createAppDelegate()
_ = NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

Finally, remove the @UIApplicationMain and @NSApplicationMain from the AppDelegate.

  // iOS
- @UIApplicationMain
  class AppDelegate: UIResponder, UIApplicationDelegate

  // macOS
- @NSApplicationMain
  class AppDelegate: NSObject, NSApplicationDelegate

It is also a good practice to add a test case to verify that the application is using TestAppDelegate in a testing environment.

XCTAssertTrue(UIApplication.shared.delegate is TestAppDelegate)

Lazy Dependency

Using Factory

In Cocoa applications, view controllers are created lazily. For example, DetailViewController is not created until the user taps an item on ListViewController. In this case we have to pass a factory closure of DetailViewController to ListViewController.

/// A root view controller
class ListViewController {
  var detailViewControllerFactory: ((Item) -> DetailViewController)!

  func presentItemDetail(_ selectedItem: Item) {
    let detailViewController = self.detailViewControllerFactory(selectedItem)
    self.present(detailViewController, animated: true)
  }
}

static func resolve() -> AppDependency {
  let storyboard = UIStoryboard(name: "Main", bundle: nil)
  let networking = Networking()

  let detailViewControllerFactory: (Item) -> DetailViewController = { selectedItem in
    let detailViewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
    detailViewController.networking = networking
    detailViewController.item = selectedItem
    return detailViewController
  }

  return AppDependency(
    networking: networking,
    detailViewControllerFactory: detailViewControllerFactory
  )
}

But it has a critical problem: we cannot test the factory closure. Because the factory closure is created in the Composition Root but we should not access the Composition Root in a testing environment. What if I forget to inject the DetailViewController.networking property?

One possible approach is to create a factory closure outside of the Composition Root. Note that Storyboard and Networking is from the Composition Root, and Item is from the previous view controller so we have to separate the scope.

extension DetailViewController {
  static let factory: (UIStoryboard, Networking) -> (Item) -> DetailViewController = { storyboard, networking in
    return { selectedItem in
      let detailViewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
      detailViewController.networking = networking
      detailViewController.item = selectedItem
      return detailViewController
    }
  }
}

static func resolve() -> AppDependency {
  let storyboard = ...
  let networking = ...
  return .init(
    detailViewControllerFactory: DetailViewController.factory(storyboard, networking)
  )
}

Now we can test the DetailViewController.factory closure. Every dependencies are resolved in the Composition Root and a selected item can be passed from ListViewController to DetailViewController in runtime.

Using Configurator

There is another lazy dependency. Cells are created lazily but we cannot use the factory closure because the cells are created by the framework. We can just configure the cells.

Imagine that an UICollectionViewCell or UITableViewCell displays an image. There is an imageDownloader which downloads an actual image in a production environment and returns a mock image in a testing environment.

class ItemCell {
  var imageDownloader: ImageDownloaderType?
  var imageURL: URL? {
    didSet {
      guard let imageDownloader = self.imageDownloader else { return }
      self.imageView.setImage(with: self.imageURL, using: imageDownloader)
    }
  }
}

This cell is displayed in DetailViewController . DetailViewController should inject imageDownloader to the cell and sets the image property. Like we did in the factory, we can create a configurator closure for it. But this closure takes an existing instance and doens't have a return value.

class ItemCell {
  static let configurator: (ImageDownloaderType) -> (ItemCell, UIImage) -> Void = { imageDownloader
    return { cell, image in
      cell.imageDownloader = imageDownloader
      cell.image = image
    }
  }
}

DetailViewController can have the configurator and use it when configurating cell.

class DetailViewController {
  var itemCellConfigurator: ((ItemCell, UIImage) -> Void)?

  func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    ...
    self.itemCellConfigurator?(cell, image)
    return cell
  }
}

DetailViewController.itemCellConfigurator is injected from a factory.

extension DetailViewController {
  static let factory: (UIStoryboard, Networking, (ItemCell, UIImage) -> Void) -> (Item) -> DetailViewController = { storyboard, networking, imageCellConfigurator in
    return { selectedItem in
      let detailViewController = storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! DetailViewController
      detailViewController.networking = networking
      detailViewController.item = selectedItem
      detailViewController.imageCellConfigurator = imageCellConfigurator
      return detailViewController
    }
  }
}

And the Composition Root finally looks like:

static func resolve() -> AppDependency {
  let storybard = ...
  let networking = ...
  let imageDownloader = ...
  let listViewController = ...
  listViewController.detailViewControllerFactory = DetailViewController.factory(
    storyboard,
    networking,
    ImageCell.configurator(imageDownloader)
  )
  ...
}

Problem

Theoretically it works. But as you can see in the DetailViewController.factory it will be very complicated when there are many dependencies. This is why I created Pure. Pure makes factories and configurators neat.

Getting Started

Dependency and Payload

First of all, take a look at the factory and the configurator we used in the example code.

static let factory: (UIStoryboard, Networking, (ItemCell, UIImage) -> Void) -> (Item) -> DetailViewController
static let configurator: (ImageDownloaderType) -> (ItemCell, UIImage) -> Void

Those are the functions that return another function. The outer functions are executed in the Composition Root to inject static dependencies like Networking and the inner functions are executed in the view controllers to pass a runtime information like selectedItem. The parameter of the outer function is Dependency. The parameter of the inner function is Payload.

static let factory: (UIStoryboard, Networking, (ItemCell, UIImage) -> Void) -> (Item) -> DetailViewController
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^      ^^^^
                                        Dependency                             Payload

static let configurator: (ImageDownloaderType) -> (ItemCell, UIImage) -> Void
                          ^^^^^^^^^^^^^^^^^^^      ^^^^^^^^^^^^^^^^^
                              Dependency                Payload

Pure generalizes the factory and configurator using Dependency and Payload.

Module

Pure treats every class that requires a dependency and a payload as a Module. A protocol Module requires two types: Dependency and Payload.

protocol Module {
  /// A dependency that is resolved in the Composition Root.
  associatedtype Dependency

  /// A runtime information for configuring the module.
  associatedtype Payload
}

There are two types of module: FactoryModule and ConfiguratorModule.

Factory Module

FactoryModule is a generalized version of factory closure. It requires an initializer which takes both dependency and payload.

protocol FactoryModule: Module {
  init(dependency: Dependency, payload: Payload)
}

class DetailViewController: FactoryModule {
  struct Dependency {
    let storyboard: UIStoryboard
    let networking: Networking
  }

  struct Payload {
    let selectedItem: Item
  }

  init(dependency: Dependency, payload: Payload) {
  }
}

When a class conforms to FactoryModule, it will have a nested type Factory. Factory.init(dependency:) takes a dependency of the module and has a method create(payload:) which creates a new instance.

class Factory<Module> {
  let dependency: Module.Dependency
  func create(payload: Module.Payload) -> Module
}

// In AppDependency
let factory = DetailViewController.Factory(dependency: .init(
  storyboard: storyboard
  networking: networking
))

// In ListViewController
let viewController = factory.create(payload: .init(
  selectedItem: selectedItem
))

Configurator Module

ConfiguratorModule is a generalized version of configurator closure. It requires a configure() method which takes both dependency and payload.

protocol ConfiguratorModule: Module {
  func configure(dependency: Dependency, payload: Payload)
}

class ItemCell: ConfiguratorModule {
  struct Dependency {
    let imageDownloader: ImageDownloaderType
  }

  struct Payload {
    let image: UIImage
  }

  func configure(dependency: Dependency, payload: Payload) {
    self.imageDownloader = dependency.imageDownloader
    self.image = payload.image
  }
}

When a class conforms to ConfiguratorModule, it will have a nested type Configurator. Configurator.init(dependency:) takes a dependency of the module and has a method configure(_:payload:) which configures an existing instance.

class Configurator<Module> {
  let dependency: Module.Dependency
  func configure(_ module: Module, payload: Module.Payload)
}

// In AppDependency
let configurator = ItemCell.Configurator(dependency: .init(
  imageDownloader: imageDownloader
))

// In DetailViewController
configurator.configure(cell, payload: .init(image: image))

With FactoryModule and ConfiguratorModule, the example can be refactored as below:

static func resolve() -> AppDependency {
  let storybard = ...
  let networking = ...
  let imageDownloader = ...
  return .init(
    detailViewControllerFactory: DetailViewController.Factory(dependency: .init(
      storyboard: storyboard,
      networking: networking,
      itemCellConfigurator: ItemCell.Configurator(dependency: .init(
        imageDownloader: imageDownloader
      ))
    ))
  )
}

Customizing

Factory and Configurator are customizable. This is an example of customized factory:

extension Factory where Module == DetailViewController {
  func create(payload: Module.Payload, extraValue: ExtraValue) -> Payload {
    let module = self.create(payload: payload)
    module.extraValue = extraValue
    return module
  }
}

Storyboard Support

FactoryModule can support Storyboard-instantiated view controllers using customizing feature. The code below is an example for storyboard support of DetailViewController:

extension Factory where Module == DetailViewController {
  func create(payload: Module.Payload) -> Payload {
    let module = self.dependency.storyboard.instantiateViewController(withIdentifier: "DetailViewController") as! Module
    module.networking = dependency.networking
    module.itemCellConfigurator = dependency.itemCellConfigurator
    module.selectedItem = payload.selectedItem
    return module
  }
}

URLNavigator Support

URLNavigator is an elegant library for deeplink support. Pure can be also used in registering a view controller to a navigator.

class UserViewController {
  struct Payload {
    let userID: Int
  }
}

extension Factory where Module == UserViewController {
  func create(url: URLConvertible, values: [String: Any], context: Any?) -> Module? {
    guard let userID = values["id"] else { return nil }
    return self.create(payload: .init(userID: userID))
  }
}

let navigator = Navigator()
navigator.register("myapp://user/<id>", UserViewController.Factory().create)

Installation

Contribution

Any discussions and pull requests are welcomed ๐Ÿ’–

  • To development:

    $ make project
  • To test:

    $ swift test

License

Pure is under MIT license. See the LICENSE file for more info.

More Repositories

1

Then

โœจ Super sweet syntactic sugar for Swift initializers
Swift
4,045
star
2

URLNavigator

โ›ต๏ธ Elegant URL Routing for Swift
Swift
3,128
star
3

Toaster

๐Ÿž Toast for Swift
Swift
1,668
star
4

UITextView-Placeholder

A missing placeholder for UITextView
Objective-C
1,458
star
5

RxTodo

iOS Todo Application using RxSwift and ReactorKit
Swift
1,291
star
6

SwiftyImage

๐ŸŽจ Generate image resources in Swift
Swift
1,075
star
7

SwiftUITodo

An example to-do list app using SwiftUI which is introduced in WWDC19
Swift
715
star
8

Umbrella

โ˜‚๏ธ Analytics abstraction layer for Swift
Swift
607
star
9

Drrrible

Dribbble for iOS using ReactorKit
Swift
514
star
10

CocoaSeeds

Git Submodule Alternative for Cocoa.
Ruby
341
star
11

RxViewController

RxSwift wrapper for UIViewController and NSViewController
Swift
331
star
12

SwipeBack

Enable iOS 7+ swipe-to-back when custom back button is set.
Objective-C
321
star
13

UICollectionViewFlexLayout

A drop-in replacement for UICollectionViewFlowLayout
Swift
296
star
14

MoyaSugar

๐Ÿฏ Syntactic sugar for Moya
Swift
190
star
15

Carte

๐Ÿด Open source license notice view generator for Swift
Ruby
189
star
16

graygram-ios

[40์‹œ๊ฐ„๋งŒ์— Swift๋กœ iOS ์•ฑ ๋งŒ๋“ค๊ธฐ] ํ‘๋ฐฑ ์‚ฌ์ง„ ์ „์šฉ ์†Œ์…œ ๋ฏธ๋””์–ด ์•ฑ
Swift
180
star
17

ReusableKit

Generic reusables for UICollectionView and UITableView
Swift
158
star
18

UINavigationItem-Margin

Margins for UINavigationItem
Objective-C
158
star
19

allkdic

์˜ฌใ…‹์‚ฌ์ „ - ๋งฅ์—์„œ ๋‹จ์ถ•ํ‚ค๋ฅผ ๋ˆ„๋ฅด๋ฉด ์˜์–ด์‚ฌ์ „์ด ๋™‡!!!!
Swift
128
star
20

SwiftyColor

๐ŸŽจ The most sexy way to use colors in Swift.
Swift
120
star
21

RxCodable

RxSwift wrapper for Codable
Swift
113
star
22

RxExpect

RxSwift testing framework
Swift
105
star
23

korail

๐Ÿš† An unofficial Korail API for Python.
Python
90
star
24

Stubber

A minimal method stub for Swift
Swift
86
star
25

Cleverbot

iOS Messaging Application using Cleverbot and ReactorKit
Swift
78
star
26

Blockchain

A blockchain simulator written in Swift
Swift
65
star
27

LetsGitHubSearch

Let'Swift 18 Workshop - Let's TDD
Swift
63
star
28

CancelBag

A DisposeBag for Combine
Swift
53
star
29

TouchAreaInsets

Expand UIView touch area just like a charm
Objective-C
50
star
30

UIViewController-NavigationBar

[Deprecated] UIViewController with its own navigation bar. It provides an availability for the smooth push/pop animations between view controllers which have different navigation bar styles.
Swift
50
star
31

ios-with-swift-in-40-hours

40์‹œ๊ฐ„๋งŒ์— Swift๋กœ iOS ์•ฑ ํ•˜๋‚˜๋ฅผ ๋งŒ๋“ค์–ด๋ด…์‹œ๋‹ค.
49
star
32

SectionReactor

A ReactorKit extension for managing table view and collection view sections with RxDataSources
Swift
45
star
33

SafeCollection

Safe Collection for Swift
Swift
42
star
34

Fallback

๐Ÿ’Š Syntactic sugar for Swift do-try-catch
Swift
42
star
35

AsyncBlockOperation

NSOperation subclass for async block.
Objective-C
41
star
36

CGFloatLiteral

CGFloat(10) becomes 10.f
Swift
35
star
37

Endpoint

๐Ÿš€ Elegant API Abstraction for Swift
Swift
35
star
38

RxJSON

RxSwift wrapper for JSON
Swift
33
star
39

ASCollectionFlexLayout

A custom collection layout that allows to use Texture layout specs in ASCollectionNode.
Swift
27
star
40

next-route-map

๐Ÿš Routes for Next.js
TypeScript
26
star
41

Date.swift

๐Ÿ“… A Swift Date Library
Swift
26
star
42

ImageEffects

Bring UIImageEffects (WWDC 2013) to UIImage category with handy interface.
Objective-C
25
star
43

Todobox

[40์‹œ๊ฐ„๋งŒ์— Swift๋กœ iOS ์•ฑ ๋งŒ๋“ค๊ธฐ] ํ•  ์ผ ๋ชฉ๋ก ๊ด€๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
Swift
24
star
44

RxReusable

Reusable cells and views for RxSwift
Swift
23
star
45

AlertReactor

A ReactorKit extension for UIAlertController
Swift
23
star
46

UIButton-BackgroundContentMode

A missing background content mode for UIButton.
Objective-C
22
star
47

sketch-export-sizes-generator

๐Ÿ’Ž A simple plugin for Sketch that generates export sizes of layers.
20
star
48

RxDelegateCells

[Don't use this] UITableView Cell Height and UICollectionView Cell Size for RxSwift
Swift
19
star
49

Immutable

Missing non-mutating functions in Swift
Swift
18
star
50

PureSwinject

Auto register Pure factories to Swinject
Swift
18
star
51

graygram-web

Python
16
star
52

daumpostcode

๋‹ค์Œ ์šฐํŽธ๋ฒˆํ˜ธ ์„œ๋น„์Šค๋ฅผ ๋ณ„๋„์˜ ํŒจํ‚ค์ง€ ์„ค์น˜ ์—†์ด๋„ ๋„ค์ดํ‹ฐ๋ธŒ ์•ฑ์—์„œ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
HTML
16
star
53

Swiftproj

A command-line tool for managing Xcode project with Swift Package Manager.
Ruby
15
star
54

Meal

[40์‹œ๊ฐ„๋งŒ์— Swift๋กœ iOS ์•ฑ ๋งŒ๋“ค๊ธฐ] ์ „๊ตญ ์ดˆ/์ค‘/๊ณ ๋“ฑํ•™๊ต ๊ธ‰์‹ ์กฐํšŒ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
Swift
15
star
55

SwinjectSafeAuto

SwinjectSafeAuto allows to auto-register services to the container and verify the required services are properly registered.
Swift
14
star
56

FastLogin

2018 FAST CAMPUS DEV SEMINAR ์‹ค์Šต ์•ฑ
Swift
14
star
57

NSOperationQueue-CompletionBlock

Completion block for NSOperationQueue.
Objective-C
13
star
58

graphql-codegen-typescript-fixtures

A plugin for graphql-code-generator that generates TypeScript fixtures for testing.
TypeScript
12
star
59

Kodium

Better Korean typo for medium.com
JavaScript
11
star
60

Estimator

Planning Poker application that communicates over BLE (Bluetoothe Low Energy)
Swift
10
star
61

MapViewVisibleAnnotations

Displays which annotations are currently visible in the specific area.
Swift
9
star
62

jira-velocity-chart-completion-rate

Displays completion rate in JIRA velocity chart.
JavaScript
9
star
63

Slots

Dynamic contents management for Swift.
Swift
9
star
64

github-monospace-editor

GitHub Monospace Editor Userscript
JavaScript
9
star
65

flask-restdoc

Flask-Restdoc is a simple tool that generates RESTful API documentation automatically from python files.
Python
9
star
66

notion-badge

Embed App Store badge in Notion
HTML
9
star
67

SwinjectFactory

Experimental Swinject extension for factory injection.
Swift
8
star
68

if_

Swift if-else statement as an expression (Experimental)
Swift
8
star
69

flask-errorhandler

Error handlers for Flask blueprints.
Python
7
star
70

ModelKit

[deprecated] Experimental Model framework for Swift
Swift
7
star
71

shlee322-fb-troll

์ƒํ˜์ด์˜ ํŽ˜๋ถ ๊ธ€๋งˆ๋‹ค ์ผํ•˜๋ผ๋Š” ๋Œ“๊ธ€์„ ๋‹ฌ์•„๋ณด์•„์š” ^์˜ค^
Python
7
star
72

adorable

M๊™ผฬˆaฬ†ฬŽkฬŠฬˆeฬŒฬˆ tฬฬˆeฬ‘ฬˆxฬ†ฬˆt๊™ผฬˆ aฬŠฬˆdฬŒฬˆoฬ„ฬˆrฬฬˆaฬ‘ฬˆbฬ†ฬˆl๊™ผฬˆeฬ†ฬŽ
HTML
7
star
73

resume

My rรฉsumรฉ
Makefile
6
star
74

Playground

A playground for an ideal project
HCL
6
star
75

Bagel

GIF converter for Mac
Swift
5
star
76

RxGitHubSearch

An example GitHub search app for my lecture
Swift
5
star
77

xcode-project-template

Swift
5
star
78

staccato

Staccato ๋ชจ์ž„ ์†Œ๊ฐœ ํŽ˜์ด์ง€
Python
5
star
79

fbmarkdown

Chrome extension that enable markdown on facebook.
JavaScript
5
star
80

JLModel

JLModel allows you to manage models in very simple way.
Objective-C
5
star
81

blog

์–ธ์  ๊ฐ„ ์“ฐ๊ฒ ์ง€?
CSS
4
star
82

TypedKey

Statically-typed key for Swift.
Swift
4
star
83

Hippo-iOS

Objective-C
4
star
84

devxoul.github.io

HTML
4
star
85

naver-cafe-chat-notification

๋„ค์ด๋ฒ„ ์นดํŽ˜ ์ƒˆ ์ฑ„ํŒ… ๊ฐœ์ˆ˜๋ฅผ ๋ธŒ๋ผ์šฐ์ € ํƒ€์ดํ‹€๋กœ ์•Œ๋ ค์ฃผ๋Š” ์Šคํฌ๋ฆฝํŠธ.
JavaScript
4
star
86

fabric-verbose

Python
4
star
87

TistoryMarkdown

A Markdown plugin for Tistory.
4
star
88

4SharedDownloader

You can download files from 4shared.com without delay.
ActionScript
4
star
89

help-me-rx-tableview-cell-height

Help me!
Swift
3
star
90

macsafe

Keep your MacBook from thieves.
Python
3
star
91

NAR

Nginx Auto Reloader
JavaScript
2
star
92

Editag

The easiest way to edit id3 tag.
ActionScript
2
star
93

ImageCrawler

JavaScript
2
star
94

ovenbreak

Utils for ovenapp.io
Makefile
2
star
95

JLChromeTabController

A chrome-like tab bar for iOS.
Objective-C
2
star
96

Evermind

Evermind is a handy tool that makes it easy to mindmap your idea from your work, study, and everyday life.
ActionScript
2
star
97

JLUtils

Objective-C
1
star
98

FastcampusFrame

Swift
1
star
99

Sonaki

Objective-C
1
star
100

Calendar

Infinite-scrolling calendar example
Objective-C
1
star