A Swift framework for working with emails

Overview

Build Status Carthage Pods Swift 5.0 Platforms

Postal is a swift framework providing simple access to common email providers.

Example

Connect

let postal = Postal(configuration: .icloud(login: "[email protected]", password: "mypassword"))
postal.connect { result in
    switch result {
    case .success:
        print("success")
    case .failure(let error):
        print("error: \(error)")
    }
}

Search

let filter = .subject(value: "Foobar") && .from(value: "[email protected]")
postal.search("INBOX", filter: filter) { result in
    switch result {
    case .success(let indexes):
        print("success: \(indexes)")
    case .failure(let error):
        print("error: \(error)")
    }
}

Fetch

let indexset = NSIndexSet(index: 42)
postal.fetchMessages("INBOX", uids: indexset, flags: [ .headers ], onMessage: { email in
    print("new email received: \(email)")
}, onComplete: error in
    if error = error {
        print("an error occured: \(error)")
    }
}

Want to debug your IMAP session ?

postal.logger = { log in
    print(log)
}

What about Mailcore ?

Postal does not address the same goal as MailCore. You can take a look at our thoughts in the TechnicalNotes document.

Provider quirks

Email protocol is standardized. However providers implementations often provides extension or variations of these standards. We tried to build a document to synthesize working around these variations here: ProviderQuirks.

Contributing

Postal has been a great effort and we could really use your help on many areas:

  • Finding and reporting bugs.
  • New feature suggestions.
  • Answering questions on issues.
  • Documentation improvements.
  • Reviewing pull requests.
  • Fixing bugs/new features.
  • Improving tests.
  • Contribute to elaborate the Roadmap.

If any of that sounds cool to you, please send a pull request!

Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms that you can find here: CodeOfConduct.

Requirements

  • Xcode 10
  • OS X 10.10 or later
  • iOS 8.0 or later

Installation

Carthage

Postal is Carthage compatible.

  • Add github "snipsco/Postal" to your Cartfile.
  • Run carthage update.

CocoaPods

Postal also can be used by CocoaPods.

  • Add the followings to your Podfile:
use_frameworks!
pod 'Postal'
  • Run pod install.

Manual

  1. Add the Postal repository as a submodule of your application’s repository.
git submodule add https://github.com/snipsco/Postal.git
git submodule update --init --recursive
  1. Drag and drop Postal.xcodeproj into your application’s Xcode project or workspace.
  2. On the “General” tab of your application target’s settings, add Postal.framework to the “Embedded Binaries” section.
  3. If your application target does not contain Swift code at all, you should also set the EMBEDDED_CONTENT_CONTAINS_SWIFT build setting to “Yes”.

License

Postal is released under the MIT License.

