diff --git a/Ring/Ring.xcodeproj/project.pbxproj b/Ring/Ring.xcodeproj/project.pbxproj
index a73b9636d16f2aa0b11dbff3eafc7ae3376d6079..4b23f2aca470a071513faa9bdbf92e007db75828 100644
--- a/Ring/Ring.xcodeproj/project.pbxproj
+++ b/Ring/Ring.xcodeproj/project.pbxproj
@@ -157,8 +157,6 @@
 		1A3CA32D1F13DA7200283748 /* Chameleon+Ring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3CA32C1F13DA7200283748 /* Chameleon+Ring.swift */; };
 		1A3D28A71F0EB9DB00B524EE /* Bool+String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3D28A61F0EB9DB00B524EE /* Bool+String.swift */; };
 		1A3D28A91F0EBF0200B524EE /* UIView+Ring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A3D28A81F0EBF0200B524EE /* UIView+Ring.swift */; };
-		1A5DC01E1F355DA70075E8EF /* ContactsAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A5DC01D1F355DA70075E8EF /* ContactsAdapterDelegate.swift */; };
-		1A5DC0201F355DCF0075E8EF /* ContactsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A5DC01F1F355DCF0075E8EF /* ContactsService.swift */; };
 		1A5DC0241F3564360075E8EF /* ContactRequestModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A5DC0231F3564360075E8EF /* ContactRequestModel.swift */; };
 		1A5DC0281F3564AA0075E8EF /* MessageModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A5DC0271F3564AA0075E8EF /* MessageModel.swift */; };
 		1A5DC02C1F3565250075E8EF /* MeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A5DC02B1F3565250075E8EF /* MeViewController.swift */; };
@@ -200,6 +198,9 @@
 		56BBC9DF1EDDC9D300CDAF8B /* LookupNameResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 56BBC9DE1EDDC9D300CDAF8B /* LookupNameResponse.m */; };
 		56C715FF1F0D36C600770048 /* ContactsAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 56C715FE1F0D36C600770048 /* ContactsAdapter.mm */; };
 		5C093F011FB495830011D90E /* Differentiator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C093F001FB495830011D90E /* Differentiator.framework */; };
+		5C340DF51FCC6D5B00302D1A /* ContactRequestsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C340DF41FCC6D5B00302D1A /* ContactRequestsManager.swift */; };
+		5C9CF8391FCDA2D6005E3A69 /* ContactsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C716021F0D466100770048 /* ContactsService.swift */; };
+		5C9CF83A1FCDA2D6005E3A69 /* ContactsAdapterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56C716001F0D36D900770048 /* ContactsAdapterDelegate.swift */; };
 		5CBADD081FC36DA5009CEFD7 /* ConversationModelHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBADD071FC36DA5009CEFD7 /* ConversationModelHelper.swift */; };
 		5CE66F781FBF80B700EE9291 /* NewAccountsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE66F771FBF80B700EE9291 /* NewAccountsService.swift */; };
 		5CE66F7C1FBF811200EE9291 /* InitialLoadingViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5CE66F7A1FBF811200EE9291 /* InitialLoadingViewController.storyboard */; };
@@ -401,8 +402,6 @@
 		1A3D28A81F0EBF0200B524EE /* UIView+Ring.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Ring.swift"; sourceTree = "<group>"; };
 		1A5DC00A1F3558980075E8EF /* ContactsAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContactsAdapter.h; sourceTree = "<group>"; };
 		1A5DC00D1F3559070075E8EF /* ContactsAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ContactsAdapter.mm; sourceTree = "<group>"; };
-		1A5DC01D1F355DA70075E8EF /* ContactsAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsAdapterDelegate.swift; sourceTree = "<group>"; };
-		1A5DC01F1F355DCF0075E8EF /* ContactsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsService.swift; sourceTree = "<group>"; };
 		1A5DC0231F3564360075E8EF /* ContactRequestModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactRequestModel.swift; sourceTree = "<group>"; };
 		1A5DC0271F3564AA0075E8EF /* MessageModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessageModel.swift; sourceTree = "<group>"; };
 		1A5DC02B1F3565250075E8EF /* MeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeViewController.swift; sourceTree = "<group>"; };
@@ -453,6 +452,7 @@
 		56C716001F0D36D900770048 /* ContactsAdapterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsAdapterDelegate.swift; sourceTree = "<group>"; };
 		56C716021F0D466100770048 /* ContactsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactsService.swift; sourceTree = "<group>"; };
 		5C093F001FB495830011D90E /* Differentiator.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Differentiator.framework; path = Carthage/Build/iOS/Differentiator.framework; sourceTree = "<group>"; };
+		5C340DF41FCC6D5B00302D1A /* ContactRequestsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestsManager.swift; sourceTree = "<group>"; };
 		5CBADD071FC36DA5009CEFD7 /* ConversationModelHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationModelHelper.swift; sourceTree = "<group>"; };
 		5CE66F771FBF80B700EE9291 /* NewAccountsService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewAccountsService.swift; sourceTree = "<group>"; };
 		5CE66F7A1FBF811200EE9291 /* InitialLoadingViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = InitialLoadingViewController.storyboard; sourceTree = "<group>"; };
@@ -618,11 +618,10 @@
 				564C44631E943E1E000F92B1 /* NameRegistrationAdapterDelegate.swift */,
 				56C716021F0D466100770048 /* ContactsService.swift */,
 				56C716001F0D36D900770048 /* ContactsAdapterDelegate.swift */,
