diff --git a/enzevalos_iphone.xcodeproj/project.pbxproj b/enzevalos_iphone.xcodeproj/project.pbxproj
index aa519c40f4d8c8c5a8d5caa7f11e92e1e856e273..4bdfef76ea68cb6d844d3f2ff6fc30997146fd0a 100644
--- a/enzevalos_iphone.xcodeproj/project.pbxproj
+++ b/enzevalos_iphone.xcodeproj/project.pbxproj
@@ -67,6 +67,8 @@
 		472F398C1E2519C8009260FB /* CNContactExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F398B1E2519C8009260FB /* CNContactExtension.swift */; };
 		472F398E1E251B8D009260FB /* MailAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F398D1E251B8D009260FB /* MailAddress.swift */; };
 		472F39901E252470009260FB /* CNMailAddressesExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 472F398F1E252470009260FB /* CNMailAddressesExtension.swift */; };
+		4733B1CE25262CDB00AB5600 /* PersistentDataProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4733B1CD25262CDB00AB5600 /* PersistentDataProtocol.swift */; };
+		4733B1E52527196100AB5600 /* PersistentDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4733B1E42527196100AB5600 /* PersistentDataProvider.swift */; };
 		47358D92244A5AEA000116D7 /* SelectableTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47358D91244A5AEA000116D7 /* SelectableTextView.swift */; };
 		474054982244D7A9007CF83B /* MailServerConfigurationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 474054972244D7A9007CF83B /* MailServerConfigurationTest.swift */; };
 		474994022261E4E6000F8DA5 /* SimpleSendIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 474994012261E4E6000F8DA5 /* SimpleSendIcon.swift */; };
@@ -411,6 +413,8 @@
 		472F398B1E2519C8009260FB /* CNContactExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CNContactExtension.swift; sourceTree = "<group>"; };
 		472F398D1E251B8D009260FB /* MailAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MailAddress.swift; sourceTree = "<group>"; };
 		472F398F1E252470009260FB /* CNMailAddressesExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CNMailAddressesExtension.swift; sourceTree = "<group>"; };
+		4733B1CD25262CDB00AB5600 /* PersistentDataProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentDataProtocol.swift; sourceTree = "<group>"; };
+		4733B1E42527196100AB5600 /* PersistentDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistentDataProvider.swift; sourceTree = "<group>"; };
 		47358D91244A5AEA000116D7 /* SelectableTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableTextView.swift; sourceTree = "<group>"; };
 		474054972244D7A9007CF83B /* MailServerConfigurationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MailServerConfigurationTest.swift; sourceTree = "<group>"; };
 		474994012261E4E6000F8DA5 /* SimpleSendIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleSendIcon.swift; sourceTree = "<group>"; };
@@ -1387,6 +1391,8 @@
 				47FAE3112524BFDB005A1BCB /* PersistentDataError.swift */,
 				47FAE31B2524C07B005A1BCB /* CoreMail.swift */,
 				47FAE3482524FB58005A1BCB /* CoreAddress.swift */,
+				4733B1CD25262CDB00AB5600 /* PersistentDataProtocol.swift */,
+				4733B1E42527196100AB5600 /* PersistentDataProvider.swift */,
 			);
 			path = persistentData;
 			sourceTree = "<group>";
@@ -2332,6 +2338,7 @@
 				0EF148082422572500B3C198 /* general-helpers.c in Sources */,
 				F113C3851F30D06800E7F1D6 /* QRScannerView.swift in Sources */,
 				97BDE0432429188500B0BF03 /* BadgeProgressView.swift in Sources */,
+				4733B1CE25262CDB00AB5600 /* PersistentDataProtocol.swift in Sources */,
 				47C822682438A85C005BCE73 /* SenderDetails.swift in Sources */,
 				477670C6228454F700043604 /* ButtonCell.swift in Sources */,
 				47C8225824379EAE005BCE73 /* FloatingActionButton.swift in Sources */,
@@ -2357,6 +2364,7 @@
 				478154A921FF3FF400A931EC /* Invitation.swift in Sources */,
 				47A2A56E2350A4EF0013883D /* MoreInformationViewController.swift in Sources */,
 				F1AF938F1E2D04BA00755128 /* CustomCells.swift in Sources */,