Comments
  • fetchLast and fetchMessages never return...

    fetchLast and fetchMessages never return...

    I try to access my IMAP server with this code, which is based on your demo code:

    `

    import Foundation
    import Postal
    
    class MailManagerPostal {
        var configuration: Configuration!
        
        fileprivate lazy var postal: Postal = Postal(configuration: self.configuration)
        fileprivate var messages: [FetchResult] = []
        
        init(hostname: String, userName: String, password: String) {
            log.debug("init called...")
            
            configuration = Configuration(hostname: hostname, port: 993, login: userName, password: .plain(password), connectionType: .tls, checkCertificateEnabled: false)
    
            log.debug("configuration created: \(self.configuration.description)")
            
            postal.connect(timeout: Postal.defaultTimeout, completion: { [weak self] result in
                log.debug("postal.connect completed.")
                switch result {
                case .success:
                    log.debug("fetching messages now.")
                    
                    let indexset = IndexSet(0...100000)
                    
                    self?.postal.fetchMessages("INBOX", uids: indexset, flags: [ .fullHeaders ], onMessage: { message in
                        log.debug("message : \(message.header)")
                        self?.messages.insert(message, at: 0)
                    }, onComplete: { error in
                        if let error = error {
                            log.error("fetch failed: \((error as NSError).localizedDescription)")
                        } else {
                            log.debug("connection successful")
                        }
                        log.debug("fetch complete.")
                    })
                    
                case .failure(let error):
                    log.error("connection failed: \((error as NSError).localizedDescription)")
                }
            })
            log.debug("init finished.")
        }
    }
    

    `

    Problem: The last message I see is "fetching messages now.". Then nothing happens any more. What's happening here? Where is my mistake? The credentials I use work with using mailcode2 without a problem.

    Some more questions:

    • How can I debug the IMAP requests?
    • Where can I find a documentation of the API?
    • Can I manipulate message, especially add custom headers to a message (or take a message, add custom headers, write it as a new message to the server and delete the old one...)?
    opened by ikemuc 8
  • fetchMessages attempts to fetch 1 additional message (fetchLast fails with .undefined)

    fetchMessages attempts to fetch 1 additional message (fetchLast fails with .undefined)

    It appears that giving an IndexSet to fetchMessages() with a single index actually tries to fetch 2 messages. This also means that fetchLast() fails.

    i.e.

       let indexset = IndexSet(integer: 2750)
       postal.fetchMessages("INBOX", uids: indexset, flags: [ .fullHeaders ], ...
    

    actually tries to fetch messages 2750 and 2751.

    opened by matadan 6
  • Question: How do you get the message text?

    Question: How do you get the message text?

    I'm struggling to find the right API to use to retrieve the message body. If I take the raw data from a message part, I can convert it to a String but it has extraneous characters. Is there an API to use to get the contents of a message?

    question 
    opened by matadan 3
  • Clean up from Xcode 10.2 changes

    Clean up from Xcode 10.2 changes

    As mentioned in the recent XCode 10.2 PR thread: https://github.com/snipsco/Postal/pull/83#issuecomment-485564820

    • Result module was removed but is still referenced in Postal.podspec. I don't use pods in my project so I'm not sure what issues this may cause.

    • Postal-iOS framework has 7 swift language-related warnings:

    Screen Shot 2019-04-22 at 5 39 00 PM
    opened by ktkopone 2
  • "Use of extraneous '&'" error when building for iOS 12

    When compiling a project with Postal installed via CocoaPods, the build fails in Extensions+Parsing.swift with error Use of extraneous '&' in lines 51, 55, 60 and 64

    opened by henrik-dmg 2
  • Circle CI can't find libetpan

    Circle CI can't find libetpan

    Breaks here

    ❌  /Users/distiller/theios/Pods/Postal/dependencies/module.modulemap:2:12: header 'build/ios/include/libetpan/libetpan.h' not found
    
        header "build/ios/include/libetpan/libetpan.h"
               ^
    
    
    
    ❌  /Users/distiller/theios/Pods/Postal/Postal/Address+Parsing.swift:26:8: could not build Objective-C module 'libetpan'
    
    import libetpan
           ^
    

    Any ideas how to fix, it works locally.

    opened by dai-cb 2
  • Compatibility with ReactiveCocoa 6.0.0 / ReactiveSwift 2.0.0

    Compatibility with ReactiveCocoa 6.0.0 / ReactiveSwift 2.0.0

    Hi,

    I'd like to use ReactiveCocoa 6.0.0 (using ReactiveSwift 2.0.0) in my application, but found out (by a dependency problem with Result 3.1 vs. 3.2), that Postal uses ReactiveSwift 1.

    Is there any chance, that Postal works with ReactiveSwift 2 / ReactiveCocoa 6 / Result 3.2.3 ?

    Regards, Eike

    opened by ikemuc 2
  • Optimization of NSIndexSet.enumerate(batchSize: Int)

    Optimization of NSIndexSet.enumerate(batchSize: Int)

    Problem: We have to optimize the NSIndexSet.enumerate(batchSize: Int) because of too much complexity in it -> O(n)

    Example: If we try to fetch all emails we would do something like:

    let range = NSRange(location: 1, length: Int.max - 1)
    let indexSet = NSIndexSet(indexesInRange: range)
    
    var results = [FetchResult]()
    try imapSession.fetchMessages("INBOX", set: IMAPIndexes.indexes(indexSet), flags: [ .fullHeaders ]) { _ in }
    

    Current result: It takes too much time for a provider that have a batchSize == Int.max to create the only one indexset needed for the fetch.

    Expected result: Instant fetch

    Possible solution: We could try to use the Strideable protocol instead or simply optimize inside the method.

    bug 
    opened by klefevre 2
  • What is the API to get the text content of an email?

    What is the API to get the text content of an email?

    It was asked here by someone: https://stackoverflow.com/questions/41155748/how-get-mail-content-using-postal-swift-framework

    Please let me know if there are any document or dev guide to start? Or how to find out?

    opened by tlkahn 1
  • postal.logger statement doesn't compile

    postal.logger statement doesn't compile

    Hi klevefre,

    you wrote about debugging Postal:

    You can set a closure on a Postal instance to have IMAP logs: let postal = Postal(configuration: ...) postal.logger { log in print(log) }

    Unfortunately this doesn't work in my code. I get the compiler error message "Cannot call value of non-function type 'logger?'

    This is my code:

    init(hostname: String, userName: String, password: String) {
        log.debug("init called...")
        
        configuration = Configuration(hostname: hostname, port: 993, login: userName, password: .plain(password), connectionType: .tls, checkCertificateEnabled: false)
        
        let postal: Postal = Postal(configuration: configuration)
        postal.logger { log in
            print(log)
        }
    [...]
    

    What's wrong with my code? It's exactly as you wrote...?

    opened by ikemuc 1
  • How know encode type when I parse content a mail?

    How know encode type when I parse content a mail?

    I have created a mail app using Postal Framework and my mail server send the message to app with different type data, so how I know a data in body is encode/decode by type? In my example I don't know data should encode with NSUTF8StringEncoding or NSISO2022JPStringEncoding :(

    msg.body?.allParts.forEach({ (singlePart) in
        let meesage = String.init(data: (singlePart.data?.rawData)!, encoding: NSUnicodeStringEncoding)
        if singlePart.mimeType == MimeType.textPlain {
            mailInfo.messageDescription = meesage?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) ?? ""
        } else if singlePart.mimeType == MimeType.textHtml {
            mailInfo.message = meesage ?? ""
        }
    })
    

    Thanks for support!

    opened by khacdai37 1
  • Further development?

    Further development?

    Postal looks fantastic, and the approach of interfacing with libetpan directly is the way to go. However I'm saddened to see there's been no activity since 2017 - is this project still supported by its creators? MailCore stopped working recently and I'm saddened to see another great effort has stopped as well.

    opened by guidedways 4
  • Is support for push (IDLE) planned?

    Is support for push (IDLE) planned?

    I was wondering if Postal supports Push (i.e. the user's email server informing an app that uses Postal about new messages). As far as I understand, this is known under the name IDLE (https://tools.ietf.org/html/rfc2177).

    I didn't find any traces of it in the examples or in documentation/roadmap, so I thought I would ask how big the interest is in implementing it or if there have been any plans already.

    I have also checked libetpan and it seems like it's available there. If implementing it is (in your opinion) uncomplicated enough for me to try it without substantial C experience, I'd love a tip on where to start.

    Otherwise, I would greatly appreciate any advice on what other packages I might use for that functionality in a macOS app. I'm writing it in Swift, but wouldn't mind using a package from another language, if the interop works. From what I found, MailCore or libetpan could be my only options, would be interested to find out otherwise.

    Thanks for any advice/help and to the contributors for their work.

    opened by janek 0
  • Certificate error

    Certificate error

    Whenever I try to connect to any e-mail account, I am getting a certificate error.

    error: certificate. The operation couldn’t be completed. (Postal.PostalError error 4.)

    opened by cemiltokatli 0
  • Commandline App | Postal library | postal.connect | I get nothing back (no error or else)

    Commandline App | Postal library | postal.connect | I get nothing back (no error or else)

    Hello,

    I am new in this library and I hope I get help here. I use Swift 5.

    When I use the following code I get only "ENDED" but nothing else back in the console. No error message. What I am doing wrong?

    A happy New Year to all.

    Greetings, Sven

    import Foundation      
    import Postal  
        
    let postal : Postal = Postal(configuration: Configuration(hostname: "email.none.none", port: 993, login: "[email protected]", password: .plain("test"), connectionType: .tls, checkCertificateEnabled: true, batchSize: 1000, spamFolderName: "junk"))  
        
    func getEmail() {  
        let indexset = IndexSet(integer: 1)  
        postal.fetchMessages("INBOX", uids: indexset, flags: [ .headers ], onMessage: { email in  
            print("new email received: \(email)")  
        }, onComplete: { error in  
            if error == error {  
                print("an error occured: \(error)")  
            }  
        })  
    }  
        
    postal.connect { result in  
        switch result {  
        case .success:  
            getEmail()  
        case .failure(let error):  
            print("error: \(error)")  
        }  
    }  
        
    print("ENDED")  
    
    opened by sm-a 0
Releases(v0.5.0)
Owner
Snips
We make technology disappear
Snips
A peer to peer framework for OS X, iOS and watchOS 2 that presents a similar interface to the MultipeerConnectivity framework

This repository is a peer to peer framework for OS X, iOS and watchOS 2 that presents a similar interface to the MultipeerConnectivity framework (which is iOS only) that lets you connect any 2 devices from any platform. This framework works with peer to peer networks like bluetooth and ad hoc wifi networks when available it also falls back onto using a wifi router when necessary. It is built on top of CFNetwork and NSNetService. It uses the newest Swift 2's features like error handling and protocol extensions.

Manav Gabhawala 93 Aug 2, 2022
Server-side Swift. The Perfect core toolset and framework for Swift Developers. (For mobile back-end development, website and API development, and more…)

Perfect: Server-Side Swift 简体中文 Perfect: Server-Side Swift Perfect is a complete and powerful toolbox, framework, and application server for Linux, iO

PerfectlySoft Inc. 13.9k Jan 6, 2023
Socket framework for Swift using the Swift Package Manager. Works on iOS, macOS, and Linux.

BlueSocket Socket framework for Swift using the Swift Package Manager. Works on iOS, macOS, and Linux. Prerequisites Swift Swift Open Source swift-5.1

Kitura 1.3k Dec 26, 2022
Swift backend / server framework (Pure Swift, Supports Linux)

NetworkObjects NetworkObjects is a #PureSwift backend. This framework compiles for OS X, iOS and Linux and serves as the foundation for building power

Alsey Coleman Miller 258 Oct 6, 2022
OAuth2 framework for macOS and iOS, written in Swift.

OAuth2 OAuth2 frameworks for macOS, iOS and tvOS written in Swift 5.0. ⤵️ Installation ?? Usage ?? Sample macOS app (with data loader examples) ?? Tec

Pascal Pfiffner 1.1k Jan 8, 2023
Swift/Obj-C HTTP framework with a focus on REST and JSON

Now Archived and Forked PMHTTP will not be maintained in this repository going forward. Please use, create issues on, and make PRs to the fork of PHMT

Postmates Inc. 509 Sep 4, 2022
Swift Paging is a framework that helps you load and display pages of data from a larger dataset from local storage or over network.

Swift Paging is a framework that helps you load and display pages of data from a larger dataset from local storage or over network. This approach allows your app to use both network bandwidth and system resources more efficiently. It's built on top of Combine, allowing you to harness its full power, handle errors easily, etc.

Gordan Glavaš 12 Dec 9, 2022
💧 A server-side Swift web framework.

Vapor is a web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website, API, or cloud project. Take

Vapor 22.4k Jan 2, 2023
A Swift web framework and HTTP server.

A Swift Web Framework and HTTP Server Summary Kitura is a web framework and web server that is created for web services written in Swift. For more inf

Kitura 7.6k Jan 6, 2023
A Ruby on Rails inspired Web Framework for Swift that runs on Linux and OS X

IMPORTANT! We don't see any way how to make web development as great as Ruby on Rails or Django with a very static nature of current Swift. We hope th

Saulius Grigaitis 2k Dec 5, 2022
A minimal, fast and unopinionated web framework for Swift

![Fire Image] (http://i.imgur.com/1qR6Nl4.png) Blackfire An extremely fast Swift web framework ?? Getting Started If you're familiar with express.js t

Elliott Minns 908 Dec 2, 2022
An open-source Swift framework for building event-driven, zero-config Multipeer Connectivity apps

PeerKit An open-source Swift framework for building event-driven, zero-config Multipeer Connectivity apps Usage // Automatically detect and attach to

JP Simard 861 Dec 23, 2022
A Swift Multiplatform Single-threaded Non-blocking Web and Networking Framework

Serverside non-blocking IO in Swift Ask questions in our Slack channel! Lightning (formerly Edge) Node Lightning is an HTTP Server and TCP Client/Serv

SkyLab 316 Oct 6, 2022
Super lightweight web framework in Swift based on SWSGI

Ambassador Super lightweight web framework in Swift based on SWSGI Features Super lightweight Easy to use, designed for UI automatic testing API mocki

Envoy 170 Nov 15, 2022
DispatchSource based socket framework written in pure Swift

SwiftDSSocket Overview SwiftDSSocket is a purely swift based asynchronous socket framework built atop DispatchSource. Function signatures are pretty m

Yi Huang 65 Nov 15, 2022
libuv base Swift web HTTP server framework

Notice Trevi now open a Trevi Community. Yoseob/Trevi project split up into respective Trevi, lime, middlewares and sys packages at our community. If

leeyoseob 46 Jan 29, 2022
Minimal web framework and middleware for Swift

Kunugi Kunugi(椚) is minimal web framework and middleware systems for Swift. This is inpired by Node.js' Koa. Kunugi doesn't provide http server its se

Yusuke Ito 35 Apr 18, 2022
iOS Network monitor/interceptor framework written in Swift

NetShears NetShears is a Network interceptor framework written in Swift. NetShears adds a Request interceptor mechanisms to be able to modify the HTTP

Divar 119 Dec 21, 2022
Lightweight, flexible HTTP server framework written in Swift

Hummingbird Lightweight, flexible server framework written in Swift. Hummingbird consists of three main components, the core HTTP server, a minimal we

Hummingbird 245 Dec 30, 2022