-				1A5DC01D1F355DA70075E8EF /* ContactsAdapterDelegate.swift */,
-				1A5DC01F1F355DCF0075E8EF /* ContactsService.swift */,
 				62A88D361F6C2ED400F8AB18 /* PresenceAdapterDelegate.swift */,
 				62A88D3A1F6C3ACC00F8AB18 /* PresenceService.swift */,
 				62DFAB2D1F9FF0D0002D6F9C /* NetworkService.swift */,
+				5C340DF41FCC6D5B00302D1A /* ContactRequestsManager.swift */,
 			);
 			path = Services;
 			sourceTree = "<group>";
@@ -1392,6 +1391,8 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				5C9CF8391FCDA2D6005E3A69 /* ContactsService.swift in Sources */,
+				5C9CF83A1FCDA2D6005E3A69 /* ContactsAdapterDelegate.swift in Sources */,
 				557086521E8ADB9D001A7CE4 /* SystemAdapter.mm in Sources */,
 				0E2D5F531F9145C800D574BF /* LinkNewDeviceCell.swift in Sources */,
 				0586C94B1F684DF600613517 /* UIImage+Helpers.swift in Sources */,
@@ -1401,7 +1402,6 @@
 				1A2D18F71F292D7200B2C785 /* MessageCellSent.swift in Sources */,
 				04399AAC1D1C304300E99CD9 /* AccountAdapter.mm in Sources */,
 				1AABA7461F0FE9C000739605 /* UIColor+Ring.swift in Sources */,
-				1A5DC0201F355DCF0075E8EF /* ContactsService.swift in Sources */,
 				1A2D18C71F29180700B2C785 /* DeviceModel.swift in Sources */,
 				1A20418F1F1EAC0E00C08435 /* Coordinator.swift in Sources */,
 				1A2D18A11F27A6D600B2C785 /* LinkDeviceViewController.swift in Sources */,
@@ -1414,7 +1414,6 @@
 				1A5DC0241F3564360075E8EF /* ContactRequestModel.swift in Sources */,
 				1A5DC03F1F35678D0075E8EF /* ContactRequestsViewController.swift in Sources */,
 				1A20418B1F1EA58A00C08435 /* ViewModelBased.swift in Sources */,
-				1A5DC01E1F355DA70075E8EF /* ContactsAdapterDelegate.swift in Sources */,
 				1A20418D1F1EABCC00C08435 /* StateableResponsive.swift in Sources */,
 				1A0C4EE51F1D67DF00550433 /* WalkthroughCoordinator.swift in Sources */,
 				1A2D18DD1F29192D00B2C785 /* MessableBubble.swift in Sources */,
@@ -1458,6 +1457,7 @@
 				04399AAE1D1C304300E99CD9 /* Utils.mm in Sources */,
 				56BBC9A31ED714DF00CDAF8B /* ConversationsService.swift in Sources */,
 				563AEC771EA664C0003A5641 /* RegistrationResponse.m in Sources */,
+				5C340DF51FCC6D5B00302D1A /* ContactRequestsManager.swift in Sources */,
 				564C445B1E8EA44E000F92B1 /* Durations.swift in Sources */,
 				0EB479951FA28A7300106AFD /* ButtonTransparentBackground.swift in Sources */,
 				0EB1A5D11F8EBE23009923E2 /* DeviceCell.swift in Sources */,