+				4733B1E52527196100AB5600 /* PersistentDataProvider.swift in Sources */,
 				8428A8711F436A1E007649A5 /* GamificationStatusViewController.swift in Sources */,
 				F1866C86201F707200B72453 /* EmailHelper.m in Sources */,
 				47F79241203492E3005E7DB6 /* KeyRecord+CoreDataProperties.swift in Sources */,
diff --git a/enzevalos_iphone/persistentData/CoreAddress.swift b/enzevalos_iphone/persistentData/CoreAddress.swift
index 2693b4c6ba46d546e390bcec7aa8fe600c7dc8e3..913beaa72abb0e5213c553cff37bc38d70799356 100644
--- a/enzevalos_iphone/persistentData/CoreAddress.swift
+++ b/enzevalos_iphone/persistentData/CoreAddress.swift
@@ -6,13 +6,26 @@
 //  Copyright © 2020 fu-berlin. All rights reserved.
 //
 
-extension CoreAddress {
-    func update(addressProperties: AddressProperties){
+extension CoreAddress: PersistentDataProtocol {
+    typealias T = AddressProperties
+    static var entityName = "CoreAddress"
+    
+    func update(with addressProperties: AddressProperties){
         email = addressProperties.email
     }
 }
 
-struct AddressProperties {
+struct AddressProperties: DataPropertyProtocol {
+    var entityName = "CoreAddress"
+    
+    func update(m: Any) -> Bool {
+        if let m = m as? CoreAddress {
+            m.update(with: self)
+            return true
+        }
+        return false
+    }
+    
     let email: String
     
     var dictionary: [String: Any] {
diff --git a/enzevalos_iphone/persistentData/CoreMail.swift b/enzevalos_iphone/persistentData/CoreMail.swift
index d33f5fff2d06cc4b4cdc14c99bc374be5bf977f9..8e78c79ff01e8d194230fe8149c1aac363432626 100644
--- a/enzevalos_iphone/persistentData/CoreMail.swift
+++ b/enzevalos_iphone/persistentData/CoreMail.swift
@@ -9,12 +9,16 @@
 /**
  Managed object subclass extension for the Quake entity.
  */
-extension CoreMail {
+extension CoreMail: PersistentDataProtocol {
+    static let entityName = "CoreMail"
     
     func update(with mailProperties: MailProperties) {
         messageID = mailProperties.messageID
         subject = mailProperties.subject
     }
+    
+    func update(mail: IncomingMail) {
+    }
 }
 
 /**
@@ -23,13 +27,17 @@ extension CoreMail {
  
  What about attached keys (public, secret keys)
  */
-struct MailProperties {
+struct MailProperties: DataPropertyProtocol {  
+    
+    var entityName = "CoreMail"
+    
     
     // Header properties
     let messageID: String
     let subject: String
     let date: Date
     let flags: Int16
+    let from: AddressProperties
     
     // Content properties
     var body: String = ""
@@ -46,4 +54,12 @@ struct MailProperties {
         dic["subject"] = subject
         return dic
     }
+    
+    func update(m: Any) -> Bool {
+        if let m = m as? CoreMail {
+            m.update(with: self)
+            return true
+        }
+        return false
+    }
 }
diff --git a/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents
index 07e1b2666fa8bede7fffe2b8255d3fdc33a0df34..bc7537718072638a381cfd352045afaac3ec7119 100644
--- a/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents
+++ b/enzevalos_iphone/persistentData/DataModel.xcdatamodeld/DataModel.xcdatamodel/contents
@@ -1,21 +1,54 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
 <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17192" systemVersion="19H2" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
-    <entity name="CoreAddress" representedClassName="CoreAddress" syncable="YES" codeGenerationType="class">
+    <entity name="AbstractKeyRecord" representedClassName="AbstractKeyRecord" isAbstract="YES" syncable="YES" codeGenerationType="class">
+        <attribute name="cryptoProtocol" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="id" attributeType="String"/>
+    </entity>
+    <entity name="Address" representedClassName="Address" syncable="YES" codeGenerationType="class">
         <attribute name="email" attributeType="String"/>
         <relationship name="inFromField" toMany="YES" deletionRule="Nullify" destinationEntity="CoreMail" inverseName="fromAddress" inverseEntity="CoreMail"/>
+        <uniquenessConstraints>
+            <uniquenessConstraint>
+                <constraint value="email"/>
+            </uniquenessConstraint>
+        </uniquenessConstraints>
     </entity>
     <entity name="CoreMail" representedClassName="CoreMail" syncable="YES" codeGenerationType="class">
+        <attribute name="body" attributeType="String"/>
+        <attribute name="date" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="encryptionState" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="flag" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
         <attribute name="messageID" attributeType="String"/>
-        <attribute name="subject" optional="YES" attributeType="String"/>
-        <relationship name="fromAddress" maxCount="1" deletionRule="Nullify" destinationEntity="CoreAddress" inverseName="inFromField" inverseEntity="CoreAddress"/>
+        <attribute name="signatureState" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="subject" attributeType="String"/>
+        <attribute name="xMailer" optional="YES" attributeType="String"/>
+        <relationship name="bccAddresses" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Address"/>
+        <relationship name="ccAddresses" toMany="YES" deletionRule="Nullify" destinationEntity="Address"/>
+        <relationship name="fromAddress" maxCount="1" deletionRule="Nullify" destinationEntity="Address" inverseName="inFromField" inverseEntity="Address"/>
+        <relationship name="toAddresses" maxCount="1" deletionRule="Nullify" destinationEntity="Address"/>
         <uniquenessConstraints>
             <uniquenessConstraint>
                 <constraint value="messageID"/>
             </uniquenessConstraint>
         </uniquenessConstraints>
     </entity>
+    <entity name="PublicKeyRecord" representedClassName="PublicKeyRecord" syncable="YES" codeGenerationType="class">
+        <attribute name="discoveryDate" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="lastSeenInAutocryptHeader" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="lastSeenSignedMail" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+        <attribute name="origin" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
+        <attribute name="preferEncryption" attributeType="Integer 16" usesScalarValueType="YES"/>
+    </entity>
+    <entity name="SecretKeyRecord" representedClassName="SecretKeyRecord" parentEntity="AbstractKeyRecord" syncable="YES" codeGenerationType="class">
+        <attribute name="exported" attributeType="Boolean" usesScalarValueType="YES"/>
+        <attribute name="importedDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
+    </entity>
+    <fetchRequest name="FetchAddressesRequest" entity="Address" fetchLimit="200" fetchBatchSize="200"/>
     <elements>
-        <element name="CoreMail" positionX="-54" positionY="-9" width="128" height="88"/>
-        <element name="CoreAddress" positionX="-36" positionY="27" width="128" height="73"/>
+        <element name="Address" positionX="-36" positionY="27" width="128" height="28"/>
+        <element name="CoreMail" positionX="-54" positionY="-9" width="128" height="28"/>
+        <element name="AbstractKeyRecord" positionX="-27" positionY="54" width="128" height="28"/>
+        <element name="SecretKeyRecord" positionX="-9" positionY="63" width="128" height="28"/>
+        <element name="PublicKeyRecord" positionX="9" positionY="63" width="128" height="28"/>
     </elements>
 </model>
\ No newline at end of file
diff --git a/enzevalos_iphone/persistentData/DataProvider.swift b/enzevalos_iphone/persistentData/DataProvider.swift
index f701c9ec3edea842a9cef74ab0bb0f1098549f41..f5495661c974775e04b36b8eeb3f2e09e39613b2 100644
--- a/enzevalos_iphone/persistentData/DataProvider.swift
+++ b/enzevalos_iphone/persistentData/DataProvider.swift
@@ -7,17 +7,13 @@
 //
 
 import CoreData
-let MAILNAME = "CoreMail"
-
 
 /*
  One Entity needs a name, type, a struct with dict and a update function with the struct...
  */
 
 public class DataProvider {
-    
-    let fastImport = false
-    
+        
     // MARK: - Core Data
     
     /**
@@ -64,9 +60,14 @@ public class DataProvider {
         return taskContext
     }
     
+    func importMails(from emails: [MailProperties], completionHandler: @escaping (Error?) -> Void, withBIR: Bool = true){
+        importData(from: emails, completionHandler: completionHandler, withBIR: withBIR)
+    }
     
-    func importMails(from emails: [MailProperties], completionHandler: @escaping (Error?) -> Void, withBIR: Bool = true) {
-        guard !emails.isEmpty else {
+    
+    
+    func importData(from data: [DataPropertyProtocol], completionHandler: @escaping (Error?) -> Void, withBIR: Bool = true) {
+        guard !data.isEmpty else {
             completionHandler(nil)
             return
         }
@@ -75,10 +76,10 @@ public class DataProvider {
         DispatchQueue.global(qos: .background).async {
             do {
                 if withBIR {
-                    try self.importMailsUsingBIR(from: emails)
+                    try self.importDataUsingBIR(from: data)
                 }
                 else {
-                    try self.importEmailsBeforeBIR(from: emails)
+                    try self.importDataBeforeBIR(from: data)
                 }
             } catch {
                 performError = error
@@ -95,17 +96,17 @@ public class DataProvider {
      Uses NSBatchInsertRequest (BIR) to import a list of emails into the Core Data store on a private queue.
      NSBatchInsertRequest is available since iOS 13 and macOS 10.15.
     */
-    private func importMailsUsingBIR(from emails: [MailProperties]) throws {
+    private func importDataUsingBIR(from data: [DataPropertyProtocol]) throws {
         var performError: Error?
         
         let taskContext = self.newTaskContext()
         taskContext.performAndWait {
-            let batchInsert = self.newBatchInsertRequest(with: emails)
-            batchInsert.resultType = .statusOnly
-            
-            if let batchInsertResult = try? taskContext.execute(batchInsert) as? NSBatchInsertResult,
-                let success = batchInsertResult.result as? Bool, success {
-                return
+            if let batchInsert = self.newBatchInsertRequest(with: data) {
+                batchInsert.resultType = .statusOnly
+                if let batchInsertResult = try? taskContext.execute(batchInsert) as? NSBatchInsertResult,
+                    let success = batchInsertResult.result as? Bool, success {
+                    return
+                }
             }
             performError = PersistentDataError.batchInsertError
         }
@@ -114,13 +115,17 @@ public class DataProvider {
         }
     }
 
-    private func newBatchInsertRequest(with mails: [MailProperties]) -> NSBatchInsertRequest {
+    private func newBatchInsertRequest(with data: [DataPropertyProtocol]) -> NSBatchInsertRequest? {
+        guard data.count > 0 else {
+            return nil
+        }
+        let name = data.first!.entityName
         let batchInsert: NSBatchInsertRequest
         var index = 0
-        let total = mails.count
-        batchInsert = NSBatchInsertRequest(entityName: MAILNAME, dictionaryHandler: { dictionary in
+        let total = data.count
+        batchInsert = NSBatchInsertRequest(entityName: name, dictionaryHandler: { dictionary in
             guard index < total else { return true }
-            dictionary.addEntries(from: mails[index].dictionary)
+            dictionary.addEntries(from: data[index].dictionary)
             index += 1
             return false
         })
@@ -131,12 +136,12 @@ public class DataProvider {
      Imports a JSON dictionary into the Core Data store on a private queue,
      processing the record in batches to avoid a high memory footprint.
     */
-    private func importEmailsBeforeBIR(from emails: [MailProperties]) throws {
-        guard !emails.isEmpty else { return }
+    private func importDataBeforeBIR(from data: [DataPropertyProtocol]) throws {
+        guard !data.isEmpty else { return }
                 
         // Process records in batches to avoid a high memory footprint.
         let batchSize = 256
-        let count = emails.count
+        let count = data.count
         
         // Determine the total number of batches.
         var numBatches = count / batchSize
@@ -150,7 +155,7 @@ public class DataProvider {
             
             // Create a batch for this range from the decoded JSON.
             // Stop importing if any batch is unsuccessful.
-            try importOneBatch(Array(emails[range]))
+            try importOneBatch(Array(data[range]))
         }
     }
     
@@ -163,21 +168,25 @@ public class DataProvider {
      catches throws within the closure and uses a return value to indicate
      whether the import is successful.
     */
-    private func importOneBatch(_ emails: [MailProperties]) throws {
+    private func importOneBatch(_ data: [DataPropertyProtocol]) throws {
+        guard data.count > 0 else {
+            throw PersistentDataError.creationError
+        }
+        
+        let name = data.first!.entityName
+
         let taskContext = newTaskContext()
         var performError: Error?
         
-        // taskContext.performAndWait runs on the URLSession's delegate queue
-        // so it won’t block the main thread.
         taskContext.performAndWait {
             // Create a new record for each quake in the batch.
-            for m in emails {
-                // Create a Quake managed object on the private queue context.
-                guard let email = NSEntityDescription.insertNewObject(forEntityName: MAILNAME, into: taskContext) as? CoreMail else {
+            for m in data {
+                // Create a managed object on the private queue context.
+                let item = NSEntityDescription.insertNewObject(forEntityName: name, into: taskContext)
+                if !m.update(m: item) {
                     performError = PersistentDataError.creationError
                     return
                 }
-                email.update(with: m)
             }
             
             // Save all insertions and deletions from the context to the store.
@@ -204,23 +213,37 @@ public class DataProvider {
     func deleteAll(completionHandler: @escaping (Error?) -> Void) {
         let taskContext = newTaskContext()
         taskContext.perform {
-            let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: MAILNAME)
-            let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
-            batchDeleteRequest.resultType = .resultTypeCount
-            
-            // Execute the batch insert
-            if let batchDeleteResult = try? taskContext.execute(batchDeleteRequest) as? NSBatchDeleteResult,
-                batchDeleteResult.result != nil {
-                completionHandler(nil)
-
-            } else {
-                completionHandler(PersistentDataError.batchDeleteError)
+            for name in [CoreAddress.entityName, CoreMail.entityName] {
+                let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: name)
+                let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
+                batchDeleteRequest.resultType = .resultTypeCount
+                
+                // Execute the batch insert
+                guard let batchDeleteResult = try? taskContext.execute(batchDeleteRequest) as? NSBatchDeleteResult,
+                    batchDeleteResult.result != nil else {
+                    completionHandler(PersistentDataError.batchDeleteError)
+                    return
+                }
             }
+            completionHandler(nil)
         }
     }
 
     // MARK: - NSFetchedResultsController
     
+    lazy var fetchAddressResultController: NSFetchedResultsController<CoreAddress> = {
+        let freq = NSFetchRequest<CoreAddress>(entityName: CoreAddress.entityName)
+        freq.sortDescriptors = [NSSortDescriptor(key: "email", ascending: false)]
+
+        let controller = NSFetchedResultsController(fetchRequest: freq, managedObjectContext: persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
+        do {
+            try controller.performFetch()
+        } catch {
+            fatalError("Unresolved error \(error)")
+        }
+        return controller
+    }()
+    
     /**
          A fetched results controller delegate to give consumers a chance to update
          the user interface when content changes.
@@ -228,12 +251,12 @@ public class DataProvider {
         weak var fetchedResultsControllerDelegate: NSFetchedResultsControllerDelegate?
         
         /**
-         A fetched results controller to fetch Quake records sorted by time.
+         A fetched results controller to fetch EMail records sorted by time.
          */
         lazy var fetchedResultsController: NSFetchedResultsController<CoreMail> = {
             
             // Create a fetch request for the Quake entity sorted by time.
-            let fetchRequest = NSFetchRequest<CoreMail>(entityName: MAILNAME)
+            let fetchRequest = NSFetchRequest<CoreMail>(entityName: CoreMail.entityName)
             // Create a fetched results controller and set its fetch request, context, and delegate.
             fetchRequest.sortDescriptors = [NSSortDescriptor(key: "messageID", ascending: false)]
             fetchRequest.propertiesToFetch = ["messageID", "subject"]
@@ -255,7 +278,7 @@ public class DataProvider {
      Resets viewContext and refetches the data from the store.
      */
     func resetAndRefetch() {
-        persistentContainer.viewContext.reset()
+        reset()
         do {
             try fetchedResultsController.performFetch()
         } catch {
@@ -263,6 +286,10 @@ public class DataProvider {
         }
     }
     
+    func reset(){
+        persistentContainer.viewContext.reset()
+    }
+    
     // MARK: - NSPersistentStoreRemoteChange handler
 
     /**
diff --git a/enzevalos_iphone/persistentData/PersistentDataProtocol.swift b/enzevalos_iphone/persistentData/PersistentDataProtocol.swift
new file mode 100644
index 0000000000000000000000000000000000000000..954e57af691c48f622caa29718a1ebd0ad8eb183
--- /dev/null
+++ b/enzevalos_iphone/persistentData/PersistentDataProtocol.swift
@@ -0,0 +1,23 @@
+//
+//  PersistentDataProtocol.swift
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 01.10.20.
+//  Copyright © 2020 fu-berlin. All rights reserved.
+//
+
+protocol PersistentDataProtocol {
+    associatedtype T
+
+    func update(with properties: T)
+}
+
+protocol DataPropertyProtocol {
+    var entityName: String { get }
+    var dictionary: [String: Any] { get }
+
+    func update(m: Any) -> Bool 
+}
+
+
+
diff --git a/enzevalos_iphone/persistentData/PersistentDataProvider.swift b/enzevalos_iphone/persistentData/PersistentDataProvider.swift
new file mode 100644
index 0000000000000000000000000000000000000000..36e63744f81ef570a83cca0eefca5706f5e34669
--- /dev/null
+++ b/enzevalos_iphone/persistentData/PersistentDataProvider.swift
@@ -0,0 +1,154 @@
+//
+//  PersistentDataProvider.swift
+//  enzevalos_iphone
+//
+//  Created by Oliver Wiese on 02.10.20.
+//  Copyright © 2020 fu-berlin. All rights reserved.
+//
+
+import CoreData
+
+class PersitentDataProvider {
+    // MARK: - Core Data
+    
+    /**
+     A persistent container to set up the Core Data stack.
+    */
+    lazy var persistentContainer: NSPersistentContainer = {
+        let container = NSPersistentContainer(name: "DataModel")
+        
+        // Enable remote notifications
+        guard let description = container.persistentStoreDescriptions.first else {
+            fatalError("Failed to retrieve a persistent store description.")
+        }
+        description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
+    
+        container.loadPersistentStores { storeDesription, error in
+            guard error == nil else {
+                fatalError("Unresolved error \(error!)")
+            }
+        }
+
+        // This sample refreshes UI by refetching data, so doesn't need to merge the changes.
+        container.viewContext.automaticallyMergesChangesFromParent = false
+        container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
+        container.viewContext.undoManager = nil
+        container.viewContext.shouldDeleteInaccessibleFaults = true
+        
+        // Observe Core Data remote change notifications.
+        NotificationCenter.default.addObserver(
+            self, selector: #selector(type(of: self).storeRemoteChange(_:)),
+            name: .NSPersistentStoreRemoteChange, object: nil)
+        return container
+    }()
+    
+    /**
+     Creates and configures a private queue context.
+    */
+    private func newTaskContext() -> NSManagedObjectContext {
+        // Create a private queue context.
+        let taskContext = persistentContainer.newBackgroundContext()
+        taskContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
+        // Set unused undoManager to nil for macOS (it is nil by default on iOS)
+        // to reduce resource requirements.
+        taskContext.undoManager = nil
+        return taskContext
+    }
+    
+    
+    func importMails(from mails: [MailProperties], completionHandler: @escaping (Error?) -> Void) {
+        guard !mails.isEmpty else {
+            completionHandler(nil)
+            return
+        }
+        var performError: Error? = nil
+        
+        DispatchQueue.global(qos: .background).async {
+            // Process records in batches to avoid a high memory footprint.
+            let batchSize = 30
+            let count = mails.count
+            
+            if count < batchSize {
+                do {
+                    try self.importOneBatch(mails)
+                } catch {
+                    performError = error
+                }
+            } else {
+                // Determine the total number of batches.
+                var numBatches = count / batchSize
+                numBatches += count % batchSize > 0 ? 1 : 0
+                
+                for batchNumber in 0 ..< numBatches {
+                    // Determine the range for this batch.
+                    let batchStart = batchNumber * batchSize
+                    let batchEnd = batchStart + min(batchSize, count - batchNumber * batchSize)
+                    let range = batchStart..<batchEnd
+                    
+                    // Create a batch for this range from the decoded JSON.
+                    // Stop importing if any batch is unsuccessful.
+                    do {
+                        try self.importOneBatch(Array(mails[range]))
+                    } catch {
+                        performError = error
+                    }
+                }
+            }
+           
+            DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
+                completionHandler(performError)
+            })
+                
+        }
+    }
+    
+    private func importOneBatch(_ mails: [MailProperties]) throws {
+        guard mails.count > 0 else {
+            throw PersistentDataError.creationError
+        }
+
+        let taskContext = newTaskContext()
+        var performError: Error?
+        
+        taskContext.performAndWait {
+            // Create a new record for each mail in the batch.
+            for m in mails {
+                // Create a managed object on the private queue context.
+                let from = NSEntityDescription.insertNewObject(forEntityName: CoreAddress.entityName, into: taskContext) as! CoreAddress
+                from.update(with: m.from)
+
+                let mail = NSEntityDescription.insertNewObject(forEntityName: CoreMail.entityName, into: taskContext) as! CoreMail
+                mail.fromAddress = from
+                mail.messageID = m.messageID
+                
+                // add relationship -> Update?
+               
+            }
+            
+            // Save all insertions and deletions from the context to the store.
+            if taskContext.hasChanges {
+                do {
+                    try taskContext.save()
+                } catch {
+                    performError = error
+                    return
+                }
+                // Reset the taskContext to free the cache and lower the memory footprint.
+                taskContext.reset()
+            }
+        }
+        
+        if let error = performError {
+            throw error
+        }
+    }
+    
+    /**
+     Handles remote store change notifications (.NSPersistentStoreRemoteChange).
+     storeRemoteChange runs on the queue where the changes were made.
+     */
+    @objc
+    func storeRemoteChange(_ notification: Notification) {
+         //print("\(#function): Got a persistent store remote change notification!")
+    }
+}
diff --git a/enzevalos_iphoneTests/CoreAddressTest.swift b/enzevalos_iphoneTests/CoreAddressTest.swift
index 4f6aaf27f05a93c75d3d3fe70c6e07c7981f7941..4cffb4a1bde92f93a66c8673b455fa58852d8d60 100644
--- a/enzevalos_iphoneTests/CoreAddressTest.swift
+++ b/enzevalos_iphoneTests/CoreAddressTest.swift
@@ -21,16 +21,28 @@ class CoreAddressTest: XCTestCase {
     }
 
     func testCreateAddress() throws {
+        let n = 1
+        
         let addr1 = AddressProperties(email: "vic@example.com")
+        let addr2 = AddressProperties(email: "vic@example.com")
+        let importExpectation = expectation(description: "Import new addresses!")
+        let addresses = [addr1, addr2]
         
-        let addresses = [addr1]
-    }
-
-    func testPerformanceExample() throws {
-        // This is an example of a performance test case.
-        self.measure {
-            // Put the code you want to measure the time of here.
+        provider.importData(from: addresses, completionHandler: {error in
+            if let error = error {
+                XCTFail("Error while importing addresses! \(error)")
+            }
+            importExpectation.fulfill()
+        })
+        wait(for: [importExpectation], timeout: TimeInterval(20))
+        provider.reset()
+        let frc = provider.fetchAddressResultController
+        XCTAssertEqual(frc.fetchedObjects?.count ?? 0, n, "Missing message! \(frc.fetchedObjects?.count ?? 0)")
+        if let objects = frc.fetchedObjects {
+            for obj in objects {
+                print("#from mails: \(obj.inFromField?.count ?? -1)")
+            }
         }
+        
     }
-
 }
diff --git a/enzevalos_iphoneTests/CoreMailTest.swift b/enzevalos_iphoneTests/CoreMailTest.swift
index 79574a89f647085160a835ea4017c16bf0e97ec7..f721f083c1ecfeac42f0eabfeb58a84d9993ae39 100644
--- a/enzevalos_iphoneTests/CoreMailTest.swift
+++ b/enzevalos_iphoneTests/CoreMailTest.swift
@@ -12,6 +12,7 @@ import XCTest
 class CoreMailTest: XCTestCase {
 
     var provider = DataProvider()
+    var p2 = PersitentDataProvider()
     
     override func setUpWithError() throws {
         provider.deleteAll(completionHandler: {_ in })
@@ -20,7 +21,74 @@ class CoreMailTest: XCTestCase {
     override func tearDownWithError() throws {
         provider.deleteAll(completionHandler: {_ in })
     }
+    
+    func testImportMail(){
+        let addr = "vic@example.com"
+        let addr1 = AddressProperties(email: addr)
+        let importExpectation = expectation(description: "Import new msg!")
+
+        let m1 = MailProperties(messageID: "1", subject: "MSG1", date: Date(), flags: 0, from: addr1, body: "THIS IS MY SECOND MSG1", signatureState: 0, encryptionState: 0)
+        let m2 = MailProperties(messageID: "2", subject: "MSG2", date: Date(), flags: 0, from: addr1, body: "THIS IS MY SECOND MSG1", signatureState: 0, encryptionState: 0)
 
+        p2.importMails(from: [m1,m2], completionHandler: {error in
+            if let error = error {
+                XCTFail("Error while importing messages! \(error)")
+            }
+            importExpectation.fulfill()})
+        wait(for: [importExpectation], timeout: TimeInterval(20))
+        self.provider.resetAndRefetch()
+        if let mails = provider.fetchedResultsController.fetchedObjects {
+            for m in mails {
+                XCTAssertEqual(m.fromAddress?.email ?? "", addr, "Wrong email from address: \(m.fromAddress?.email ?? "-1")")
+            }
+        }
+        else {
+            XCTFail("No mails...")
+        }
+        
+        let frc = provider.fetchAddressResultController
+        if let objects = frc.fetchedObjects {
+            for obj in objects {
+                XCTAssertEqual(obj.inFromField?.count, 2, "Missing mails.. \(String(describing: obj.inFromField?.count))")
+            }
+        } else {
+            XCTFail("No addresses...")
+        }
+        
+
+    }
+    
+    func testImportOneMail() {
+        let addr1 = AddressProperties(email: "vic@example.com")
+        let importExpectation = expectation(description: "Import new addresses!")
+        let addresses = [addr1]
+        
+        provider.importData(from: addresses, completionHandler: {error in
+            if let error = error {
+                XCTFail("Error while importing addresses! \(error)")
+            }
+            let m = MailProperties(messageID: "1", subject: "MSG1", date: Date(), flags: 0, from: addr1, body: "THIS IS MY SECOND MSG1", signatureState: 0, encryptionState: 0)
+            self.provider.importMails(from: [m], completionHandler: {error in
+                if let error = error {
+                    XCTFail("Error while importing messages! \(error)")
+                }
+                importExpectation.fulfill()
+            }, withBIR: true)
+            
+        })
+        wait(for: [importExpectation], timeout: TimeInterval(20))
+        self.provider.resetAndRefetch()
+        if let mails = provider.fetchedResultsController.fetchedObjects {
+            for m in mails {
+                print(m.messageID, m.subject, m.fromAddress?.email)
+            }
+        }
+        else {
+            XCTFail("No mails...")
+        }
+        
+    }
+/*
     func testImport(withBIR: Bool) {
         let n = 100
         
@@ -45,6 +113,7 @@ class CoreMailTest: XCTestCase {
         provider.resetAndRefetch()
         XCTAssertEqual(provider.fetchedResultsController.fetchedObjects?.count ?? 0, n, "Missing message! \(provider.fetchedResultsController.fetchedObjects?.count ?? 0)")
     }
+ 
     
     func testImportWithBIR() {
         testImport(withBIR: true)
@@ -53,4 +122,5 @@ class CoreMailTest: XCTestCase {
     func testImportBeforeBIR() {
         testImport(withBIR: false)
     }
+*/
 }
diff --git a/enzevalos_iphoneTests/GeneratedMocks.swift b/enzevalos_iphoneTests/GeneratedMocks.swift
index eaaae2ddb9212b1c31dae63385ea42d08b295b6c..20fda625749c80505919b8abe49ee279ea10ba6c 100644
--- a/enzevalos_iphoneTests/GeneratedMocks.swift
+++ b/enzevalos_iphoneTests/GeneratedMocks.swift
@@ -1,4 +1,4 @@
-// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationModel.swift at 2020-09-30 17:29:22 +0000
+// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationModel.swift at 2020-10-02 10:21:42 +0000
 
 //
 //  AuthenticationModel.swift
@@ -654,7 +654,7 @@ import Foundation
 }
 
 
-// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationViewModel.swift at 2020-09-30 17:29:22 +0000
+// MARK: - Mocks generated from file: enzevalos_iphone/AuthenticationViewModel.swift at 2020-10-02 10:21:42 +0000
 
 //
 //  AuthenticationViewModel.swift