diff --git a/Ring/Ring/Account/VCardUtils.swift b/Ring/Ring/Account/VCardUtils.swift
index a3506b48dd87bf464ac16c7550b059ba453aeb1f..572c4f5eb41a85aa408b02c4c1c9c54d7ec85242 100644
--- a/Ring/Ring/Account/VCardUtils.swift
+++ b/Ring/Ring/Account/VCardUtils.swift
@@ -30,23 +30,24 @@ enum VCardFolders: String {
 enum VCardFiles: String {
     case myProfile
 }
+
 class VCardUtils {
 
-    class func saveVCard(vCard: CNContact, withName name: String, inFolder folder: String) -> Observable<Void> {
-        return Observable.create { observable in
+    class func saveVCard(vCard: CNContact, withName name: String, inFolder folder: String) -> Completable {
+        return Completable.create { completable in
             if let directoryURL = VCardUtils.getFilePath(forFile: name, inFolder: folder, createIfNotExists: true) {
                 do {
                     let data = try CNContactVCardSerialization.dataWithImageAndUUID(from: vCard, andImageCompression: nil)
                     try data.write(to: directoryURL)
-                    observable.on(.completed)
+                    completable(.completed)
 
                 } catch {
-                    observable.on(.error(ContactServiceError.saveVCardFailed))
+                    completable(.error(ContactServiceError.saveVCardFailed))
                 }
             } else {
-                observable.on(.error(ContactServiceError.saveVCardFailed))
+                completable(.error(ContactServiceError.saveVCardFailed))
             }
-            return Disposables.create { }
+            return Disposables.create()
         }
     }
 
diff --git a/Ring/Ring/AppDelegate.swift b/Ring/Ring/AppDelegate.swift
index 5405ca805b590eaec3e54be848623d1edcd4ca55..80be19afcef2d028e69763bf06642b4c9692753a 100644
--- a/Ring/Ring/AppDelegate.swift
+++ b/Ring/Ring/AppDelegate.swift
@@ -48,13 +48,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
                             withConversationService: self.conversationsService,
                             withContactsService: self.contactsService,
                             withPresenceService: self.presenceService,
-                            withNetworkService: self.networkService
+                            withNetworkService: self.networkService,
+                            withContactRequestsManager: self.contactRequestsManager
                             )
     }()
+
     private lazy var appCoordinator: AppCoordinator = {
         return AppCoordinator(with: self.injectionBag)
     }()
 
+    private lazy var contactRequestsManager: ContactRequestsManager = {
+        return ContactRequestsManager(with: self.newAccountsService,
+                                      contactsService: self.contactsService,
+                                      conversationsService: self.conversationsService,
+                                      presenceService: self.presenceService)
+    }()
+
     private let log = SwiftyBeaver.self
 
     private let disposeBag = DisposeBag()
diff --git a/Ring/Ring/Coordinators/InjectionBag.swift b/Ring/Ring/Coordinators/InjectionBag.swift
index 87e8ac1f0f5f1ebc15cb09bb274f2f05e5d5c648..a8390c82017743b2a1802529abd68e35accbe4ed 100644
--- a/Ring/Ring/Coordinators/InjectionBag.swift
+++ b/Ring/Ring/Coordinators/InjectionBag.swift
@@ -30,6 +30,8 @@ class InjectionBag {
     let presenceService: PresenceService
     let networkService: NetworkService
 
+    let contactRequestsManager: ContactRequestsManager
+
     init (withDaemonService daemonService: DaemonService,
           withAccountService accountService: AccountsService,
           withNewAccountsService newAccountsService: NewAccountsService,
@@ -37,7 +39,8 @@ class InjectionBag {
           withConversationService conversationService: ConversationsService,
           withContactsService contactsService: ContactsService,
           withPresenceService presenceService: PresenceService,
-          withNetworkService networkService: NetworkService) {
+          withNetworkService networkService: NetworkService,
+          withContactRequestsManager contactRequestsManager: ContactRequestsManager) {
         self.daemonService = daemonService
         self.accountService = accountService
         self.newAccountsService = newAccountsService
@@ -46,6 +49,7 @@ class InjectionBag {
         self.contactsService = contactsService
         self.presenceService = presenceService
         self.networkService = networkService
+        self.contactRequestsManager = contactRequestsManager
     }
 
 }
diff --git a/Ring/Ring/EditProfileViewModel.swift b/Ring/Ring/EditProfileViewModel.swift
index c3ebad5a1ce3a737703e995a85bf2a3989d85d75..2be8450eed1d81753d16956de4fdd48b2f0377ea 100644
--- a/Ring/Ring/EditProfileViewModel.swift
+++ b/Ring/Ring/EditProfileViewModel.swift
@@ -42,14 +42,16 @@ class EditProfileViewModel {
       }
 
     func saveProfile() {
-
         let vcard = CNMutableContact()
         if let image = self.image.value, !image.isEqual(defaultImage) {
             vcard.imageData = UIImagePNGRepresentation(image)
         }
-            vcard.familyName = self.profileName.value
-        _ = VCardUtils.saveVCard(vCard: vcard, withName: VCardFiles.myProfile.rawValue, inFolder: VCardFolders.profile.rawValue).subscribe()
-
+        vcard.familyName = self.profileName.value
+        VCardUtils.saveVCard(vCard: vcard,
+                             withName: VCardFiles.myProfile.rawValue,
+                             inFolder: VCardFolders.profile.rawValue)
+            .subscribe()
+            .disposed(by: self.disposeBag)
     }
 
     func updateImage(_ image: Image) {
diff --git a/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift b/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift
index 705db7f2467d15238b91632fe26aeb25c79c85cd..897fb870c5a26a41da5e6c782d4982f43d60621a 100644
--- a/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift
+++ b/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift
@@ -146,7 +146,6 @@ class ContactRequestsViewController: UIViewController, StoryboardBased, ViewMode
     }
 
     func setupBindings() {
-
         self.viewModel
             .hasInvitations
             .observeOn(MainScheduler.instance)
@@ -155,26 +154,26 @@ class ContactRequestsViewController: UIViewController, StoryboardBased, ViewMode
     }
 
     func acceptButtonTapped(withItem item: ContactRequestItem) {
-        viewModel.accept(withItem: item).subscribe(onError: { [unowned self] error in
-            self.log.error("Accept trust request failed")
-            }, onCompleted: { [unowned self] in
-                self.log.info("Accept trust request done")
+        self.viewModel.accept(withItem: item).subscribe(onCompleted: { [weak self] in
+            self?.log.info("Accept trust request done")
+        }, onError: { [weak self] error in
+            self?.log.error("Accept trust request failed")
         }).disposed(by: self.disposeBag)
     }
 
     func discardButtonTapped(withItem item: ContactRequestItem) {
-        viewModel.discard(withItem: item).subscribe(onError: { [unowned self] error in
-            self.log.error("Discard trust request failed")
-            }, onCompleted: { [unowned self] in
-                self.log.info("Discard trust request done")
+        self.viewModel.discard(withItem: item).subscribe(onCompleted: { [weak self] in
+            self?.log.info("Discard trust request done")
+        }, onError: { [weak self] error in
+            self?.log.error("Discard trust request failed")
         }).disposed(by: self.disposeBag)
     }
 
     func banButtonTapped(withItem item: ContactRequestItem) {
-        viewModel.ban(withItem: item).subscribe(onError: { [unowned self] error in
-            self.log.error("Ban trust request failed")
-            }, onCompleted: { [unowned self] in
-                self.log.info("Ban trust request done")
+        self.viewModel.ban(withItem: item).subscribe(onCompleted: { [weak self] in
+            self?.log.info("Ban trust request done")
+        }, onError: { [weak self] (error) in
+            self?.log.error("Ban trust request failed")
         }).disposed(by: self.disposeBag)
     }
 }
diff --git a/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift b/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift
index b71b934bcb1075e1dd6b221f5df789b3bd03d813..ce22baa3a4c9d2279f0e949977667b612e531184 100644
--- a/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift
+++ b/Ring/Ring/Features/ContactRequests/ContactRequestsViewModel.swift
@@ -2,6 +2,7 @@
  *  Copyright (C) 2017 Savoir-faire Linux Inc.
  *
  *  Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
+ *  Author: Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -22,7 +23,7 @@ import RxSwift
 import Contacts
 import SwiftyBeaver
 
-class ContactRequestsViewModel: Stateable, ViewModel {
+final class ContactRequestsViewModel: Stateable, ViewModel {
 
     // MARK: - Rx Stateable
     private let stateSubject = PublishSubject<State>()
@@ -30,51 +31,43 @@ class ContactRequestsViewModel: Stateable, ViewModel {
         return self.stateSubject.asObservable()
     }()
 
-    let contactsService: ContactsService
-    let accountsService: AccountsService
-    let conversationService: ConversationsService
-    let nameService: NameService
-    let presenceService: PresenceService
+    private let contactsService: ContactsService
+    private let accountsService: NewAccountsService
+    private let conversationService: ConversationsService
+    fileprivate let nameService: NameService
+    private let presenceService: PresenceService
+    private let contactRequestsManager: ContactRequestsManager
 
     fileprivate let disposeBag = DisposeBag()
-    fileprivate let log = SwiftyBeaver.self
+    private let log = SwiftyBeaver.self
 
-    fileprivate let injectionBag: InjectionBag
+    private let injectionBag: InjectionBag
 
     required init(with injectionBag: InjectionBag) {
         self.contactsService = injectionBag.contactsService
-        self.accountsService = injectionBag.accountService
+        self.accountsService = injectionBag.newAccountsService
         self.conversationService = injectionBag.conversationsService
         self.nameService = injectionBag.nameService
         self.presenceService = injectionBag.presenceService
+        self.contactRequestsManager = injectionBag.contactRequestsManager
 
         self.injectionBag = injectionBag
-
-        self.contactsService.contactRequests
-            .asObservable()
-            .subscribe(onNext: {[unowned self] contactRequests in
-                guard let account = self.accountsService.currentAccount else { return }
-                guard let ringId = contactRequests.last?.ringId else { return }
-                self.conversationService.generateMessage(ofType: GeneratedMessageType.receivedContactRequest,
-                                                         forRindId: ringId,
-                                                         forAccount: account)
-            })
-            .disposed(by: self.disposeBag)
     }
 
     lazy var contactRequestItems: Observable<[ContactRequestItem]> = {
-        return self.contactsService.contactRequests
-            .asObservable()
-            .map({ [unowned self] contactRequests in
-                return contactRequests
-                    .filter { $0.accountId == self.accountsService.currentAccount?.id }
-                    .sorted { $0.receivedDate > $1.receivedDate }
-                    .map { contactRequest in
-                        let item = ContactRequestItem(withContactRequest: contactRequest)
-                        self.lookupUserName(withItem: item)
-                        return item
-                    }
-            })
+        let contactRequestsObs = self.contactsService.contactRequests.asObservable()
+        let currentAccountObs = self.accountsService.currentAccount().asObservable()
+
+        return Observable.combineLatest(contactRequestsObs, currentAccountObs, resultSelector: { (requests, account) -> [ContactRequestItem] in
+            return requests
+                .filter { $0.accountId == account.id }
+                .sorted { $0.receivedDate > $1.receivedDate }
+                .map { contactRequest in
+                    let item = ContactRequestItem(withContactRequest: contactRequest)
+                    //TODO: Lookup username
+                    return item
+                }
+        })
     }()
 
     lazy var hasInvitations: Observable<Bool> = {
@@ -85,77 +78,62 @@ class ContactRequestsViewModel: Stateable, ViewModel {
             })
     }()
 
-    func accept(withItem item: ContactRequestItem) -> Observable<Void> {
-        let acceptCompleted = self.contactsService.accept(contactRequest: item.contactRequest, withAccount: self.accountsService.currentAccount!)
-
-        let accountHelper = AccountModelHelper(withAccount: self.accountsService.currentAccount!)
-        self.conversationService.saveMessage(withId: "",
-                                             withContent: GeneratedMessageType.contactRequestAccepted.rawValue,
-                                             byAuthor: accountHelper.ringId!,
-                                             toConversationWith: item.contactRequest.ringId,
-                                             currentAccountId: (self.accountsService.currentAccount?.id)!, generated: true)
-            .subscribe(onCompleted: { [unowned self] in
-                self.log.debug("Message saved")
-            })
-            .disposed(by: disposeBag)
-
-        self.presenceService.subscribeBuddy(withAccountId: (self.accountsService.currentAccount?.id)!,
-                                            withUri: item.contactRequest.ringId,
-                                            withFlag: true)
-
-        if let vCard = item.contactRequest.vCard {
-            let saveVCardCompleted = self.contactsService.saveVCard(vCard: vCard, forContactWithRingId: item.contactRequest.ringId)
-            return Observable<Void>.zip(acceptCompleted, saveVCardCompleted) { _, _ in
-                return
-            }
-        } else {
-            return acceptCompleted.asObservable()
-        }
-    }
-
-    func discard(withItem item: ContactRequestItem) -> Observable<Void> {
-        return self.contactsService.discard(contactRequest: item.contactRequest,
-                                            withAccount: self.accountsService.currentAccount!)
+    func accept(withItem item: ContactRequestItem) -> Completable {
+        return self.accountsService.currentAccount().asObservable()
+            .flatMap { [unowned self] (account) -> Completable in
+                return self.contactRequestsManager.accept(contactRequest: item.contactRequest,
+                                                          account: account)
+            }.asCompletable()
     }
 
-    func ban(withItem item: ContactRequestItem) -> Observable<Void> {
-        let discardCompleted = self.contactsService.discard(contactRequest: item.contactRequest,
-                                                            withAccount: self.accountsService.currentAccount!)
-
-        let removeCompleted = self.contactsService.removeContact(withRingId: item.contactRequest.ringId,
-                                                                 ban: true,
-                                                                 withAccount: self.accountsService.currentAccount!)
-
-        return Observable<Void>.zip(discardCompleted, removeCompleted) { _, _ in
-            return
-        }
+    func discard(withItem item: ContactRequestItem) -> Completable {
+        return self.accountsService.currentAccount().asObservable()
+            .flatMap { [unowned self] (account) -> Completable in
+                return self.contactRequestsManager.discard(contactRequest: item.contactRequest,
+                                                           account: account)
+            }.asCompletable()
     }
 
-    fileprivate func lookupUserName(withItem item: ContactRequestItem) {
-
-        self.nameService.usernameLookupStatus.asObservable()
-            .filter({ lookupNameResponse in
-                return lookupNameResponse.address == item.contactRequest.ringId
-            })
-            .subscribe(onNext: { lookupNameResponse in
-                if lookupNameResponse.state == .found && !lookupNameResponse.name.isEmpty {
-                    item.userName.value = lookupNameResponse.name
-                } else {
-                    item.userName.value = lookupNameResponse.address
-                }
-            })
-            .disposed(by: self.disposeBag)
-
-        self.nameService.lookupAddress(withAccount: (accountsService.currentAccount?.id)!,
-                                              nameserver: "",
-                                              address: item.contactRequest.ringId)
+    func ban(withItem item: ContactRequestItem) -> Completable {
+        return self.accountsService.currentAccount().asObservable()
+            .flatMap { [unowned self] (account) -> Completable in
+                return self.contactRequestsManager.ban(contactRequest: item.contactRequest,
+                                                       account: account)
+            }.asCompletable()
     }
 
     func showConversation (forRingId ringId: String) {
-        let conversationViewModel = ConversationViewModel(with: self.injectionBag)
-        let conversation = self.conversationService.findConversation(withRingId: ringId,
-                                                                     withAccountId: (accountsService.currentAccount?.id)!)
-        conversationViewModel.conversation = conversation
-        self.stateSubject.onNext(ConversationsState.conversationDetail(conversationViewModel: conversationViewModel))
+        self.accountsService.currentAccount().subscribe(onSuccess: { [unowned self] (account) in
+            let conversationViewModel = ConversationViewModel(with: self.injectionBag)
+            let conversation = self.conversationService.findConversation(withRingId: ringId,
+                                                                         withAccountId: account.id)
+            conversationViewModel.conversation = conversation
+            self.stateSubject.onNext(ConversationsState.conversationDetail(conversationViewModel: conversationViewModel))
+        }, onError: { [unowned self] (error) in
+            self.log.error("No account available")
+        }).disposed(by: self.disposeBag)
     }
 }
+
+//extension ContactRequestsViewModel {
+//
+//    fileprivate func lookupUserName(withItem item: ContactRequestItem) {
+//        self.nameService.usernameLookupStatus.asObservable()
+//            .filter({ lookupNameResponse in
+//                return lookupNameResponse.address == item.contactRequest.ringId
+//            })
+//            .subscribe(onNext: { lookupNameResponse in
+//                if lookupNameResponse.state == .found && !lookupNameResponse.name.isEmpty {
+//                    item.userName.value = lookupNameResponse.name
+//                } else {
+//                    item.userName.value = lookupNameResponse.address
+//                }
+//            })
+//            .disposed(by: self.disposeBag)
+//
+//                self.nameService.lookupAddress(withAccount: (accountsService.currentAccount?.id)!,
+//                                                      nameserver: "",
+//                                                      address: item.contactRequest.ringId)
+//    }
+//
+//}
diff --git a/Ring/Ring/Services/ContactRequestsManager.swift b/Ring/Ring/Services/ContactRequestsManager.swift
new file mode 100644
index 0000000000000000000000000000000000000000..07a3fc2411b49ed98f90e8867d76a75d80a3fa2f
--- /dev/null
+++ b/Ring/Ring/Services/ContactRequestsManager.swift
@@ -0,0 +1,92 @@
+/*
+ *  Copyright (C) 2017 Savoir-faire Linux Inc.
+ *
+ *  Author: Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
+ */
+
+import RxSwift
+
+final class ContactRequestsManager {
+
+    let accountsService: NewAccountsService
+    let contactsService: ContactsService
+    let conversationsService: ConversationsService
+    let presenceService: PresenceService
+
+    let disposeBag = DisposeBag()
+
+    init(with accountsService: NewAccountsService,
+         contactsService: ContactsService,
+         conversationsService: ConversationsService,
+         presenceService: PresenceService) {
+        self.accountsService = accountsService
+        self.contactsService = contactsService
+        self.conversationsService = conversationsService
+        self.presenceService = presenceService
+
+        self.handleNewContactsRequests()
+    }
+
+    private func handleNewContactsRequests() {
+        let currentAccountObservable = self.accountsService.currentAccount().asObservable()
+        let contactRequestsObservable = self.contactsService.contactRequests.asObservable()
+
+        Observable
+            .combineLatest(currentAccountObservable, contactRequestsObservable) { [weak self] (account, requests) in
+                guard let ringId = requests.last?.ringId else { return }
+                self?.conversationsService.generateMessage(ofType: GeneratedMessageType.receivedContactRequest,
+                                                           forRindId: ringId,
+                                                           forAccount: account)
+            }
+            .subscribe()
+            .disposed(by: self.disposeBag)
+    }
+
+    func accept(contactRequest: ContactRequestModel, account: AccountModel) -> Completable {
+        let accountHelper = AccountModelHelper(withAccount: account)
+        let completable = self.contactsService.accept(contactRequest: contactRequest, withAccount: account)
+            .andThen(self.conversationsService.saveMessage(withId: "",
+                                                           withContent: GeneratedMessageType.contactRequestAccepted.rawValue,
+                                                           byAuthor: accountHelper.ringId!,
+                                                           toConversationWith: contactRequest.ringId,
+                                                           currentAccountId: account.id,
+                                                           generated: true))
+            .andThen(self.presenceService.subscribeBuddy(withAccountId: account.id,
+                                                         withUri: contactRequest.ringId,
+                                                         withFlag: true))
+        if let vcard = contactRequest.vCard {
+            completable.andThen(self.contactsService.saveVCard(vCard: vcard,
+                                                               forContactWithRingId: contactRequest.ringId))
+        }
+        return completable
+    }
+
+    func discard(contactRequest: ContactRequestModel, account: AccountModel) -> Completable {
+        return self.contactsService.discard(contactRequest: contactRequest,
+                                            withAccount: account)
+    }
+
+    func ban(contactRequest: ContactRequestModel, account: AccountModel) -> Completable {
+        let discard = self.contactsService.discard(contactRequest: contactRequest,
+                                                   withAccount: account)
+        let remove = self.contactsService.removeContact(withRingId: contactRequest.ringId,
+                                                        ban: true,
+                                                        withAccount: account)
+        return discard.andThen(remove)
+    }
+
+}
diff --git a/Ring/Ring/Services/ContactsService.swift b/Ring/Ring/Services/ContactsService.swift
index 8fae60a097f39377ef4ddecd228436b020d37fb8..07f41ba5d5ccd414a054ae73032ba8dd958c79f1 100644
--- a/Ring/Ring/Services/ContactsService.swift
+++ b/Ring/Ring/Services/ContactsService.swift
@@ -93,22 +93,22 @@ class ContactsService {
         }
     }
 
-    func accept(contactRequest: ContactRequestModel, withAccount account: AccountModel) -> Observable<Void> {
-        return Observable.create { [unowned self] observable in
+    func accept(contactRequest: ContactRequestModel, withAccount account: AccountModel) -> Completable {
+        return Completable.create { [unowned self] completable in
             let success = self.contactsAdapter.acceptTrustRequest(fromContact: contactRequest.ringId,
                                                                   withAccountId: account.id)
             if success {
-                observable.on(.completed)
+                completable(.completed)
             } else {
-                observable.on(.error(ContactServiceError.acceptTrustRequestFailed))
+                completable(.error(ContactServiceError.acceptTrustRequestFailed))
             }
 
-            return Disposables.create { }
+            return Disposables.create ()
         }
     }
 
-    func discard(contactRequest: ContactRequestModel, withAccount account: AccountModel) -> Observable<Void> {
-        return Observable.create { [unowned self] observable in
+    func discard(contactRequest: ContactRequestModel, withAccount account: AccountModel) -> Completable {
+        return Completable.create { [unowned self] completable in
             let success = self.contactsAdapter.discardTrustRequest(fromContact: contactRequest.ringId,
                                                                    withAccountId: account.id)
 
@@ -116,11 +116,11 @@ class ContactsService {
             self.removeContactRequest(withRingId: contactRequest.ringId)
 
             if success {
-                observable.on(.completed)
+                completable(.completed)
             } else {
-                observable.on(.error(ContactServiceError.diacardTrusRequestFailed))
+                completable(.error(ContactServiceError.diacardTrusRequestFailed))
             }
-            return Disposables.create { }
+            return Disposables.create()
         }
     }
 
@@ -150,16 +150,17 @@ class ContactsService {
         }
     }
 
-    func removeContact(contact: ContactModel, ban: Bool, withAccount account: AccountModel) -> Observable<Void> {
+    func removeContact(contact: ContactModel, ban: Bool, withAccount account: AccountModel) -> Completable {
         return removeContact(withRingId: contact.ringId, ban: ban, withAccount: account)
     }
 
-    func removeContact(withRingId ringId: String, ban: Bool, withAccount account: AccountModel) -> Observable<Void> {
-        return Observable.create { [unowned self] observable in
+    func removeContact(withRingId ringId: String, ban: Bool, withAccount account: AccountModel) -> Completable {
+        return Completable.create { [unowned self] completable in
             self.contactsAdapter.removeContact(withURI: ringId, accountId: account.id, ban: ban)
             self.removeContactRequest(withRingId: ringId)
-            observable.on(.completed)
-            return Disposables.create { }
+            completable(.completed)
+
+            return Disposables.create()
         }
     }
 
@@ -249,9 +250,10 @@ extension ContactsService: ContactsAdapterDelegate {
 
     // MARK: - profile
 
-    func saveVCard(vCard: CNContact, forContactWithRingId ringID: String) -> Observable<Void> {
-        let vCardSaved = VCardUtils.saveVCard(vCard: vCard, withName: ringID, inFolder: VCardFolders.contacts.rawValue)
-        return vCardSaved
+    func saveVCard(vCard: CNContact, forContactWithRingId ringID: String) -> Completable {
+        return VCardUtils.saveVCard(vCard: vCard,
+                                    withName: ringID,
+                                    inFolder: VCardFolders.contacts.rawValue)
     }
 
     func loadVCard(forContactWithRingId ringID: String) -> Single<CNContact> {
diff --git a/Ring/Ring/Services/PresenceService.swift b/Ring/Ring/Services/PresenceService.swift
index c318df8967307f5ae9643d22feb4851fae2c5f75..c19ee8a2dcff8ed9b3733e542d6d7d5084c79475 100644
--- a/Ring/Ring/Services/PresenceService.swift
+++ b/Ring/Ring/Services/PresenceService.swift
@@ -39,19 +39,26 @@ class PresenceService {
         PresenceAdapter.delegate = self
     }
 
-    func subscribeBuddies(withAccount account: AccountModel, withContacts contacts: [ContactModel]) {
+    func subscribeBuddies(withAccount account: AccountModel, withContacts contacts: [ContactModel]) -> Completable {
+        var subscriptions = [Completable]()
         for contact in contacts {
-            subscribeBuddy(withAccountId: account.id,
-                           withUri: contact.ringId,
-                           withFlag: true)
+            let subscription = self.subscribeBuddy(withAccountId: account.id,
+                                                   withUri: contact.ringId,
+                                                   withFlag: true)
+            subscriptions.append(subscription)
         }
+        return Completable.merge(subscriptions)
     }
 
     func subscribeBuddy(withAccountId accountId: String,
                         withUri uri: String,
-                        withFlag flag: Bool) {
-        presenceAdapter.subscribeBuddy(withURI: uri, withAccountId: accountId, withFlag: flag)
-        contactPresence[uri] = false
+                        withFlag flag: Bool) -> Completable {
+        return Completable.create(subscribe: { [unowned self] (completable) -> Disposable in
+            self.presenceAdapter.subscribeBuddy(withURI: uri, withAccountId: accountId, withFlag: flag)
+            self.contactPresence[uri] = false
+            completable(.completed)
+            return Disposables.create()
+        })
     }
 }